From d837490606d1bceafda4a3f5b35891f2e5bdc1c5 Mon Sep 17 00:00:00 2001 From: Bramfeld Team Date: Mon, 31 Aug 2015 14:01:44 +0200 Subject: [PATCH] version 3.0 --- COPYRIGHT | 24 + LAYOUT | 53 + Makefile | 12 + README | 20 + TODO | 217 ++ common/Makefile | 5 + common/buffer.cc | 167 ++ common/buffer.h | 1755 +++++++++++++++++ common/common.h | 42 + common/debug.h | 69 + common/endian.h | 141 ++ common/factory.h | 171 ++ common/filter.h | 82 + common/lib.mk | 6 + common/limits.h | 33 + common/log.cc | 188 ++ common/log.h | 217 ++ common/program.mk | 110 ++ common/ref.h | 154 ++ common/registrar.h | 66 + common/ring_buffer.h | 151 ++ common/subdir.mk | 7 + common/test.h | 107 + common/thread/Makefile | 0 common/thread/TODO | 15 + common/thread/atomic.h | 95 + common/thread/lib.mk | 6 + common/thread/thread.cc | 71 + common/thread/thread.h | 59 + common/time/Makefile | 3 + common/time/lib.mk | 3 + common/time/time.cc | 68 + common/time/time.h | 105 + common/timer/Makefile | 3 + common/timer/lib.mk | 3 + common/timer/timer.cc | 56 + common/timer/timer.h | 65 + common/types.h | 41 + common/uuid/Makefile | 3 + common/uuid/lib.mk | 7 + common/uuid/uuid.cc | 131 ++ common/uuid/uuid.h | 86 + config/Makefile | 0 config/config.cc | 144 ++ config/config.h | 87 + config/config_class.cc | 74 + config/config_class.h | 111 ++ config/config_class_address.cc | 56 + config/config_class_address.h | 70 + config/config_class_log_mask.cc | 41 + config/config_class_log_mask.h | 53 + config/config_exporter.h | 49 + config/config_object.cc | 54 + config/config_object.h | 105 + config/config_type.h | 48 + config/config_type_address_family.cc | 37 + config/config_type_address_family.h | 37 + config/config_type_enum.h | 85 + config/config_type_flags.h | 92 + config/config_type_int.cc | 67 + config/config_type_int.h | 49 + config/config_type_log_level.cc | 41 + config/config_type_log_level.h | 35 + config/config_type_pointer.cc | 64 + config/config_type_pointer.h | 49 + config/config_type_proto.cc | 10 + config/config_type_proto.h | 16 + config/config_type_string.cc | 28 + config/config_type_string.h | 72 + config/lib.mk | 14 + crypto/Makefile | 0 crypto/TODO | 5 + crypto/crypto_encryption.cc | 103 + crypto/crypto_encryption.h | 110 ++ crypto/crypto_encryption_openssl.cc | 306 +++ crypto/crypto_hash.cc | 77 + crypto/crypto_hash.h | 103 + crypto/crypto_hash_openssl.cc | 130 ++ crypto/crypto_mac.cc | 77 + crypto/crypto_mac.h | 91 + crypto/crypto_mac_openssl.cc | 156 ++ crypto/crypto_random.h | 80 + crypto/crypto_random_openssl.cc | 130 ++ crypto/lib.mk | 17 + event/Makefile | 0 event/action.h | 91 + event/callback.h | 46 + event/callback_queue.h | 150 ++ event/event.h | 136 ++ event/event_callback.h | 34 + event/event_message.h | 22 + event/event_poll_epoll.cc | 152 ++ event/event_poll_kqueue.cc | 159 ++ event/event_poll_poll.cc | 157 ++ event/event_poll_port.cc | 174 ++ event/event_poll_select.cc | 138 ++ event/event_system.cc | 133 ++ event/event_system.h | 92 + event/io_service.cc | 329 +++ event/io_service.h | 73 + event/lib.mk | 41 + event/object_callback.h | 107 + event/typed_callback.h | 138 ++ event/typed_pair_callback.h | 141 ++ http/Makefile | 0 http/http_protocol.cc | 253 +++ http/http_protocol.h | 95 + http/lib.mk | 3 + io/Makefile | 4 + io/lib.mk | 5 + io/net/Makefile | 0 io/net/lib.mk | 4 + io/net/tcp_server.cc | 64 + io/net/tcp_server.h | 73 + io/net/udp_server.cc | 59 + io/net/udp_server.h | 73 + io/sink_filter.cc | 74 + io/sink_filter.h | 31 + io/socket/Makefile | 0 io/socket/lib.mk | 15 + io/socket/socket.cc | 582 ++++++ io/socket/socket.h | 75 + io/socket/socket_types.h | 44 + io/socket/unix_server.cc | 64 + io/socket/unix_server.h | 68 + io/stream_handle.cc | 69 + io/stream_handle.h | 54 + programs/Makefile | 3 + programs/wanproxy/Makefile | 19 + programs/wanproxy/proxy_connector.cc | 261 +++ programs/wanproxy/proxy_connector.h | 77 + programs/wanproxy/proxy_listener.cc | 137 ++ programs/wanproxy/proxy_listener.h | 69 + programs/wanproxy/wanproxy.cc | 111 ++ programs/wanproxy/wanproxy.conf | 85 + programs/wanproxy/wanproxy.h | 121 ++ programs/wanproxy/wanproxy_codec.h | 65 + programs/wanproxy/wanproxy_config.cc | 203 ++ programs/wanproxy/wanproxy_config.h | 59 + .../wanproxy/wanproxy_config_class_codec.cc | 128 ++ .../wanproxy/wanproxy_config_class_codec.h | 94 + .../wanproxy_config_class_interface.cc | 31 + .../wanproxy_config_class_interface.h | 43 + .../wanproxy/wanproxy_config_class_peer.cc | 31 + .../wanproxy/wanproxy_config_class_peer.h | 43 + .../wanproxy/wanproxy_config_class_proxy.cc | 109 + .../wanproxy/wanproxy_config_class_proxy.h | 83 + .../wanproxy/wanproxy_config_type_codec.cc | 44 + .../wanproxy/wanproxy_config_type_codec.h | 50 + .../wanproxy_config_type_compressor.cc | 35 + .../wanproxy_config_type_compressor.h | 40 + .../wanproxy_config_type_proxy_role.cc | 35 + .../wanproxy_config_type_proxy_role.h | 51 + .../wanproxy_config_type_proxy_type.cc | 37 + .../wanproxy_config_type_proxy_type.h | 40 + ssh/Makefile | 0 ssh/TODO | 18 + ssh/lib.mk | 12 + ssh/ssh_algorithm_negotiation.cc | 233 +++ ssh/ssh_algorithm_negotiation.h | 139 ++ ssh/ssh_compression.cc | 57 + ssh/ssh_compression.h | 56 + ssh/ssh_encryption.cc | 128 ++ ssh/ssh_encryption.h | 82 + ssh/ssh_filter.cc | 516 +++++ ssh/ssh_filter.h | 55 + ssh/ssh_key_exchange.cc | 357 ++++ ssh/ssh_key_exchange.h | 74 + ssh/ssh_language.h | 52 + ssh/ssh_mac.cc | 122 ++ ssh/ssh_mac.h | 75 + ssh/ssh_protocol.cc | 154 ++ ssh/ssh_protocol.h | 138 ++ ssh/ssh_server_host_key.cc | 187 ++ ssh/ssh_server_host_key.h | 63 + ssh/ssh_session.cc | 112 ++ ssh/ssh_session.h | 159 ++ xcodec/FUTURE | 79 + xcodec/Makefile | 5 + xcodec/TODO | 19 + xcodec/cache/coss/Makefile | 4 + xcodec/cache/coss/lib.mk | 5 + xcodec/cache/coss/test/Makefile | 3 + xcodec/cache/coss/test/xcodec-coss1/Makefile | 7 + .../coss/test/xcodec-coss1/xcodec-coss1.cc | 96 + xcodec/cache/coss/xcodec_cache_coss.cc | 371 ++++ xcodec/cache/coss/xcodec_cache_coss.h | 223 +++ xcodec/example/Makefile | 3 + xcodec/example/xcodec-hash-roll1/Makefile | 7 + .../xcodec-hash-roll1/xcodec-hash-roll1.cc | 112 ++ xcodec/lib.mk | 5 + xcodec/test/Makefile | 4 + xcodec/test/xcodec-encode-decode1/Makefile | 5 + .../xcodec-encode-decode1.cc | 108 + xcodec/test/xcodec-hash1/Makefile | 5 + xcodec/test/xcodec-hash1/xcodec-hash1.cc | 314 +++ xcodec/xcodec.h | 80 + xcodec/xcodec_cache.h | 201 ++ xcodec/xcodec_decoder.cc | 176 ++ xcodec/xcodec_decoder.h | 54 + xcodec/xcodec_encoder.cc | 252 +++ xcodec/xcodec_encoder.h | 57 + xcodec/xcodec_filter.cc | 495 +++++ xcodec/xcodec_filter.h | 74 + xcodec/xcodec_hash.h | 177 ++ zlib/Makefile | 0 zlib/lib.mk | 5 + zlib/zlib_filter.cc | 163 ++ zlib/zlib_filter.h | 48 + 209 files changed, 19662 insertions(+) create mode 100644 COPYRIGHT create mode 100644 LAYOUT create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 common/Makefile create mode 100644 common/buffer.cc create mode 100644 common/buffer.h create mode 100644 common/common.h create mode 100644 common/debug.h create mode 100644 common/endian.h create mode 100644 common/factory.h create mode 100644 common/filter.h create mode 100644 common/lib.mk create mode 100644 common/limits.h create mode 100644 common/log.cc create mode 100644 common/log.h create mode 100644 common/program.mk create mode 100644 common/ref.h create mode 100644 common/registrar.h create mode 100644 common/ring_buffer.h create mode 100644 common/subdir.mk create mode 100644 common/test.h create mode 100644 common/thread/Makefile create mode 100644 common/thread/TODO create mode 100644 common/thread/atomic.h create mode 100644 common/thread/lib.mk create mode 100644 common/thread/thread.cc create mode 100644 common/thread/thread.h create mode 100644 common/time/Makefile create mode 100644 common/time/lib.mk create mode 100644 common/time/time.cc create mode 100644 common/time/time.h create mode 100644 common/timer/Makefile create mode 100644 common/timer/lib.mk create mode 100644 common/timer/timer.cc create mode 100644 common/timer/timer.h create mode 100644 common/types.h create mode 100644 common/uuid/Makefile create mode 100644 common/uuid/lib.mk create mode 100644 common/uuid/uuid.cc create mode 100644 common/uuid/uuid.h create mode 100644 config/Makefile create mode 100644 config/config.cc create mode 100644 config/config.h create mode 100644 config/config_class.cc create mode 100644 config/config_class.h create mode 100644 config/config_class_address.cc create mode 100644 config/config_class_address.h create mode 100644 config/config_class_log_mask.cc create mode 100644 config/config_class_log_mask.h create mode 100644 config/config_exporter.h create mode 100644 config/config_object.cc create mode 100644 config/config_object.h create mode 100644 config/config_type.h create mode 100644 config/config_type_address_family.cc create mode 100644 config/config_type_address_family.h create mode 100644 config/config_type_enum.h create mode 100644 config/config_type_flags.h create mode 100644 config/config_type_int.cc create mode 100644 config/config_type_int.h create mode 100644 config/config_type_log_level.cc create mode 100644 config/config_type_log_level.h create mode 100644 config/config_type_pointer.cc create mode 100644 config/config_type_pointer.h create mode 100644 config/config_type_proto.cc create mode 100644 config/config_type_proto.h create mode 100644 config/config_type_string.cc create mode 100644 config/config_type_string.h create mode 100644 config/lib.mk create mode 100644 crypto/Makefile create mode 100644 crypto/TODO create mode 100644 crypto/crypto_encryption.cc create mode 100644 crypto/crypto_encryption.h create mode 100644 crypto/crypto_encryption_openssl.cc create mode 100644 crypto/crypto_hash.cc create mode 100644 crypto/crypto_hash.h create mode 100644 crypto/crypto_hash_openssl.cc create mode 100644 crypto/crypto_mac.cc create mode 100644 crypto/crypto_mac.h create mode 100644 crypto/crypto_mac_openssl.cc create mode 100644 crypto/crypto_random.h create mode 100644 crypto/crypto_random_openssl.cc create mode 100644 crypto/lib.mk create mode 100644 event/Makefile create mode 100644 event/action.h create mode 100644 event/callback.h create mode 100644 event/callback_queue.h create mode 100644 event/event.h create mode 100644 event/event_callback.h create mode 100644 event/event_message.h create mode 100644 event/event_poll_epoll.cc create mode 100644 event/event_poll_kqueue.cc create mode 100644 event/event_poll_poll.cc create mode 100644 event/event_poll_port.cc create mode 100644 event/event_poll_select.cc create mode 100644 event/event_system.cc create mode 100644 event/event_system.h create mode 100644 event/io_service.cc create mode 100644 event/io_service.h create mode 100644 event/lib.mk create mode 100644 event/object_callback.h create mode 100644 event/typed_callback.h create mode 100644 event/typed_pair_callback.h create mode 100644 http/Makefile create mode 100644 http/http_protocol.cc create mode 100644 http/http_protocol.h create mode 100644 http/lib.mk create mode 100644 io/Makefile create mode 100644 io/lib.mk create mode 100644 io/net/Makefile create mode 100644 io/net/lib.mk create mode 100644 io/net/tcp_server.cc create mode 100644 io/net/tcp_server.h create mode 100644 io/net/udp_server.cc create mode 100644 io/net/udp_server.h create mode 100644 io/sink_filter.cc create mode 100644 io/sink_filter.h create mode 100644 io/socket/Makefile create mode 100644 io/socket/lib.mk create mode 100644 io/socket/socket.cc create mode 100644 io/socket/socket.h create mode 100644 io/socket/socket_types.h create mode 100644 io/socket/unix_server.cc create mode 100644 io/socket/unix_server.h create mode 100644 io/stream_handle.cc create mode 100644 io/stream_handle.h create mode 100644 programs/Makefile create mode 100644 programs/wanproxy/Makefile create mode 100644 programs/wanproxy/proxy_connector.cc create mode 100644 programs/wanproxy/proxy_connector.h create mode 100644 programs/wanproxy/proxy_listener.cc create mode 100644 programs/wanproxy/proxy_listener.h create mode 100644 programs/wanproxy/wanproxy.cc create mode 100644 programs/wanproxy/wanproxy.conf create mode 100644 programs/wanproxy/wanproxy.h create mode 100644 programs/wanproxy/wanproxy_codec.h create mode 100644 programs/wanproxy/wanproxy_config.cc create mode 100644 programs/wanproxy/wanproxy_config.h create mode 100644 programs/wanproxy/wanproxy_config_class_codec.cc create mode 100644 programs/wanproxy/wanproxy_config_class_codec.h create mode 100644 programs/wanproxy/wanproxy_config_class_interface.cc create mode 100644 programs/wanproxy/wanproxy_config_class_interface.h create mode 100644 programs/wanproxy/wanproxy_config_class_peer.cc create mode 100644 programs/wanproxy/wanproxy_config_class_peer.h create mode 100644 programs/wanproxy/wanproxy_config_class_proxy.cc create mode 100644 programs/wanproxy/wanproxy_config_class_proxy.h create mode 100644 programs/wanproxy/wanproxy_config_type_codec.cc create mode 100644 programs/wanproxy/wanproxy_config_type_codec.h create mode 100644 programs/wanproxy/wanproxy_config_type_compressor.cc create mode 100644 programs/wanproxy/wanproxy_config_type_compressor.h create mode 100644 programs/wanproxy/wanproxy_config_type_proxy_role.cc create mode 100644 programs/wanproxy/wanproxy_config_type_proxy_role.h create mode 100644 programs/wanproxy/wanproxy_config_type_proxy_type.cc create mode 100644 programs/wanproxy/wanproxy_config_type_proxy_type.h create mode 100644 ssh/Makefile create mode 100644 ssh/TODO create mode 100644 ssh/lib.mk create mode 100644 ssh/ssh_algorithm_negotiation.cc create mode 100644 ssh/ssh_algorithm_negotiation.h create mode 100644 ssh/ssh_compression.cc create mode 100644 ssh/ssh_compression.h create mode 100644 ssh/ssh_encryption.cc create mode 100644 ssh/ssh_encryption.h create mode 100644 ssh/ssh_filter.cc create mode 100644 ssh/ssh_filter.h create mode 100644 ssh/ssh_key_exchange.cc create mode 100644 ssh/ssh_key_exchange.h create mode 100644 ssh/ssh_language.h create mode 100644 ssh/ssh_mac.cc create mode 100644 ssh/ssh_mac.h create mode 100644 ssh/ssh_protocol.cc create mode 100644 ssh/ssh_protocol.h create mode 100644 ssh/ssh_server_host_key.cc create mode 100644 ssh/ssh_server_host_key.h create mode 100644 ssh/ssh_session.cc create mode 100644 ssh/ssh_session.h create mode 100644 xcodec/FUTURE create mode 100644 xcodec/Makefile create mode 100644 xcodec/TODO create mode 100644 xcodec/cache/coss/Makefile create mode 100644 xcodec/cache/coss/lib.mk create mode 100644 xcodec/cache/coss/test/Makefile create mode 100644 xcodec/cache/coss/test/xcodec-coss1/Makefile create mode 100644 xcodec/cache/coss/test/xcodec-coss1/xcodec-coss1.cc create mode 100644 xcodec/cache/coss/xcodec_cache_coss.cc create mode 100644 xcodec/cache/coss/xcodec_cache_coss.h create mode 100644 xcodec/example/Makefile create mode 100644 xcodec/example/xcodec-hash-roll1/Makefile create mode 100644 xcodec/example/xcodec-hash-roll1/xcodec-hash-roll1.cc create mode 100644 xcodec/lib.mk create mode 100644 xcodec/test/Makefile create mode 100644 xcodec/test/xcodec-encode-decode1/Makefile create mode 100644 xcodec/test/xcodec-encode-decode1/xcodec-encode-decode1.cc create mode 100644 xcodec/test/xcodec-hash1/Makefile create mode 100644 xcodec/test/xcodec-hash1/xcodec-hash1.cc create mode 100644 xcodec/xcodec.h create mode 100644 xcodec/xcodec_cache.h create mode 100644 xcodec/xcodec_decoder.cc create mode 100644 xcodec/xcodec_decoder.h create mode 100644 xcodec/xcodec_encoder.cc create mode 100644 xcodec/xcodec_encoder.h create mode 100644 xcodec/xcodec_filter.cc create mode 100644 xcodec/xcodec_filter.h create mode 100644 xcodec/xcodec_hash.h create mode 100644 zlib/Makefile create mode 100644 zlib/lib.mk create mode 100644 zlib/zlib_filter.cc create mode 100644 zlib/zlib_filter.h diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..80229c6 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,24 @@ +WANProxy is distributed under the following terms: + +Copyright (c) 2008-2013 WANProxy.org. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/LAYOUT b/LAYOUT new file mode 100644 index 0000000..939b08c --- /dev/null +++ b/LAYOUT @@ -0,0 +1,53 @@ +Component directory layout: + +Directory Description +============= ============================================================= +common Non-specific functionality that is of general interest, e.g. + the logging system, test infrastructure and Buffer class. +common/thread Threading infrastructure. +common/timer Time sampling infrastructure. +common/uuid UUID API. +config General-purpose configuration system infrastructure and some + essential types and classes. +crypto Cryptographic API and implementations, including hashes, MACs + and encryption algorithms. +event The event system and infrastructure to support it, such as + the various implementations of the poll interface. +http HTTP protocol implementation and utility functions. +io IO system, namely file descriptors interfaces. +io/net Networking abstractions for the IO system, utility functions and + protocols. +io/pipe Pipe infrastructure; abstractions and useful types. +io/socket Sockets interface to the IO system. +network Low-level networking, e.g. packet capture. +network/uinet User-space TCP/IP stack (see network/uinet/LAYOUT for futher + layout info). +programs Stand-alone programs, organized into sub-directories named by + program. +programs/diskdup + Block storage demonstration program, for making two block + devices or files have identical contents with a minimal number + of writes. +programs/fwdproxy + Simple file-driven port-forwarding proxy. +programs/tack File-oriented dictionary compression using XCodec. +programs/wanproxy + WANProxy. +programs/websplat + Simple web server. +programs/xcdump XCodec stream decoder and pretty-printer. +ssh SSH protocol implementation. +xcodec The XCodec codec, which performs deduplication, and its + internal interfaces. +xml Trivial XML functions. +zlib Wrappers around zlib, providing Pipes that do inflate and + deflate. + +Standard sub-directories: + +Directory Description +============= ============================================================= +test Unit tests. +example Example programs which may test functionality in a + non-automated fashion or merely provide example usage of a + complex interface. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f9969b6 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +#SUBDIR+=common +#SUBDIR+=config +#SUBDIR+=crypto +#SUBDIR+=event +#SUBDIR+=http +#SUBDIR+=io +SUBDIR+=programs +#SUBDIR+=ssh +#SUBDIR+=xcodec +#SUBDIR+=zlib + +include common/subdir.mk diff --git a/README b/README new file mode 100644 index 0000000..3200b8c --- /dev/null +++ b/README @@ -0,0 +1,20 @@ +WANProxy XTech version 3.0 +========================== + +This is an efficiency-oriented light-weight version of the original WANProxy as released by Juli Mallett on 2013, modified later by Diego Woitasen to add a persistent cache on disk based on Squid COSS patterns. + +The main objectives of this version have been: + +1. Introduce a multithreaded structure with a helper thread servicing network connections while the main thread takes care of the whole data processing. + +2. Simplify the data flow on the main thread using direct calls instead of callbacks to increase performance and reduce memory usage, so that servers can support hundreds of clients and clients can run on light network appliances. + +3. Optimize the persistent cache on disk making indices faster, avoiding duplication of segments and reusing disk space based on LRU criteria in order to keep data cached as long as it can be useful. + +Only the core wanproxy program is included here without any associated utilities or additional functionalities not essential to its basic operation mode, to be able to optimize this one as much as possible. + +This version has been originated, sponsored and advised by XTech. + + + + diff --git a/TODO b/TODO new file mode 100644 index 0000000..446076a --- /dev/null +++ b/TODO @@ -0,0 +1,217 @@ +High-priority: + Add a mechanism for forwarding callbacks between EventThread instances, + or even just between thread instances. Have a callback queue and an + fd to poll on, and when a remote thread adds something to an empty + callback queue, it writes something to the fd so as to wake up its + peer thread. NB: Even better if the poll-compatible wakeups (note, + that's the real point here: don't want to mix fd polling and cv waiting, + for instance) were dependent on the EventPoll implementation. That way + we could use kqueue's user types, etc., rather than needing to use a + pipe. + + Alternately, move fd polling to its own thread and use condvars and + callback queues for everything else. + + As part of testing/enabling/proofing that work, split EventThread into + two threads, and add one more. One thread which does FD polling. One + thread which does callback / timeout dispatch. And then move the I/O + system into its own thread. Does that sound about right? Easy then to + start to move other things to their own threads, because the threading + concerns of each of those subsystems would be covered. So we could + have some number of XCodec threads or whatever. A work submission + model that's fairly generic would be nice. So you could submit + callbacks or timeouts to the callback thread, fd registration and + deregestration to the poll thread, and I/O requests to the I/O thread. + + Adds some latency, but probably worth it? + + And for threads which need a specialized work submission process, we + could just override some method from Thread. Have a simple base class + for work items, let each type of thread do a dynamic_cast from there + up? Or provide more specific interfaces, do queueing in each thread's + class, and just provide a common alert() / biff() / ping() / wakeup() + method which alerts that an empty mailbox has become non-empty. So + the poll thread doesn't have to use condvars AND fds somehow. And let + each thread specify a timeout for sleeping, mostly useful for the + callback thread. + +Make it so that Actions can be given a callback after they are created, and +then the source of the Action from that gets the hint to start and where to +continue after the asynchronous action. This has the nice benefit of getting +rid of the callback parameter to async methods, e.g. + handler_.wait(socket_->read(0, handler_.callback())); +Becomes: + handler_.wait(socket_->read(0)); +Where handler_.wait is like: + void wait(Action *a) + { + a->callback(this, &EventHandler::handle_callback); + action_ = a; + } +Alternately, it would be nice to make asynchronous interfaces move to taking +CallbackHandler or EventHandler instances, e.g. + socket_->read(0, &handler_); +And then socket_->read is like: + void read(size_t amt, EventHandler *handler) + { + read_callback_ = handler->callback(); + handler->wait(cancellation(this, Socket::read_cancel)); + + start_read(); + } + +I feel like continuing to live with these possibilities may make clear how the +various APIs may best change, e.g. perhaps read should be: + void read(size_t amt, EventHandler *handler) + { + read_handler_ = handler; + read_handler_->cancellation(this, Socket::read_cancel); + + start_read(); + } + ... + void read_complete(Event e) + { + read_handler_->consume(e); + } + +%%% + +o) Do a pass with Log taking a LogHandle& not a const LogHandle& so I can + find places using hard-coded strings to move into LogHandles. Using a + LogHandle is vastly superior since it probably means doing the right + thing in virtual classes so that errors in the base class caused by the + subclass make it clear where the problem might be. + +o) PipeProducer::input_do; PipeProducer::input_cork(), partial consume() -> + require input_cork(). + +Note: +o) Callbacks are going through a period of major change. For now, except for + the CallbackSchedulers, it is assumed that all callbacks will need to be + strongly typed, that is that most interfaces will take a SimpleCallback or + an EventCallback or similar, and use it directly. This will mostly be a + problem in the TimeoutQueue. If some class feels that it ought to be able + to schedule a timeout to handle a callback for, say, an EventCallback that + has already had its parameter set, we'll have problems. + + NB: Once all extant code is converted in this manner, the CallbackBase + class could be named back to Callback, but since few things should be + using it directly, the more obtuse name may be desirable as an aid in + avoiding foot-shooting. Or just rename CallbackBase to Schedulable + and have done with it. + + XXX Eventually the schedule function could be moved into SimpleCallback, + TypedCallback, etc., so that we can in the latter case blow up badly + if someone asks to schedule a function whose parameter has not been + set. + +o) Make tack use the EventSystem and IOSystem now that the performance of + the latter is substantially better than the hand-rolled I/O of tack. +o) Replace singletons with thread-local storage. +o) Get simple packet capture/injection stuff working, enough to do some trivial + packet tunneling / deduplication stuff. +o) Packet framing, so we can divide incoming Buffers up into protocol control + and data fields, so that we deduplicate at useful boundaries and don't do + things like include ephemeral fields or sensitive information. +o) Begin introducing locking. +o) Split polling across multiple threads, or do callbacks in one thread and + run the IOSystem on another (IOThread?) +o) Find ways to reduce the cost of the EventThread abstraction, possibly by + decomposing it into several things and running timeouts and polling in + separate threads, so the inner loops are tighter. callback-speed1 has + gotten quite painful on Mac OS X (though little impact on FreeBSD, perhaps + thread-local storage is faster on FreeBSD?) +o) Add centralized implementations of Catenate and other patterns in lots of + the tests. +o) When splitting things into different threads, add a pipe-oriented condition + variable facility or something, at least for threads which need to poll, + rather than just use condition variabls. Or perhaps we should just use an + inter-thread messaging paradigm to handle different queues for each thread, + which is slow but we can probably batch updates through a scheduler. + +For 0.7.1: +o) Add an ActionCache which classes can use to dole out Actions and which at + destruction time will assert that there are no outstanding actions. This + will make debugging code involving Actions easier and give better Action + allocation performance, done properly. + +For 0.7.2: +o) Test error handling in epoll. +o) Test error handling with port(3C). +o) Make UnixClient and UnixServer take the SocketType as an argument to make + it possible to support both stream-orientation and datagram-orientation? + Would need to update UnixServer's API to support both models. +o) Something higher-level than Pipe which supports various disciplines and + makes it easier to write stream processing modules. +o) Make *Client::connect() work like TCPClient::connect(). +o) Move to the getrusage-based Timer class and make it clear that that is what + it is for. Perhaps create a Timer base class and a UserTimer and WallTimer + for real use? +o) Add a flush token for Pipe::input() ? +o) Add a flush Event type for Pipe::output() ? + XXX It's unclear, but I think a flush method or similar may be needed to + implement buffer limiting? A flush method being like input(), except + that it only returns once the last Pipe in the Pipeline has returned + flush complete, or error if data cannot be flushed because there is not + enough to finish processing the data that is pending, at which point + you at least know that everything else has been flushed? +o) Make Pipe::input take a parameter to send EOS along with data. +o) How do we find out if remote has shut down read channel so we can stop + writing and shut down a Splice? +o) Make interface/listener objects automatically start listening. +o) Clean up address configuration objects to be more sensible, being either + specific components of addresses (e.g. to specify an IP address) or socket + addresses (e.g. AF_UNIX paths, IP+port pairs for TCP or UDP or SCTP.) +o) Connector abstraction: connection pooling / connecting via a SOCKS server. + +For 0.8.0: +o) Cache hierarchy, including persistent storage. +o) TLS. + +For 0.9.0: +o) Handle TCP OOB data. + +After 1.0.0: +o) A CLI program for management. Remember to USE_POLL=select since Mac OS X can + only use select(2) for stdin and stdout. +o) Make it possible to detect when we are sending to a socket that is also + within WANProxy and avoid a system call -- just copy directly to the + appropriate buffer. It should be pretty easy to do this with the IO queueing + system if we getpeername/getsockname to identify this occurring. +o) Add an IO queueing system that will make it possible to use lio_listio on + systems that support it. +o) Make the XCodec encoder and decoder asynchronous. +o) Connection table. Database. +o) HTTP termination and reinitiation good enough to support an HTTP proxy mode. + +Ongoing: +o) Add lots of comments. +o) Fix bugs. +o) Audit log levels. +o) Try to remove HALTs and NOTREACHEDs that aren't legitimate. +o) Give better debug information from configuration system. + +Maybe: +o) A resolver that's less fragile than the OS-supplied ones. Mac OS X, at + minimum, neither keeps a pool of file descriptors nor errors out gracefully + when the OS is out of them, leading to hangs. +o) Send definitions out-of-band, too, so that QoS and backpressure on one + connection can't delay other connections. +o) In-path forwarding using BPF and a tiny network stack. +o) Run-length-encoding. +o) Many compression algorithms. +o) Allow chaining codecs. +o) SOCKS IPv6 support. +o) Some decent way to configure Pipelines. +o) Figure out a good name for a Pipeline, since Pipeline seems rubbish. +o) Merge two Pipelines. +o) Convert the SOCKS proxy server to a PipeEndpoint that merges the Pipeline + that it is connected to with a newly-created one. +o) Be less protocol-ignorant; add protocol-aware framing to WANProxy. For + example, HTTP response headers are likely to include some amount of changing + data (timestamps, etc.) so perhaps it's better to take a clean shot at the + start of the content. And perhaps it's better to convert to large chunks in + chunked encoding mode to get bigger windows of data to encode. No need to + remember the last 3 bytes of a chunk, the chunk header, and the next N bytes, + if the chunks won't be laid out the same every time. Right? diff --git a/common/Makefile b/common/Makefile new file mode 100644 index 0000000..f2887ce --- /dev/null +++ b/common/Makefile @@ -0,0 +1,5 @@ +SUBDIR+=thread +SUBDIR+=timer +SUBDIR+=uuid + +include ../common/subdir.mk diff --git a/common/buffer.cc b/common/buffer.cc new file mode 100644 index 0000000..18fade3 --- /dev/null +++ b/common/buffer.cc @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: buffer.cc // +// Description: generic buffer composed by reference counted segments // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#if USING_SEGMENT_CACHE +std::deque BufferSegment::segment_cache; +pthread_mutex_t BufferSegment::segment_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +size_t +Buffer::fill_iovec(struct iovec *iov, size_t niov) const +{ + if (niov == 0) + return (0); + + if (niov > IOV_MAX) + niov = IOV_MAX; + + segment_list_t::const_iterator iter = data_.begin(); + size_t iovcnt = 0; + unsigned i; + + for (i = 0; i < niov; i++) { + if (iter == data_.end()) + break; + const BufferSegment *seg = *iter; + iov[i].iov_base = (void *)(uintptr_t)seg->data(); + iov[i].iov_len = seg->length(); + iovcnt++; + iter++; + } + return (iovcnt); +} + +std::string +Buffer::hexdump(unsigned start) const +{ + static const char hexchars[] = "0123456789abcdef"; + + std::string hex; + std::string vis; + + segment_list_t::const_iterator iter = data_.begin(); + while (iter != data_.end()) { + const BufferSegment *seg = *iter++; + const uint8_t *p; + + for (p = seg->data(); p < seg->end(); p++) { + hex += hexchars[(*p & 0xf0) >> 4]; + hex += hexchars[*p & 0xf]; + + if (isprint(*p)) + vis += (char)*p; + else + vis += '.'; + } + } + + std::string dump; + unsigned cur = 0; + + for (;;) { + unsigned offset = start + cur; + std::string str; + unsigned n; + + while (offset != 0) { + str = hexchars[offset & 0xf] + str; + offset >>= 4; + } + + while (str.length() < 8) + str = '0' + str; + + dump += str; + + if (cur == length_) { + break; + } + + /* + * Print hex. + */ + for (n = 0; n < 16; n++) { + if (n % 8 == 0) + dump += " "; + dump += " "; + if (cur + n < length_) { + dump += hex[n * 2]; + dump += hex[n * 2 + 1]; + } else { + hex = ""; + dump += " "; + } + } + if (hex != "") + hex = hex.substr(32); + + dump += " |"; + for (n = 0; cur < length_ && n < 16; n++) { + dump += vis[n]; + cur++; + } + if (cur < length_) + vis = vis.substr(16); + dump += "|"; + + if (cur == length_ && cur % 16 == 0) + break; + + dump += "\n"; + } + + + return (dump); +} + +std::ostream& +operator<< (std::ostream& os, const Buffer *buf) +{ + std::string str; + buf->extract(str); + return (os << str); +} + +std::ostream& +operator<< (std::ostream& os, const Buffer& buf) +{ + return (os << &buf); +} diff --git a/common/buffer.h b/common/buffer.h new file mode 100644 index 0000000..e576c5a --- /dev/null +++ b/common/buffer.h @@ -0,0 +1,1755 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_BUFFER_H +#define COMMON_BUFFER_H + +#include /* memmove(3), memcpy(3), etc. */ + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: buffer.h // +// Description: dynamic buffer composed of reference counted segments // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +struct iovec; + +/* + * XXX + * At one point I had code to add a further layer of indirection, BufferData + * or something like that, which sat below a BufferSegment as it is today + * and held the data buffer. Then skip() and trim() and friends would + * adjust offset and length members in the BufferSegment, but not need to + * copy the data. Then we don't even need to reference count BufferSegments, + * just BufferData. If we did this, then a lot of things would be cleaner + * and there would be much less overhead in most cases, and the performance + * loss could be minimal. + * + * It might even make sense to just move the offset/length of the view of + * each BufferSegment into the Buffer, or up into some container class and + * leave BufferSegment much as it is today. + * + * At the very least, it would be nice to allocate big slabs of data to point + * BufferSegments at, so that perhaps we could opportunistically use smaller + * BufferSegments and not be so unfriendly as to constantly span page + * boundaries, making swapping and all kinds of everything worse. + * + * Also at the very least, Buffers could contain their own offset field (and + * likewise use their length field) to allow non-destructive skip and trim + * within the first and last (respectively) BufferSegments. Making this work + * at the start of the Buffer is perhaps most useful as skip() is much more + * likely than trim(). + */ + + #define BUFFER_SEGMENT_SIZE (2048) +#define BUFFER_SEGMENT_CACHE_LIMIT ((1024 * 1024) / BUFFER_SEGMENT_SIZE) +#define USING_SEGMENT_CACHE 0 + +#if USING_SEGMENT_CACHE +#include +#endif + +typedef unsigned buffer_segment_size_t; + +/* + * A BufferSegment is a contiguous chunk of data which may be at most a fixed + * size of BUFFER_SEGMENT_SIZE. The data in a BufferSegment may begin at a + * non-zero offset within the BufferSegment and may end prematurely. This + * allows for skip()/trim() semantics. + * + * BufferSegments are reference counted and mutable operations copy before + * doing a write. You must provide your own locking or use only a single + * thread. One normally does not use BufferSegments directly unless one is + * importing or exporting data in a performance-critical path. Normal usage + * uses a Buffer. + */ +class BufferSegment { + uint8_t *data_; + buffer_segment_size_t offset_; + buffer_segment_size_t length_; + Atomic ref_; + + /* + * Creates a new, empty BufferSegment with a single reference. + */ + BufferSegment(void) + : data_(NULL), + offset_(0), + length_(0), + ref_(1) + { + /* XXX Built-in slab allocator? */ + data_ = (uint8_t *)malloc(BUFFER_SEGMENT_SIZE); + } + + /* + * Should almost always only be called from unref(). + */ + ~BufferSegment() + { + ASSERT("/buffer/segment", ref_ == 0); + + if (data_ != NULL) { + free(data_); + data_ = NULL; + } + } + +public: + /* + * Get an empty BufferSegment. + */ + static BufferSegment *create(void) + { +#if USING_SEGMENT_CACHE + pthread_mutex_lock (&segment_mutex); + if (!segment_cache.empty()) { + BufferSegment *seg = segment_cache.front(); + ASSERT("/buffer/segment", seg->ref_ == 0); + segment_cache.pop_front(); + seg->ref_.add (1); + + seg->offset_ = 0; + seg->length_ = 0; + + pthread_mutex_unlock (&segment_mutex); + return (seg); + } + pthread_mutex_unlock (&segment_mutex); +#endif + return (new BufferSegment()); + } + + /* + * Get a BufferSegment with the requested data. + */ + static BufferSegment *create(const uint8_t *buf, size_t len) + { + ASSERT("/buffer/segment", buf != NULL); + ASSERT("/buffer/segment", len != 0); + ASSERT("/buffer/segment", len <= BUFFER_SEGMENT_SIZE); + + BufferSegment *seg = create(); + memcpy(seg->data_, buf, len); + seg->length_ = len; + + return (seg); + } + + + /* + * Bump the reference count. + */ + void ref(void) + { + ASSERT("/buffer/segment", ref_ != 0); + ref_.add (1); + } + + /* + * Drop the reference count and delete if there are no other live + * references. + */ + void unref(void) + { + ASSERT("/buffer/segment", ref_ != 0); + if (ref_.subtract (1) == 0) { +#if USING_SEGMENT_CACHE + pthread_mutex_lock (&segment_mutex); + if (segment_cache.size() == BUFFER_SEGMENT_CACHE_LIMIT) +#endif + delete this; +#if USING_SEGMENT_CACHE + else + segment_cache.push_back(this); + pthread_mutex_unlock (&segment_mutex); +#endif + } + } + + /* + * Return the number of outstanding references. + */ + unsigned refs(void) const + { + ASSERT("/buffer/segment", ref_ != 0); + return ref_.val (); + } + + /* + * Return a mutable pointer to the start of the data. Discouraaged. + */ + uint8_t *head(void) + { + ASSERT("/buffer/segment", ref_ == 1); + return (&data_[offset_]); + } + + /* + * Return a mutable pointer to the point at which data may be + * appended. Discouraged. + */ + uint8_t *tail(void) + { + ASSERT("/buffer/segment", ref_ == 1); + return (&data_[offset_ + length_]); + } + + /* + * Append data. + */ + BufferSegment *append(const uint8_t *buf, size_t len) + { + ASSERT("/buffer/segment", buf != NULL); + ASSERT("/buffer/segment", len != 0); + ASSERT("/buffer/segment", len <= avail()); + if (ref_ != 1) { + BufferSegment *seg; + + seg = this->copy(); + this->unref(); + seg = seg->append(buf, len); + return (seg); + } + if (avail() - offset_ < len) + pullup(); + memcpy(tail(), buf, len); + length_ += len; + return (this); + } + + /* + * Append a character. + */ + BufferSegment *append(uint8_t ch) + { + return (append(&ch, 1)); + } + + /* + * Append a C++ string. + */ + BufferSegment *append(const std::string& str) + { + ASSERT("/buffer/segment", !str.empty()); + return (append((const uint8_t *)str.c_str(), str.length())); + } + + /* + * Return the amount of data that is unused within the BufferSegment. + * This includes data before offset_. + */ + size_t avail(void) const + { + return (BUFFER_SEGMENT_SIZE - length()); + } + + /* + * Clone a BufferSegment. + */ + BufferSegment *copy(void) const + { + ASSERT("/buffer/segment", length_ != 0); + BufferSegment *seg = BufferSegment::create(data(), length_); + return (seg); + } + + /* + * Copy out the requested number of bytes or the entire available + * length, whichever is smaller. Returns the amount of data read. + */ + void copyout(uint8_t *dst, unsigned offset, size_t dstsize) const + { + ASSERT("/buffer/segment", dst != NULL); + ASSERT("/buffer/segment", dstsize != 0); + ASSERT("/buffer/segment", length() >= offset + dstsize); + + memcpy(dst, data() + offset, dstsize); + } + + /* + * Return a read-only pointer to the start of data in the segment. + */ + const uint8_t *data(void) const + { + ASSERT("/buffer/segment", length_ != 0); + return (&data_[offset_]); + } + + /* + * Return a read-only pointer to the character after the end of the + * data. If writable, this would be the point at which new data + * would be appended. + */ + const uint8_t *end(void) const + { + ASSERT("/buffer/segment", length_ != 0); + return (&data_[offset_ + length_]); + } + + /* + * The length of this segment. + */ + size_t length(void) const + { + return (length_); + } + + /* + * Moves data at an offset to the start of the BufferSegment. Does not + * need to copy on write since data before offset_ is not reliable. + * Invalidates any direct pointers retrieved by head(), tail(), data() + * or end(). + */ + void pullup(void) + { + ASSERT("/buffer/segment", length_ != 0); + ASSERT("/buffer/segment", ref_ == 1); + if (offset_ == 0) + return; + memmove(data_, data(), length()); + offset_ = 0; + } + + /* + * Manually sets the length of a BufferSegment that has had its data + * directly filled by e.g. writes through head() and tail(). + */ + void set_length(size_t len) + { + ASSERT("/buffer/segment", ref_ == 1); + ASSERT("/buffer/segment", offset_ == 0); + ASSERT("/buffer/segment", len <= BUFFER_SEGMENT_SIZE); + length_ = len; + } + + /* + * Skip a number of bytes at the start of a BufferSemgent. Creates a + * copy if there are other live references. + */ + BufferSegment *skip(unsigned bytes) + { + ASSERT("/buffer/segment", bytes != 0); + ASSERT("/buffer/segment", bytes < length()); + + if (ref_ != 1) { + BufferSegment *seg; + + seg = BufferSegment::create(this->data() + bytes, this->length() - bytes); + this->unref(); + return (seg); + } + offset_ += bytes; + length_ -= bytes; + + return (this); + } + + /* + * Adjusts the length to ignore bytes at the end of a BufferSegment. + * Like skip() but at the end rather than the start. Creates a copy if + * there are live references. + */ + BufferSegment *trim(unsigned bytes) + { + ASSERT("/buffer/segment", bytes != 0); + ASSERT("/buffer/segment", bytes < length()); + + if (ref_ != 1) { + BufferSegment *seg; + + seg = BufferSegment::create(this->data(), this->length() - bytes); + this->unref(); + return (seg); + } + length_ -= bytes; + + return (this); + } + + /* + * Remove bytes at offset in the BufferSegment. Creates a copy if + * there are live references. + */ + BufferSegment *cut(unsigned offset, unsigned bytes) + { + ASSERT("/buffer/segment", bytes != 0); + ASSERT("/buffer/segment", offset + bytes < length()); + + if (offset == 0) + return (this->skip(bytes)); + + if (offset + bytes == length()) + return (this->trim(bytes)); + + if (ref_ != 1) { + BufferSegment *seg; + + seg = BufferSegment::create(this->data(), offset); + seg->append(this->data() + offset + bytes, + this->length() - (offset + bytes)); + this->unref(); + return (seg); + } + + memmove(head() + offset, head() + offset + bytes, + length() - (offset + bytes)); + length_ -= bytes; + + return (this); + } + + /* + * Adjusts the length to ignore bytes at the end of a BufferSegment. + * Like trim() but takes the desired resulting length rather than the + * number of bytes to trim. Creates a copy if there are live + * references. + */ + BufferSegment *truncate(size_t len) + { + return (trim(length() - len)); + } + + /* + * Checks if the BufferSegment's contents are identical to the byte + * buffer passed in. + */ + bool equal(const uint8_t *buf, size_t len) const + { + ASSERT("/buffer/segment", len != 0); + if (len != length()) + return (false); + return (memcmp(data(), buf, len) == 0); + } + + /* + * Checks if the BufferSegment's contents are identical to the given + * C++ string. + */ + bool equal(const std::string& str) const + { + if (str.empty()) + return (length() == 0); + return (equal((const uint8_t *)str.c_str(), str.length())); + } + + /* + * Checks if the contents of two BufferSegments are identical. + */ + bool equal(const BufferSegment *seg) const + { + return (equal(seg->data(), seg->length())); + } + +#if USING_SEGMENT_CACHE +private: + static std::deque segment_cache; + static pthread_mutex_t segment_mutex; +#endif +}; + +/* + * A Buffer is a list of reference-counted, shareable BufferSegments. + * Operations on Buffers are translated into the appropriate BufferSegment + * operation and may result in the modification of, copying of (i.e. COW) and + * creation of BufferSegments, including both their data and their metadata. + * + * For example, appending data from an external source to a Buffer may involve + * the creation of BufferSegments and copying data from said source into them, + * or if there is room in the last BufferSegment in the Buffer, the data may be + * appended to the BufferSegment in place. + * + * Almost all operations are built on either BufferSegment operations or + * operations on the list that contains them, with the exception of a cached + * length value which is reliable since BufferSegments must not change out from + * under a Buffer in data or metadata, which provides for much faster + * performance than scanning the entire list in every possible scenario. + */ +class Buffer { +public: + typedef std::deque segment_list_t; + + /* + * A SegmentIterator allows for enumeration of the BufferSegments that + * comprise a Buffer. + */ + class SegmentIterator { + const segment_list_t& list_; + segment_list_t::const_iterator iterator_; + public: + SegmentIterator(const segment_list_t& list, + segment_list_t::const_iterator& iterator) + : list_(list), + iterator_(iterator) + { } + + SegmentIterator(const segment_list_t& list) + : list_(list), + iterator_(list_.begin()) + { } + + ~SegmentIterator() + { } + + const BufferSegment *operator* (void) const + { + if (iterator_ == list_.end()) + return (NULL); + return (*iterator_); + } + + bool end(void) const + { + return (iterator_ == list_.end()); + } + + void next(void) + { + if (iterator_ == list_.end()) + return; + ++iterator_; + } + }; +private: + size_t length_; + segment_list_t data_; +public: + /* + * Create a Buffer with no BufferSegments. + */ + Buffer(void) + : length_(0), + data_() + { } + + /* + * Create a Buffer and append byte data to it from an external source. + */ + Buffer(const uint8_t *buf, size_t len) + : length_(0), + data_() + { + ASSERT("/buffer", buf != NULL); + ASSERT("/buffer", len != 0); + append(buf, len); + } + + /* + * Create a Buffer and append data to it from another Buffer. + */ + Buffer(const Buffer& source) + : length_(0), + data_() + { + if (!source.empty()) + append(source); + } + + /* + * Create a Buffer and append at most len bytes of data to it from + * another Buffer. + */ + Buffer(const Buffer& source, size_t len) + : length_(0), + data_() + { + append(source, len); + } + + /* + * Create a Buffer and append C++ std::string data to it. This is not + * optimal (goes through c_str()), but is also not very common. + */ + Buffer(const std::string& str) + : length_(0), + data_() + { + if (!str.empty()) + append((const uint8_t *)str.c_str(), str.length()); + } + + /* + * Merely clear a Buffer out to destruct it. + */ + ~Buffer() + { + clear(); + } + + /* + * Overwrite this Buffer's data with that of another buffer. + */ + Buffer& operator= (const Buffer& source) + { + clear(); + append(&source); + return (*this); + } + + /* + * Overwrite this Buffer's data with that of a C++ std::string. + */ + Buffer& operator= (const std::string& str) + { + clear(); + if (!str.empty()) + append(str); + return (*this); + } + + /* + * Append a C++ std::string's data to a Buffer. This is not optimal + * (goes through c_str()), but is also not very common. + */ + void append(const std::string& str) + { + append((const uint8_t *)str.c_str(), str.length()); + } + + /* + * Append BufferSegments from another Buffer to this Buffer. + */ + void append(const Buffer& buf) + { + segment_list_t::const_iterator it; + + for (it = buf.data_.begin(); it != buf.data_.end(); ++it) + append(*it); + } + + /* + * Append at most len bytes of data from another Buffer to this Buffer. + */ + void append(const Buffer& buf, size_t len) + { + ASSERT("/buffer", len != 0); + ASSERT("/buffer", len <= buf.length()); + return append(buf, 0, len); + } + + void append(const Buffer& buf, unsigned start, unsigned len) + { + segment_list_t::const_iterator it; + unsigned offset = 0, limit = start + len; + + for (it = buf.data_.begin(); it != buf.data_.end(); ++it) { + BufferSegment *seg = *it; + unsigned n = seg->length(); + unsigned i1 = (start > offset ? start : offset); + unsigned i2 = (limit < offset + n ? limit : offset + n); + if (i1 < i2) { + if (i2 - i1 == n) + append(seg); + else + append(seg->data() + (i1 - offset), i2 - i1); + } + if ((offset += n) >= limit) + break; + } + } + + /* + * Append BufferSegments from another Buffer to this Buffer. + */ + void append(const Buffer *buf) + { + append(*buf); + } + + /* + * Append at most len bytes of data from another Buffer to this Buffer. + */ + void append(const Buffer *buf, size_t len) + { + append(*buf, len); + } + + /* + * Append a single BufferSegment to this Buffer. + */ + void append(BufferSegment *seg) + { + ASSERT("/buffer", seg->length() != 0); + seg->ref(); + data_.push_back(seg); + length_ += seg->length(); + } + + /* + * Append a single byte to this Buffer. + */ + void append(uint8_t ch) + { + append(&ch, 1); + } + + /* + * Prevent downcasts to uint8_t. + */ +private: + void append(uint16_t); + void append(uint32_t); + void append(uint64_t); +public: + + /* + * Append multiple bytes to this Buffer. + */ + void append(const uint8_t *buf, size_t len) + { + BufferSegment *seg; + unsigned o; + + ASSERT("/buffer", len != 0); + + /* + * If we are adding less than a single segment worth of data, + * try to append it to the end of the last segment. + */ + if (len < BUFFER_SEGMENT_SIZE && !data_.empty()) { + segment_list_t::reverse_iterator it = data_.rbegin(); + seg = *it; + if (seg->refs() == 1 && seg->avail() >= len) { + seg = seg->append(buf, len); + *it = seg; + length_ += len; + return; + } + } + + /* + * Complete segments. + */ + for (o = 0; o < len / BUFFER_SEGMENT_SIZE; o++) { + seg = BufferSegment::create(buf + (o * BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE); + append(seg); + seg->unref(); + } + if (len % BUFFER_SEGMENT_SIZE == 0) + return; + seg = BufferSegment::create(buf + (o * BUFFER_SEGMENT_SIZE), len % BUFFER_SEGMENT_SIZE); + append(seg); + seg->unref(); + } + + /* + * Append a 16-bit quantity to this buffer. We always use a + * pointer here unlike with append(uint8_t) so that differences in + * type mus be explicit. Also, we don't have a variant like this + * for uint8_t since on platforms where 'char' is unsigned, a + * literal could end up here rather than the std::string overload. + */ + void append(const uint16_t *p) + { + uint8_t data[sizeof *p]; + memcpy(data, p, sizeof data); + append(data, sizeof data); + } + + /* + * Append a 32-bit quantity to this buffer. + * + * See append(const uint16_t *) for more information. + */ + void append(const uint32_t *p) + { + uint8_t data[sizeof *p]; + memcpy(data, p, sizeof data); + append(data, sizeof data); + } + + /* + * Append a 64-bit quantity to this buffer. + * + * See append(const uint16_t *) for more information. + */ + void append(const uint64_t *p) + { + uint8_t data[sizeof *p]; + memcpy(data, p, sizeof data); + append(data, sizeof data); + } + + /* + * Drop references to all BufferSegments in this Buffer and remove them + * from its list. + */ + void clear(void) + { + segment_list_t::iterator it; + + for (it = data_.begin(); it != data_.end(); ++it) { + BufferSegment *seg = *it; + + ASSERT("/buffer", length_ >= seg->length()); + length_ -= seg->length(); + seg->unref(); + } + data_.clear(); + ASSERT("/buffer", length_ == 0); + } + + /* + * Copy up to dstsize bytes out of this Buffer starting at offset to a + * byte buffer. + */ + void copyout(uint8_t *dst, unsigned offset, size_t dstsize) const + { + segment_list_t::const_iterator it; + + ASSERT("/buffer", offset + dstsize <= length_); + + for (it = data_.begin(); it != data_.end(); ++it) { + const BufferSegment *seg = *it; + + if (offset >= seg->length()) { + offset -= seg->length(); + continue; + } + + size_t seglen = seg->length() - offset; + if (dstsize > seglen) { + seg->copyout(dst, offset, seglen); + dst += seglen; + dstsize -= seglen; + offset = 0; + continue; + } + seg->copyout(dst, offset, dstsize); + break; + } + } + + /* + * Copy up to dstsize bytes out of the beginning of this Buffer to a + * byte buffer. + */ + void copyout(uint8_t *dst, size_t dstsize) const + { + return (copyout(dst, 0, dstsize)); + } + + /* + * Take a reference to the BufferSegment at the start of this Buffer. + */ + void copyout(BufferSegment **segp) const + { + ASSERT("/buffer", !empty()); + BufferSegment *src = data_.front(); + src->ref(); + *segp = src; + } + + /* + * Take a reference to a BufferSegment of len bytes and create one + * from the start of this Buffer if the first BufferSegment is not of + * the expected length. + */ + void copyout(BufferSegment **segp, size_t len) const + { + ASSERT("/buffer", len != 0); + ASSERT("/buffer", len <= BUFFER_SEGMENT_SIZE); + ASSERT("/buffer", length() >= len); + BufferSegment *src = data_.front(); + if (src->length() == len) { + src->ref(); + *segp = src; + return; + } + if (src->length() > len) { + src->ref(); + *segp = src->truncate(len); + return; + } + BufferSegment *seg = src->copy(); + copyout(seg->tail(), seg->length(), len - seg->length()); + seg->set_length(len); + *segp = seg; + } + + /* + * Extract an 8-bit quantity out of this Buffer starting at offset. + * No endianness is assumed. + */ + void extract(uint8_t *p, unsigned offset = 0) const + { + ASSERT("/buffer", length() >= offset + sizeof *p); + copyout(p, offset, sizeof *p); + } + + /* + * Extract a 16-bit quantity out of this Buffer starting at offset. + * No endianness is assumed. + */ + void extract(uint16_t *p, unsigned offset = 0) const + { + ASSERT("/buffer", length() >= offset + sizeof *p); + copyout((uint8_t *)p, offset, sizeof *p); + } + + /* + * Extract a 32-bit quantity out of this Buffer starting at offset. + * No endianness is assumed. + */ + void extract(uint32_t *p, unsigned offset = 0) const + { + ASSERT("/buffer", length() >= offset + sizeof *p); + copyout((uint8_t *)p, offset, sizeof *p); + } + + /* + * Extract a 64-bit quantity out of this Buffer starting at offset. + * No endianness is assumed. + */ + void extract(uint64_t *p, unsigned offset = 0) const + { + ASSERT("/buffer", length() >= offset + sizeof *p); + copyout((uint8_t *)p, offset, sizeof *p); + } + + /* + * Extract a string in std::string format out of the beginning of this + * Buffer. + */ + void extract(std::string& str) const + { + segment_list_t::const_iterator it; + + for (it = data_.begin(); it != data_.end(); ++it) { + const BufferSegment *seg = *it; + + str += std::string((const char *)seg->data(), seg->length()); + } + } + + /* + * Move a 16-bit quantity out of this Buffer starting at offset. + * No endianness is assumed. + */ + void moveout(uint16_t *p, unsigned offset = 0) + { + ASSERT("/buffer", length() >= offset + sizeof *p); + moveout((uint8_t *)p, offset, sizeof *p); + } + + /* + * Move a 32-bit quantity out of this Buffer starting at offset. + * No endianness is assumed. + */ + void moveout(uint32_t *p, unsigned offset = 0) + { + ASSERT("/buffer", length() >= offset + sizeof *p); + moveout((uint8_t *)p, offset, sizeof *p); + } + + /* + * Move a 64-bit quantity out of this Buffer starting at offset. + * No endianness is assumed. + */ + void moveout(uint64_t *p, unsigned offset = 0) + { + ASSERT("/buffer", length() >= offset + sizeof *p); + moveout((uint8_t *)p, offset, sizeof *p); + } + + /* + * Move a string in std::string format out of the beginning of this + * Buffer. + */ + void moveout(std::string& str) + { + segment_list_t::const_iterator it; + + for (it = data_.begin(); it != data_.end(); ++it) { + BufferSegment *seg = *it; + + str += std::string((const char *)seg->data(), seg->length()); + seg->unref(); + } + data_.clear(); + length_ = 0; + } + + /* + * Get a SegmentIterator that can be used to enumerate BufferSegments + * in this Buffer. + */ + SegmentIterator segments(void) const + { + return (SegmentIterator(data_)); + } + + /* + * Returns true if this Buffer is empty. + */ + bool empty(void) const + { + return (data_.empty()); + } + + /* + * Returns true if this Buffer's contents are identical to those of a + * specified Buffer. + */ + bool equal(const Buffer *buf) const + { + if (length() != buf->length()) + return (false); + if (empty()) + return (true); + return (prefix(buf)); + } + + /* + * Returns true if this Buffer's contents are identical to those of a + * specified byte-buffer. + */ + bool equal(const uint8_t *buf, size_t len) const + { + ASSERT("/buffer", len != 0); + if (len != length()) + return (false); + return (prefix(buf, len)); + } + + /* + * Returns true if this Buffer's contents are identical to those of a + * specified C++ std::string. + */ + bool equal(const std::string& str) const + { + if (str.length() != length()) + return (false); + if (empty()) + return (true); + return (prefix(str)); + } + + /* + * Finds the first occurance of character ch in this Buffer's data and + * sets offsetp to the offset it was found at. If a limit is given, at + * most that many characters will be searched. + */ + bool find(uint8_t ch, unsigned *offsetp, size_t len = 0) const + { + return find(ch, 0, (len ? len : length()), offsetp); + } + + bool find(uint8_t ch, unsigned start, unsigned len, unsigned *offsetp) const + { + segment_list_t::const_iterator it; + unsigned offset = 0, limit = start + len; + + for (it = data_.begin(); it != data_.end(); ++it) { + const BufferSegment *seg = *it; + const uint8_t *p; + unsigned n = seg->length(); + unsigned i1 = (start > offset ? start : offset); + unsigned i2 = (limit < offset + n ? limit : offset + n); + if (i1 < i2 && (p = (const uint8_t *) memchr (seg->data() + i1 - offset, ch, i2 - i1))) { + *offsetp = offset + (p - seg->data()); + return (true); + } + if ((offset += n) >= limit) + break; + } + return (false); + } + + /* + * Finds the first occurance of any character in s in this Buffer's + * data and sets offsetp to the offset it was found at. It indicates + * via the foundp parameter which of the set was found. + */ + bool find_any(const std::string& s, unsigned *offsetp, uint8_t *foundp = NULL) const + { + uint8_t set[256]; + segment_list_t::const_iterator it; + unsigned offset; + size_t sit; + + memset(set, 0, sizeof set); + for (sit = 0; sit < s.length(); sit++) + set[(unsigned char)s[sit]] = 1; + + offset = 0; + + for (it = data_.begin(); it != data_.end(); ++it) { + const BufferSegment *seg = *it; + const uint8_t *p = seg->data(); + size_t len = seg->length(); + unsigned i; + + for (i = 0; i < len; i++) { + if (set[p[i]] == 0) + continue; + if (foundp != NULL) + *foundp = p[i]; + *offsetp = offset + i + (p - seg->data()); + return (true); + } + offset += len; + } + return (false); + } + + /* + * Returns the current amount of data associated with this Buffer. + */ + size_t length(void) const + { + return (length_); + } + + /* + * Move up to dstsize bytes out of this Buffer starting at offset and + * into a supplied byte-buffer. + */ + void moveout(uint8_t *dst, unsigned offset, size_t dstsize) + { + ASSERT("/buffer", dstsize != 0); + ASSERT("/buffer", length() >= offset + dstsize); + copyout(dst, offset, dstsize); + skip(offset + dstsize); + } + + /* + * Move up to dstsize bytes from the start of this Buffer and into a + * supplied byte-buffer. + */ + void moveout(uint8_t *dst, size_t dstsize) + { + moveout(dst, 0, dstsize); + } + + /* + * Move up to dstsize bytes out of this Buffer starting at offset and + * into a supplied Buffer. + */ + void moveout(Buffer *dst, unsigned offset, size_t dstsize) + { + ASSERT("/buffer", dstsize != 0); + ASSERT("/buffer", length() >= offset + dstsize); + if (offset != 0) + skip(offset); + skip(dstsize, dst); + } + + /* + * Move up to dstsize bytes from the start of this Buffer and into a + * supplied Buffer. + */ + void moveout(Buffer *dst, size_t dstsize) + { + ASSERT("/buffer", dstsize != 0); + ASSERT("/buffer", length() >= dstsize); + if (dstsize == length()) { + moveout(dst); + return; + } + moveout(dst, 0, dstsize); + } + + /* + * Move everything from this Buffer and into a suppled Buffer. + */ + void moveout(Buffer *dst) + { + if (dst->empty()) { + dst->data_ = data_; + data_.clear(); + + dst->length_ = length_; + length_ = 0; + + return; + } + + dst->data_.insert(dst->data_.end(), data_.begin(), data_.end()); + data_.clear(); + + dst->length_ += length_; + length_ = 0; + } + + /* + * Move the first BufferSegment in this Buffer out of it and give our + * reference to it to the caller. + */ + void moveout(BufferSegment **segp) + { + ASSERT("/buffer", !empty()); + BufferSegment *seg = data_.front(); + data_.pop_front(); + length_ -= seg->length(); + *segp = seg; + } + + /* + * Look at the first character in this Buffer. + */ + uint8_t peek(void) const + { + ASSERT("/buffer", length_ != 0); + + const BufferSegment *seg = data_.front(); + return (seg->data()[0]); + } + + /* + * Take the first character in this Buffer. + */ + uint8_t pop(void) + { + ASSERT("/buffer", length_ != 0); + + segment_list_t::iterator front = data_.begin(); + BufferSegment *seg = *front; + uint8_t ch = seg->data()[0]; + if (seg->length() == 1) { + seg->unref(); + data_.pop_front(); + } else { + seg = seg->skip(1); + *front = seg; + } + length_--; + return (ch); + } + + /* + * Returns true if the supplied C++ std::string is a prefix of this + * Buffer. + */ + bool prefix(const std::string& str) const + { + ASSERT("/buffer", str.length() > 0); + if (str.length() > length()) + return (false); + return (prefix((const uint8_t *)str.c_str(), str.length())); + } + + /* + * Returns true if the supplied byte-buffer is a prefix of this Buffer. + */ + bool prefix(const uint8_t *buf, size_t len) const + { + ASSERT("/buffer", len > 0); + if (len > length()) + return (false); + + const uint8_t *p = buf; + size_t plen = len; + + segment_list_t::const_iterator big = data_.begin(); + const BufferSegment *bigseg = *big; + const uint8_t *q = bigseg->data(); + const uint8_t *qe = bigseg->end(); + size_t qlen = qe - q; + + ASSERT("/buffer", qlen != 0); + + for (;;) { + ASSERT("/buffer", plen != 0); + ASSERT("/buffer", qlen != 0); + + size_t cmplen = std::min(plen, qlen); + if (memcmp(q, p, cmplen) != 0) + return (false); + plen -= cmplen; + qlen -= cmplen; + + if (qlen == 0) { + big++; + if (big == data_.end()) + break; + bigseg = *big; + q = bigseg->data(); + qe = bigseg->end(); + qlen = qe - q; + } else { + q += cmplen; + } + + if (plen == 0) + break; + + p += cmplen; + } + return (true); + } + + /* + * Returns true if the supplied Buffer's data is a prefix of this + * Buffer. + */ + bool prefix(const Buffer *buf) const + { + ASSERT("/buffer", buf->length() > 0); + if (buf->length() > length()) + return (false); + + segment_list_t::const_iterator pfx = buf->data_.begin(); + + segment_list_t::const_iterator big = data_.begin(); + const BufferSegment *bigseg = *big; + const uint8_t *q = bigseg->data(); + const uint8_t *qe = bigseg->end(); + size_t qlen = qe - q; + + while (pfx != buf->data_.end()) { + const BufferSegment *pfxseg = *pfx; + const uint8_t *p = pfxseg->data(); + const uint8_t *pe = pfxseg->end(); + size_t plen = pe - p; + + ASSERT("/buffer", qlen != 0); + ASSERT("/buffer", plen != 0); + + for (;;) { + ASSERT("/buffer", plen != 0); + ASSERT("/buffer", qlen != 0); + + size_t cmplen = std::min(plen, qlen); + if (memcmp(q, p, cmplen) != 0) + return (false); + plen -= cmplen; + qlen -= cmplen; + + if (qlen == 0) { + big++; + if (big == data_.end()) + break; + bigseg = *big; + q = bigseg->data(); + qe = bigseg->end(); + qlen = qe - q; + } else { + q += cmplen; + } + + if (plen == 0) + break; + + p += cmplen; + } + pfx++; + } + return (true); + } + + /* + * Remove the leading bytes from this Buffer. + * + * XXX Keep in sync with trim(). + */ + void skip(size_t bytes, Buffer *clip = NULL) + { + segment_list_t::iterator it; + unsigned skipped; + + ASSERT("/buffer", bytes != 0); + ASSERT("/buffer", !empty()); + + if (bytes == length()) { + if (clip != NULL) { + moveout(clip); + return; + } + clear(); + return; + } + + skipped = 0; + + while ((it = data_.begin()) != data_.end()) { + BufferSegment *seg = *it; + + if (skipped == bytes) + break; + + /* + * Skip entire segments. + */ + if ((bytes - skipped) >= seg->length()) { + skipped += seg->length(); + data_.erase(it); + if (clip != NULL) + clip->append(seg); + seg->unref(); + continue; + } + + /* + * Skip a partial segment. + */ + if (clip != NULL) + clip->append(seg->data(), bytes - skipped); + seg = seg->skip(bytes - skipped); + *it = seg; + skipped += bytes - skipped; + ASSERT("/buffer", skipped == bytes); + break; + } + length_ -= skipped; + } + + /* + * Remove the trailing bytes from this Buffer. + * + * XXX Keep in sync with skip(). + */ + void trim(size_t bytes, Buffer *clip = NULL) + { + segment_list_t::iterator it; + unsigned trimmed; + + ASSERT("/buffer", bytes != 0); + ASSERT("/buffer", !empty()); + + if (bytes == length()) { + if (clip != NULL) { + moveout(clip); + return; + } + clear(); + return; + } + + trimmed = 0; + + while ((it = --data_.end()) != data_.end()) { + BufferSegment *seg = *it; + + if (trimmed == bytes) + break; + + /* + * Trim entire segments. + */ + if ((bytes - trimmed) >= seg->length()) { + trimmed += seg->length(); + data_.erase(it); + if (clip != NULL) + clip->append(seg); + seg->unref(); + continue; + } + + /* + * Trim a partial segment. + */ + if (clip != NULL) + clip->append(seg->data() + (seg->length() - (bytes - trimmed)), bytes - trimmed); + seg = seg->trim(bytes - trimmed); + *it = seg; + trimmed += bytes - trimmed; + ASSERT("/buffer", trimmed == bytes); + break; + } + length_ -= trimmed; + } + + /* + * Remove internal bytes from this Buffer. + * + * NB: skip() and trim() could be implemented in terms of cut(), but + * it is a marked pessimization to do so. It would, however, make + * sense to merge skip and trim and to just pick the iterator based on + * the end it's being done at. + */ + void cut(unsigned offset, size_t bytes, Buffer *clip = NULL) + { + segment_list_t::iterator it; + + ASSERT("/buffer", offset <= length_); + ASSERT("/buffer", bytes != 0); + ASSERT("/buffer", offset + bytes <= length_); + + /* Remove from start. */ + if (offset == 0) { + skip(bytes, clip); + return; + } + + /* Remove from end. */ + if (offset + bytes == length_) { + trim(bytes, clip); + return; + } + + /* Preemptively adjust length. */ + length_ -= bytes; + + it = data_.begin(); + while (it != data_.end()) { + BufferSegment *seg = *it; + + ASSERT("/buffer", bytes != 0); + + if (offset >= seg->length()) { + ++it; + offset -= seg->length(); + continue; + } + + /* Remove this element, point iterator at the next one. */ + it = data_.erase(it); + if (offset + bytes >= seg->length()) { + if (offset == 0) { + /* We do not need this segment at all. */ + bytes -= seg->length(); + if (clip != NULL) + clip->append(seg); + seg->unref(); + + if (bytes == 0) + break; + continue; + } + if (clip != NULL) + clip->append(seg->data() + offset, seg->length() - offset); + /* We need only the first offset bytes of this segment. */ + bytes -= seg->length() - offset; + seg = seg->truncate(offset); + ASSERT("/buffer", seg->length() == offset); + offset = 0; + + data_.insert(it, seg); + + if (bytes == 0) + break; + continue; + } + + /* + * This is the final segment. + * + * We could split this into two segments instead of + * using BufferSegment::cut(). That would mean doing + * (effectively): + * seg->ref(); + * seg0 = seg->truncate(offset); + * seg1 = seg->skip(offset + bytes); + * If the original seg has only one ref, we can do the + * skip in-place on it without modifying data, but we + * have to copy it for the truncate. So we have to + * do a copy and now we (inefficiently) have two + * segments. + * + * If we instead use BufferSegment::cut(), we have to + * move data around a little if there's only one ref + * and do a copy if there's more than one ref. So we + * have probably less overhead and demonstrably + * fewer segments in this Buffer. + */ + if (clip != NULL) + clip->append(seg->data() + offset, bytes); + seg = seg->cut(offset, bytes); + data_.insert(it, seg); + + offset = 0; + + bytes -= bytes; + break; + } + ASSERT("/buffer", offset == 0); + ASSERT("/buffer", bytes == 0); + } + + /* + * Truncate this Buffer to the specified length. + */ + void truncate(size_t len) + { + if (length_ == len) + return; + ASSERT("/buffer", length_ > len); + trim(length_ - len); + } + + /* + * Split this Buffer into a vector of Buffers at each occurrance of the + * specified separator character sep. + */ + std::vector split(uint8_t ch, bool include_empty = false) + { + std::vector vec; + + if (empty()) { + if (include_empty) + vec.push_back(Buffer()); + return (vec); + } + + for (;;) { + unsigned off; + if (!find(ch, &off)) { + /* + * Separator not found. + */ + if (!empty()) { + vec.push_back(*this); + clear(); + } else { + if (include_empty) + vec.push_back(Buffer()); + } + return (vec); + } + + /* + * Extract the data before the separator. + */ + if (off != 0) { + Buffer buf; + skip(off, &buf); + vec.push_back(buf); + } else { + /* + * No data before the separator. + */ + if (include_empty) + vec.push_back(Buffer()); + } + + /* + * Skip the separator. + */ + skip(1); + } + + NOTREACHED("/buffer"); + } + + /* + * Merge a vector of Buffers into a single Buffer with the requested + * separator string sep. + * + * XXX Do we need/want include_empty? + */ + static Buffer join(const std::vector& vec, const std::string& sep = "") + { + if (vec.empty()) + return (Buffer()); + + if (vec.size() == 1) + return (vec.front()); + + if (sep == "") { + Buffer buf; + std::vector::const_iterator it; + for (it = vec.begin(); it != vec.end(); ++it) + buf.append(*it); + return (buf); + } + + Buffer delim(sep); + Buffer buf; + std::vector::const_iterator it = vec.begin(); + for(;;) { + buf.append(*it); + if (++it == vec.end()) + break; + buf.append(delim); + } + return (buf); + } + + /* + * Output operator for strings. + */ + Buffer& operator<< (const std::string& str) + { + append(str); + return (*this); + } + + /* + * Output operator for Buffers. + */ + Buffer& operator<< (const Buffer& buf) + { + append(buf); + return (*this); + } + + /* + * Output operator for Buffer pointers. + */ + Buffer& operator<< (const Buffer *buf) + { + append(buf); + return (*this); + } + + /* + * Fill a suppled iovec which has at most a specified number of elements + * with the contents of this Buffer and return the number which were + * populated. + */ + size_t fill_iovec(struct iovec *, size_t) const; + + /* + * Create a string with a canonical hexdump of the contents of this + * Buffer. + */ + std::string hexdump(unsigned = 0) const; +}; + +std::ostream& operator<< (std::ostream&, const Buffer *); +std::ostream& operator<< (std::ostream&, const Buffer&); + +namespace { + template + Buffer& operator<< (Buffer& buf, T arg) + { + std::ostringstream os; + os << arg; + return (buf << os.str()); + } + + static inline Buffer& operator<< (Buffer& buf, const Buffer& src) + { + buf.append(src); + return (buf); + } + + static inline Buffer& operator<< (Buffer& buf, const Buffer *src) + { + buf.append(src); + return (buf); + } +} + +#endif /* !COMMON_BUFFER_H */ diff --git a/common/common.h b/common/common.h new file mode 100644 index 0000000..b9a645f --- /dev/null +++ b/common/common.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_COMMON_H +#define COMMON_COMMON_H + +#include +#include +#include + +#include +#include +#include /* Already here for log.h, but be explicit. */ + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + +#endif /* !COMMON_COMMON_H */ diff --git a/common/debug.h b/common/debug.h new file mode 100644 index 0000000..fadb33b --- /dev/null +++ b/common/debug.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_DEBUG_H +#define COMMON_DEBUG_H + +#if defined(NDEBUG) +#define ASSERT(log, p) \ + do { \ + if (false) \ + (void)(p); \ + } while (0) +#else +#define ASSERT(log, p) \ + do { \ + if (!(p)) { \ + HALT((log)) << "Assertion (" << #p << \ + ") failed at " \ + << __FILE__ << ':' << __LINE__ \ + << " in function " \ + << __PRETTY_FUNCTION__ \ + << '.'; \ + for (;;) \ + abort(); \ + } \ + } while (0) +#endif + +/* + * XXX + * Newer GCCs are horribly broken and seem to have become unable to detect + * that HALT(x) << bar will prevent a function ever returning. We add the + * infinite loop here to help. + */ +#if defined(NDEBUG) +#define NOTREACHED(log) \ + abort() +#else +#define NOTREACHED(log) \ + do { \ + ASSERT(log, "Should not be reached." == NULL); \ + for (;;) \ + abort(); \ + } while (0) +#endif + +#endif /* !COMMON_DEBUG_H */ diff --git a/common/endian.h b/common/endian.h new file mode 100644 index 0000000..1983137 --- /dev/null +++ b/common/endian.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_ENDIAN_H +#define COMMON_ENDIAN_H + +#if !defined(BYTE_ORDER) && !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#if defined(_LITTLE_ENDIAN) +#define BYTE_ORDER LITTLE_ENDIAN +#elif defined(_BIG_ENDIAN) +#define BYTE_ORDER BIG_ENDIAN +#else +#error "Can't determine host byte order." +#endif +#endif + +#ifndef BYTE_ORDER +#error "BYTE_ORDER must be defined." +#else +#if BYTE_ORDER == LITTLE_ENDIAN +#elif BYTE_ORDER == BIG_ENDIAN +#else +#error "Unexpected BYTE_ORDER value." +#endif +#endif + +struct Endian { + static uint16_t swap(const uint16_t& in) + { + return (((in & 0xff00u) >> 0x08) | ((in & 0x00ffu) << 0x08)); + } + + static uint32_t swap(const uint32_t& in) + { + return (((in & 0xff000000u) >> 0x18) | + ((in & 0x00ff0000u) >> 0x08) | + ((in & 0x0000ff00u) << 0x08) | + ((in & 0x000000ffu) << 0x18)); + } + + static uint64_t swap(const uint64_t& in) + { + return (((in & 0xff00000000000000ull) >> 0x38) | + ((in & 0x00ff000000000000ull) >> 0x28) | + ((in & 0x0000ff0000000000ull) >> 0x18) | + ((in & 0x000000ff00000000ull) >> 0x08) | + ((in & 0x00000000ff000000ull) << 0x08) | + ((in & 0x0000000000ff0000ull) << 0x18) | + ((in & 0x000000000000ff00ull) << 0x28) | + ((in & 0x00000000000000ffull) << 0x38)); + } +}; + +struct SwapEndian { + template + static T encode(const T& in) + { + return (Endian::swap(in)); + } + + template + static T decode(const T& in) + { + return (Endian::swap(in)); + } + + template + static void append(C *out, const T& in) + { + T swapped = encode(in); + out->append(&swapped); + } + + template + static void extract(T *out, const C *in) + { + T wire; + in->extract(&wire); + *out = decode(wire); + } +}; + +struct HostEndian { + template + static T encode(const T& in) + { + return (in); + } + + template + static T decode(const T& in) + { + return (in); + } + + template + static void append(C *out, const T& in) + { + out->append(&in); + } + + template + static void extract(T *out, const C *in) + { + in->extract(out); + } +}; + +#if BYTE_ORDER == LITTLE_ENDIAN +typedef HostEndian LittleEndian; +typedef SwapEndian BigEndian; +#elif BYTE_ORDER == BIG_ENDIAN +typedef SwapEndian LittleEndian; +typedef HostEndian BigEndian; +#endif + +#endif /* !COMMON_ENDIAN_H */ diff --git a/common/factory.h b/common/factory.h new file mode 100644 index 0000000..2847c8e --- /dev/null +++ b/common/factory.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2010-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_FACTORY_H +#define COMMON_FACTORY_H + +#include +#include + +template +class Factory { +public: + Factory(void) + { } + + virtual ~Factory() + { } + + virtual C *create(void) const = 0; +}; + +template +class ConstructorFactory : public Factory { +public: + ConstructorFactory(void) + { } + + ~ConstructorFactory() + { } + + B *create(void) const + { + return (new C()); + } +}; + +template +class ConstructorArgFactory : public Factory { + A a_; +public: + ConstructorArgFactory(A a) + : a_(a) + { } + + ~ConstructorArgFactory() + { } + + B *create(void) const + { + return (new C(a_)); + } +}; + +template +class SubclassFactory : public Factory { + Factory *factory_; +public: + SubclassFactory(Factory *factory) + : factory_(factory) + { } + + ~SubclassFactory() + { + delete factory_; + factory_ = NULL; + } + + B *create(void) const + { + return (factory_->create()); + } +}; + +template +struct factory { + Factory *operator() (void) const + { + return (new ConstructorFactory); + } + + template + Factory *operator() (T arg) const + { + return (new ConstructorArgFactory(arg)); + } +}; + +template +class FactoryMap { + typedef std::map *> map_type; + + map_type map_; +public: + FactoryMap(void) + : map_() + { } + + ~FactoryMap() + { + typename map_type::iterator it; + + while ((it = map_.begin()) != map_.end()) { + delete it->second; + map_.erase(it); + } + } + + C *create(const K& key) const + { + typename map_type::const_iterator it; + + it = map_.find(key); + if (it == map_.end()) + return (NULL); + return (it->second->create()); + } + + void enter(const K& key, Factory *factory) + { + typename map_type::iterator it; + + it = map_.find(key); + if (it != map_.end()) { + delete it->second; + map_.erase(it); + } + + map_[key] = factory; + } + + template + void enter(const K& key, Factory *factory) + { + enter(key, new SubclassFactory(factory)); + } + + std::set keys(void) const + { + typename map_type::const_iterator it; + std::set key_set; + + for (it = map_.begin(); it != map_.end(); ++it) + key_set.insert(it->first); + + return (key_set); + } +}; + +#endif /* !COMMON_FACTORY_H */ diff --git a/common/filter.h b/common/filter.h new file mode 100644 index 0000000..4460555 --- /dev/null +++ b/common/filter.h @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: filter.h // +// Description: base classes for chained data processors // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_FILTER_H +#define COMMON_FILTER_H + +#include +#include +#include + +class Filter +{ +private: + Filter* recipient_; + +public: + Filter () { recipient_ = 0; } + virtual ~Filter () { } + + void chain (Filter* nxt) { recipient_ = nxt; } + virtual bool consume (Buffer& buf) { return produce (buf); } + virtual bool produce (Buffer& buf) { return (recipient_ && recipient_->consume (buf)); } + virtual void flush (int flg) { if (recipient_) recipient_->flush (flg); } +}; + +class CountFilter : public Filter +{ +private: + intmax_t& counter_; + +public: + CountFilter (intmax_t& p) : counter_(p) { } + virtual bool consume (Buffer& buf) { counter_ += buf.length (); return produce (buf); } +}; + +class BufferedFilter : public Filter +{ +protected: + LogHandle log_; + Buffer pending_; + bool flushing_; + int flush_flags_; + +public: + BufferedFilter (const LogHandle& log) : log_ (log) { flushing_ = 0; flush_flags_ = 0; } +}; + +class LogisticFilter : public BufferedFilter +{ +protected: + Filter* upstream_; + +public: + LogisticFilter (const LogHandle& log) : BufferedFilter (log) { upstream_ = 0; } + void set_upstream (Filter* f) { upstream_ = f; } +}; + +class FilterChain : public Filter +{ +private: + std::list nodes_; + Filter* holder_; + +public: + FilterChain (Filter* f) { holder_ = f; } + virtual ~FilterChain () { while (! nodes_.empty ()) { delete nodes_.front (); nodes_.pop_front (); }} + + void prepend (Filter* f) { Filter* act = (nodes_.empty () ? holder_ : nodes_.front ()); + if (f && act) nodes_.push_front (f), chain (f), f->chain (act); } + void append (Filter* f) { Filter* act = (nodes_.empty () ? this : nodes_.front ()); + if (f && act) nodes_.push_front (f), act->chain (f), f->chain (holder_); } + virtual void flush (int flg) { if (nodes_.empty ()) chain (holder_); Filter::flush (flg); } +}; + +#endif /* !COMMON_FILTER_H */ diff --git a/common/lib.mk b/common/lib.mk new file mode 100644 index 0000000..ac69aa2 --- /dev/null +++ b/common/lib.mk @@ -0,0 +1,6 @@ +VPATH+= ${TOPDIR}/common + +SRCS+= buffer.cc +SRCS+= log.cc + +CXXFLAGS+=-include common/common.h diff --git a/common/limits.h b/common/limits.h new file mode 100644 index 0000000..18f6cff --- /dev/null +++ b/common/limits.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2009 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_LIMITS_H +#define COMMON_LIMITS_H + +#if defined(__OPENNT) +#define IOV_MAX 1024 +#endif + +#endif /* !COMMON_LIMITS_H */ diff --git a/common/log.cc b/common/log.cc new file mode 100644 index 0000000..68d54fc --- /dev/null +++ b/common/log.cc @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#ifdef USE_SYSLOG +#include +#endif + +#include +#include +#include + +struct LogMask { + regex_t regex_; + enum Log::Priority priority_; +}; + +static std::list log_masks; + +#ifdef USE_SYSLOG +static int syslog_priority(const Log::Priority&); +#endif +static std::ostream& operator<< (std::ostream&, const Log::Priority&); +static std::ostream& operator<< (std::ostream&, const struct timeval&); + +void +Log::log(const Priority& priority, const LogHandle& handle, + const std::string& message) +{ + std::list::const_iterator it; + std::string handle_string = (std::string)handle; + + /* + * XXX + * Skip this all if we're in a HALT or NOTREACHED, since we can + * generate those here. Critical logs cannot ever be masked, right? + */ + for (it = log_masks.begin(); it != log_masks.end(); ++it) { + const LogMask& mask = *it; + int rv; + + rv = regexec(&mask.regex_, handle_string.c_str(), 0, NULL, 0); + switch (rv) { + case 0: + if (priority <= mask.priority_) + goto done; + return; + case REG_NOMATCH: + continue; + default: + HALT("/log") << "Could not match regex: " << rv; + return; + } + NOTREACHED("/log"); + } + +done: +#ifdef USE_SYSLOG + std::string syslog_message; + + syslog_message += "["; + syslog_message += (std::string)handle; + syslog_message += "] "; + syslog_message += message; + + syslog(syslog_priority(priority), "%s", syslog_message.c_str()); +#endif + + struct timeval now; + int rv; + + rv = gettimeofday(&now, NULL); + if (rv == -1) + memset(&now, 0, sizeof now); + + std::cerr << now << " [" << handle_string << "] " << + priority << ": " << + message << + std::endl; +} + +bool +Log::mask(const std::string& handle_regex, const Log::Priority& priority) +{ + LogMask mask; + + if (::regcomp(&mask.regex_, handle_regex.c_str(), + REG_NOSUB | REG_EXTENDED) != 0) { + return (false); + } + mask.priority_ = priority; + + log_masks.push_back(mask); + + return (true); +} + +#ifdef USE_SYSLOG +static int +syslog_priority(const Log::Priority& priority) +{ + switch (priority) { + case Log::Emergency: + return (LOG_EMERG); + case Log::Alert: + return (LOG_ALERT); + case Log::Critical: + return (LOG_CRIT); + case Log::Error: + return (LOG_ERR); + case Log::Warning: + return (LOG_WARNING); + case Log::Notice: + return (LOG_NOTICE); + case Log::Info: + return (LOG_INFO); + case Log::Debug: + case Log::Trace: + return (LOG_DEBUG); + default: + HALT("/log") << "Unhandled log priority!"; + return (-1); + } +} +#endif + +static std::ostream& +operator<< (std::ostream& os, const Log::Priority& priority) +{ + switch (priority) { + case Log::Emergency: + return (os << "EMERG"); + case Log::Alert: + return (os << "ALERT"); + case Log::Critical: + return (os << "CRIT"); + case Log::Error: + return (os << "ERR"); + case Log::Warning: + return (os << "WARNING"); + case Log::Notice: + return (os << "NOTICE"); + case Log::Info: + return (os << "INFO"); + case Log::Debug: + return (os << "DEBUG"); + case Log::Trace: + return (os << "TRACE"); + default: + HALT("/log") << "Unhandled log priority!"; + return (os); + } +} + +static std::ostream& +operator<< (std::ostream& os, const struct timeval& tv) +{ + char buf[20]; + + snprintf(buf, sizeof buf, "%u.%06u", (unsigned)tv.tv_sec, + (unsigned)tv.tv_usec); + return (os << buf); +} diff --git a/common/log.h b/common/log.h new file mode 100644 index 0000000..ff8789d --- /dev/null +++ b/common/log.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_LOG_H +#define COMMON_LOG_H + +#include /* For abort(3). */ + +#include +#include + +class LogNull { +public: + LogNull(void) + { } + + ~LogNull() + { } + + template + const LogNull& operator<< (T) const + { + return (*this); + } + + const LogNull& operator<< (std::ostream& (*)(std::ostream&)) const + { + return (*this); + } +}; + +class LogHandle { + std::string string_; +public: + LogHandle(const char *s) + : string_(s) + { } + + LogHandle(const std::string& s) + : string_(s) + { } + + ~LogHandle() + { } + + operator const std::string& (void) const + { + return (string_); + } + + template + LogHandle operator+ (T x) const + { + LogHandle appended = *this; + appended.string_ += x; + return (appended); + } +}; + +class Log { +public: + enum Priority { + Emergency, + Alert, + Critical, + Error, + Warning, + Notice, + Info, + Trace, + Debug, + }; + + class Halt { + public: + Halt(void) + { } + + ~Halt() + { } + }; + +private: + LogHandle handle_; + Priority priority_; + std::ostringstream str_; + bool pending_; + bool halted_; +public: + Log(const LogHandle& handle, const Priority& priority, const std::string function) + : handle_(handle), + priority_(priority), + str_(), + pending_(false), + halted_(false) + { + /* + * Print the function name for non-routine messages. + */ + switch (priority_) { + case Emergency: + case Alert: + case Critical: + case Error: +#if !defined(NDEBUG) + case Debug: +#endif + str_ << function << ": "; + break; + default: + break; + } + } + + ~Log() + { + flush(); + if (halted_) { + abort(); /* XXX */ + } + } + + template + std::ostream& + operator << (T x) + { + pending_ = true; + return (str_ << x); + } + + std::ostream& + operator << (const Halt&) + { + pending_ = true; + halted_ = true; + return (str_ << "Halting: "); + } + + void + flush(void) + { + if (!pending_) + return; + Log::log(priority_, handle_, str_.str()); + } + + static void log(const Priority&, const LogHandle&, const std::string&); + static bool mask(const std::string&, const Priority&); +}; + + /* A panic condition. */ +#define EMERGENCY(log) Log(log, Log::Emergency, __PRETTY_FUNCTION__) + /* A condition that should be corrected immediately. */ +#define ALERT(log) Log(log, Log::Alert, __PRETTY_FUNCTION__) + /* Critical condition. */ +#define CRITICAL(log) Log(log, Log::Critical, __PRETTY_FUNCTION__) + /* Errors. */ +#define ERROR(log) Log(log, Log::Error, __PRETTY_FUNCTION__) + /* Warnings. */ +#define WARNING(log) Log(log, Log::Warning, __PRETTY_FUNCTION__) + /* Conditions that are not error conditions, but may need handled. */ +#define NOTICE(log) Log(log, Log::Notice, __PRETTY_FUNCTION__) + /* Informational. */ +#define INFO(log) Log(log, Log::Info, __PRETTY_FUNCTION__) + /* Debugging information. */ +#if !defined(NDEBUG) +#define DEBUG(log) Log(log, Log::Debug, __PRETTY_FUNCTION__) +#else +#define DEBUG(log) LogNull() +#endif + + /* A condition which cannot be continued from. */ +#define HALT(log) EMERGENCY(log) << Log::Halt() + +class Trace { + LogHandle log_; + std::string function_; +public: + Trace(const LogHandle& log, const std::string& function) + : log_(log), + function_(function) + { + Log(log_, Log::Trace, __PRETTY_FUNCTION__) << "Entered " << function_; + } + + ~Trace() + { + Log(log_, Log::Trace, __PRETTY_FUNCTION__) << "Exited " << function_; + } +}; + + /* Selective debug tracing. */ +#define TRACE(log) Trace trace_(log, __PRETTY_FUNCTION__) + +#endif /* !COMMON_LOG_H */ diff --git a/common/program.mk b/common/program.mk new file mode 100644 index 0000000..52d7db5 --- /dev/null +++ b/common/program.mk @@ -0,0 +1,110 @@ +ifndef TOPDIR +$(error "TOPDIR must be defined") +endif + +ifdef NETWORK_TEST +TEST=${NETWORK_TEST} +endif + +ifdef SKIP_NETWORK_TESTS +SKIP_TESTS=${SKIP_NETWORK_TESTS} +endif + +ifdef TEST +ifndef SKIP_TESTS +PROGRAM=${TEST} +SRCS+=${TEST}.cc + +all: ${PROGRAM} + +# Build and run regression tests. +regress: ${PROGRAM} +ifdef TEST_WRAPPER + ${TEST_WRAPPER} ${PWD}/${PROGRAM} +else + ${PWD}/${PROGRAM} +endif +else +# Build but don't run regression tests. +regress: ${PROGRAM} +endif +else +ifndef PROGRAM +$(error "Must have a program to build.") +endif + +all: ${PROGRAM} + +# Not a regression test, do nothing. +regress: + @true +endif + +.PHONY: regress + +OSNAME:=$(shell uname -s) + +CFLAGS+=-pipe +CPPFLAGS+=-I${TOPDIR} +ifdef NDEBUG +CFLAGS+=-O2 +CPPFLAGS+=-DNDEBUG=1 +else +CFLAGS+=-O0 +ifneq "${OSNAME}" "SunOS" +CFLAGS+=-g +endif +endif + +# OpenBSD needs no -Werror. +ifeq "${OSNAME}" "OpenBSD" +NO_WERROR=1 +endif + +# Linux needs no -Werror because the epoll headers are terrible. +# XXX Should just disable -Werror for the one file using epoll. +ifeq "${OSNAME}" "Linux" +NO_WERROR=1 +endif + +#CFLAGS+=--std gnu++0x +#CFLAGS+=-pedantic +CFLAGS+=-W -Wall +ifndef NO_WERROR +CFLAGS+=-Werror +endif +CFLAGS+=-Wno-system-headers +CFLAGS+=-Wno-unused-parameter +CFLAGS+=-Wno-switch +CFLAGS+=-Wno-uninitialized +CFLAGS+=-Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wshadow -Wcast-align -Wchar-subscripts -Wreorder +#CFLAGS+=-Winline +CXXFLAGS+=-Wno-deprecated +CXXFLAGS+=-Wnon-virtual-dtor + +$(foreach _lib,${USE_LIBS},$(eval include ${TOPDIR}/$(strip ${_lib})/lib.mk)) + +define __library_conditionals +ifdef CFLAGS_${1} +CFLAGS+=${CFLAGS_${1}} +endif +ifdef SRCS_${1} +SRCS+= ${SRCS_${1}} +endif +endef + +$(foreach _lib,${USE_LIBS},$(eval $(call __library_conditionals,$(subst /,_,${_lib})))) + +OBJS+= $(patsubst %.cc,bin/%.o,$(patsubst %.c,bin/%.o,${SRCS})) + +${PROGRAM}: ${OBJS} + ${CXX} ${CXXFLAGS} ${CFLAGS} ${LDFLAGS} -o bin/$@ ${OBJS} ${LDADD} + +bin/%.o: %.cc + ${CXX} ${CPPFLAGS} ${CXXFLAGS} ${CFLAGS} -c -o $@ $< + +bin/%.o: %.c + ${CC} ${CPPFLAGS} ${CFLAGS} -c -o $@ $< + +clean: + rm -f ${PROGRAM} ${OBJS} diff --git a/common/ref.h b/common/ref.h new file mode 100644 index 0000000..5df0bc1 --- /dev/null +++ b/common/ref.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_REF_H +#define COMMON_REF_H + +template +class Ref { + class RefObj { + T *ptr_; + unsigned count_; + public: + RefObj(T *ptr) + : ptr_(ptr), + count_(1) + { } + + private: + ~RefObj() + { + delete ptr_; + ptr_ = NULL; + } + + public: + void hold(void) + { + count_++; + } + + void drop(void) + { + if (count_-- == 1) + delete this; + } + + bool exclusive(void) const + { + return (count_ == 1); + } + + T *get(void) const + { + return (ptr_); + } + }; + + RefObj *obj_; +public: + Ref(void) + : obj_(NULL) + { } + + Ref(T *ptr) + : obj_(NULL) + { + if (ptr != NULL) + obj_ = new RefObj(ptr); + } + + /* + * XXX Template... See operator=. + */ + Ref(const Ref& ref) + : obj_(ref.obj_) + { + if (obj_ != NULL) + obj_->hold(); + } + + ~Ref() + { + if (obj_ != NULL) { + obj_->drop(); + obj_ = NULL; + } + } + + /* + * XXX + * Template so we can take a pointer from a Ref<> with a compatible + * base class? + */ + const Ref& operator= (const Ref& ref) + { + if (obj_ != NULL) { + obj_->drop(); + obj_ = NULL; + } + + if (ref.obj_ != NULL) { + obj_ = ref.obj_; + obj_->hold(); + } + return (*this); + } + + T *operator-> (void) const + { + return (obj_->get()); + } + + const T& operator* (void) const + { + const T *ptr = obj_->get(); + return (*ptr); + } + + template + Tc cast(void) const + { + const T *ptr = obj_->get(); + return (dynamic_cast(ptr)); + } + + bool exclusive(void) const + { + return (obj_->exclusive()); + } + + bool null(void) const + { + return (obj_ == NULL); + } + + bool operator< (const Ref& b) const + { + return (obj_ < b.obj_); + } +}; + +#endif /* !COMMON_REF_H */ diff --git a/common/registrar.h b/common/registrar.h new file mode 100644 index 0000000..061e566 --- /dev/null +++ b/common/registrar.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_REGISTRAR_H +#define COMMON_REGISTRAR_H + +#include + +/* + * U is merely a type used as a unique key, so that multiple Registrars of + * objects with the same type can coexist. + */ +template +class Registrar { + std::set registered_set_; + + Registrar(void) + : registered_set_() + { } + + ~Registrar() + { } +public: + void enter(T item) + { + registered_set_.insert(item); + } + + std::set enumerate(void) const + { + return (registered_set_); + } + + static Registrar *instance(void) + { + static Registrar *instance; + + if (instance == NULL) + instance = new Registrar(); + return (instance); + } +}; + +#endif /* !COMMON_REGISTRAR_H */ diff --git a/common/ring_buffer.h b/common/ring_buffer.h new file mode 100644 index 0000000..8e87e55 --- /dev/null +++ b/common/ring_buffer.h @@ -0,0 +1,151 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: ring_buffer.h // +// Description: circular buffer for unlocked exchange of data // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef COMMON_RING_BUFFER_H +#define COMMON_RING_BUFFER_H + +#define STANDARD_RING_BUFFER_CAPACITY 32768 +#define ITEM_SIZE ((int) sizeof (T)) + +template class RingBuffer +{ +private: + unsigned char buffer[N * ITEM_SIZE]; + unsigned char* reader; + unsigned char* writer; + +public: + RingBuffer (); + + int read (T& trg); + int write (const T& src); + + bool is_empty () { return (reader == writer); } + int data_size () { return (writer >= reader ? writer - reader : sizeof buffer - (reader - writer)); } +}; + +template RingBuffer::RingBuffer () +{ + reader = writer = buffer; +} + +template int RingBuffer::read (T& trg) +{ + unsigned char* r; + unsigned char* w; + int s, t, n1, n2; + + r = reader; + w = writer; + s = (w >= r ? (w - r) : sizeof buffer - (r - w)); + if (s >= ITEM_SIZE) + { + t = buffer + sizeof buffer - r; + if (ITEM_SIZE <= t) + n1 = ITEM_SIZE, n2 = 0; + else + n1 = t, n2 = ITEM_SIZE - t; + memcpy (&trg, r, n1); + if (n2) + memcpy (((char*) &trg) + n1, buffer, n2); + reader = (ITEM_SIZE < t ? r + n1 : buffer + n2); + return ITEM_SIZE; + } + + return 0; +} + +template int RingBuffer::write (const T& src) +{ + unsigned char* r; + unsigned char* w; + int s, t, n1, n2; + + r = reader; + w = writer; + s = (w >= r ? sizeof buffer - (w - r) : (r - w)); + if (s > ITEM_SIZE) + { + t = buffer + sizeof buffer - w; + if (ITEM_SIZE <= t) + n1 = ITEM_SIZE, n2 = 0; + else + n1 = t, n2 = ITEM_SIZE - t; + memcpy (w, &src, n1); + if (n2) + memcpy (buffer, ((const char*) &src) + n1, n2); + writer = (ITEM_SIZE < t ? w + n1 : buffer + n2); + return ITEM_SIZE; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include + +template class WaitBuffer +{ +private: + RingBuffer ring; + pthread_mutex_t mutex; + pthread_cond_t ready; + +public: + WaitBuffer (); + ~WaitBuffer (); + + int read (T& trg); + int write (const T& src); + + void wakeup () { if (ring.is_empty ()) pthread_cond_signal (&ready); } +}; + +template WaitBuffer::WaitBuffer () +{ + pthread_mutex_init (&mutex, 0); + pthread_cond_init (&ready, 0); +} + +template WaitBuffer::~WaitBuffer () +{ + pthread_mutex_destroy (&mutex); + pthread_cond_destroy (&ready); +} + +template int WaitBuffer::read (T& trg) +{ + if (ring.is_empty ()) + { + pthread_mutex_lock (&mutex); + if (ring.is_empty ()) + pthread_cond_wait (&ready, &mutex); + pthread_mutex_unlock (&mutex); + } + + return ring.read (trg); +} + +template int WaitBuffer::write (const T& src) +{ + int rsl = ring.write (src); + + if (rsl && ring.data_size () == ITEM_SIZE) + { + pthread_mutex_lock (&mutex); + pthread_cond_signal (&ready); + pthread_mutex_unlock (&mutex); + } + + return rsl; +} + +#endif /* !COMMON_RING_BUFFER_H */ diff --git a/common/subdir.mk b/common/subdir.mk new file mode 100644 index 0000000..fae74ab --- /dev/null +++ b/common/subdir.mk @@ -0,0 +1,7 @@ +all install regress clean: + @for _dir in ${SUBDIR}; do \ + echo "==> ${SUBMAKE_DIR}$$_dir ($@)" ; \ + (cd $$_dir && \ + SUBMAKE_DIR=${SUBMAKE_DIR}$$_dir/ ${MAKE} $@) || \ + exit $$? ; \ + done diff --git a/common/test.h b/common/test.h new file mode 100644 index 0000000..8ee6710 --- /dev/null +++ b/common/test.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_TEST_H +#define COMMON_TEST_H + +class TestGroup { + friend class Test; + + LogHandle log_; + const std::string description_; + unsigned tests_; + unsigned passes_; +public: + TestGroup(const LogHandle& log, const std::string& description) + : log_(log), + description_(description), + tests_(0), + passes_(0) + { + INFO(log_) << "Running tests in group: " << description_; + } + + ~TestGroup() + { + ASSERT(log_, tests_ != 0); + + INFO(log_) << "Test results for group: " << description_; + if (passes_ == tests_) { + INFO(log_) << "All tests passed."; + } else { + if (passes_ == 0) { + ERROR(log_) << "All tests failed."; + } else { + INFO(log_) << passes_ << "/" << tests_ << " tests passed."; + ERROR(log_) << (tests_ - passes_) << " tests failed."; + } + } + } + +private: + void test(bool passed, const std::string& description) + { + if (passed) { +#if 0 + DEBUG(log_) << "PASS: " << description; +#endif + passes_++; + } else { + ERROR(log_) << "FAIL: " << description; + } + tests_++; + } +}; + +class Test { + TestGroup& group_; + const std::string description_; + bool passed_; +public: + Test(TestGroup& group, const std::string& description) + : group_(group), + description_(description), + passed_(false) + { } + + Test(TestGroup& group, const std::string& description, bool passed) + : group_(group), + description_(description), + passed_(passed) + { } + + ~Test() + { + group_.test(passed_, description_); + } + + void pass(void) + { + ASSERT("/test", !passed_); + passed_ = true; + } +}; + +#endif /* !COMMON_TEST_H */ diff --git a/common/thread/Makefile b/common/thread/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/common/thread/TODO b/common/thread/TODO new file mode 100644 index 0000000..e076a2c --- /dev/null +++ b/common/thread/TODO @@ -0,0 +1,15 @@ +Threads need to pick a blocking mechanism and somehow stick to it reliably, so there can be a neat and tidy Thread::kill function. A signal should knock a thread out of anything, but that can still have races. It would be nice if Thread::kill didn't have to be virtual, and if it could be worked into a generalized thread-to-thread messaging paradigm, but that's problematic when one of the major purposes of having threads in the first place is to be able to use other polling mechanisms in separate threads, i.e. blocking for libpcap or X11 or whatever. + +Maybe it would be best to remove virtual void main() and instead have a worker-thread model, where threads can perform work, wait, or be signalled. The work, wait and signal functions would be virtual, which would hide a lot of the complexity about whether it's okay to block, or whether we've been signalled. And then threads could determine their own needs for block and signal; most threads might use EventPoll to do that work, but some might use something like pcap or X11 instead. No inter-thread messaging at a low-level, but maybe some higher-level interfaces that could be implemented on top of them. + +If moving to a worker model, why not provide a work unit, too, and the possibility of some messaging primitives? That also has the benefit of mirroring some of the hardware models it'd be nice for the event system and threading to be compatible with, like that of Octeon. And it allows for lossless messaging between threads, whereas signal/wait can have problems with races and spurious wakeups, right? Especially if there's any chance of the signal being consumed by something like a system call, rather than an instance of wait. But with some slightly-heavyweight locking, there's no chance of those kinds of races, because e.g. the thread state lock would be held during work processing. That nearly serializes processing, though. *Unless* we have a lockless work-queueing model in addition to a wakeup mechanism. Right? Then we can guarantee that a thread will acquire a lock, check for work, and only then block. There's still races, though, such as with pcap, or X11, which won't let us specify a lock to relinquish at wait time. Argh! + +Maybe too much generalization is a curse, and it would be better to find a limited and reliable model to support, and make sure all the applications we care about in the immediate future are plausible. + +Why not just register threads that need to generate callbacks as an "EventSource" with the EventThread/EventSystem? This is non-ideal for a world in which we would have multiple EventThreads but it's a start for now. But with this, the EventSource could include a pipe to read/write on, which would be very neat and tidy. + +I tried moving towards making EventThread the normative model for threads, and parameterizing anything using callbacks on EventThread (as a CallbackScheduler mostly, but also for timeouts, although there's no reason to not have timeouts in a different thread, and merely schedule them back to EventThread.) This is OK, but mostly devolves into single-threading anyway. + +If polling code is simplified a little further and moved to common/poll/..., it makes sense indeed to move polling into *every* thread, and to use pipes to communicate between threads (or, where possible, we could use user events like with kqueue.) That can all mostly be done under the hood after the fact for real if we begin with the EventSource model, in which new sources are registered with the EventSystem. Then we have one-direction communication, which is probably most of what we need anyway. Right? + +Actually, to avoid races, the method of inter-thread communication must be a condvar so that we can savely use mutexes. Otherwise we can go to sleep with work waiting, right? Unless we get really fussy about the data on the inter-thread pipe, but why not just use condvars instead? Then we just need to have a WorkerThread which has block/signal/work abstractions, and make EventPoll one of the few that uses something other than the default. And have a single poll thread, of course. Yes, this doesn't solve the pcap race problem, but nothing will; pcap is thread hell. diff --git a/common/thread/atomic.h b/common/thread/atomic.h new file mode 100644 index 0000000..d06650b --- /dev/null +++ b/common/thread/atomic.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_THREAD_ATOMIC_H +#define COMMON_THREAD_ATOMIC_H + +template +class Atomic { + T val_; +public: + Atomic(void) + : val_() + { } + + template + Atomic(Ta arg) + : val_(arg) + { } + + ~Atomic() + { } + + T val () const { return val_; } + bool operator == (T v) const { return (v == val_); } + bool operator != (T v) const { return (v != val_); } + + /* + * Note that these are deliberately not operator overloads, to force + * deliberate use of this class. + */ + +#if defined(__GNUC__) + template + T add(Ta arg) + { + return (__sync_add_and_fetch (&val_, arg)); + } + + template + T subtract(Ta arg) + { + return (__sync_sub_and_fetch (&val_, arg)); + } + + template + T set(Ta arg) + { + return (__sync_or_and_fetch (&val_, arg)); + } + + template + T mask(Ta arg) + { + return (__sync_and_and_fetch (&val_, arg)); + } + + template + T clear(Ta arg) + { + return (__sync_and_and_fetch (&val_, ~arg)); + } + + template + bool cmpset(To oldval, Tn newval) + { + return (__sync_bool_compare_and_swap (&val_, oldval, newval)); + } +#else +#error "No support for atomic operations for your compiler. Why not add some?" +#endif +}; + +#endif /* !COMMON_THREAD_ATOMIC_H */ diff --git a/common/thread/lib.mk b/common/thread/lib.mk new file mode 100644 index 0000000..6c40921 --- /dev/null +++ b/common/thread/lib.mk @@ -0,0 +1,6 @@ +VPATH+= ${TOPDIR}/common/thread + +SRCS+= thread.cc + +LDADD+= -lpthread + diff --git a/common/thread/thread.cc b/common/thread/thread.cc new file mode 100644 index 0000000..827757b --- /dev/null +++ b/common/thread/thread.cc @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: thread.cc // +// Description: basic structure for generic thread objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace +{ + static void* thread_posix_start (void* arg) + { + Thread* td = (Thread*) arg; + td->main (); + return 0; + } +} + +Thread::Thread (const std::string& name) : name_(name), thread_id_(0), stop_(false) +{ +} + +bool Thread::start () +{ + int rv = pthread_create (&thread_id_, NULL, thread_posix_start, this); + if (rv != 0) + { + ERROR("/thread/posix") << "Unable to start thread."; + return false; + } + return true; +} + +void Thread::stop () +{ + stop_ = true; + void* val; + int rv = pthread_join (thread_id_, &val); + if (rv == -1) + ERROR("/thread/posix") << "Thread join failed."; +} + diff --git a/common/thread/thread.h b/common/thread/thread.h new file mode 100644 index 0000000..0234c3a --- /dev/null +++ b/common/thread/thread.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_THREAD_THREAD_H +#define COMMON_THREAD_THREAD_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: thread.h // +// Description: basic structure for generic thread objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Thread +{ +protected: + std::string name_; + pthread_t thread_id_; + bool stop_; + +protected: + Thread (const std::string&); + +public: + virtual ~Thread () {} + + bool start (); + virtual void stop (); + virtual void main () = 0; +}; + +#endif /* !COMMON_THREAD_THREAD_H */ diff --git a/common/time/Makefile b/common/time/Makefile new file mode 100644 index 0000000..09eeae2 --- /dev/null +++ b/common/time/Makefile @@ -0,0 +1,3 @@ +SUBDIR+= + +include ../../common/subdir.mk diff --git a/common/time/lib.mk b/common/time/lib.mk new file mode 100644 index 0000000..3b7f223 --- /dev/null +++ b/common/time/lib.mk @@ -0,0 +1,3 @@ +VPATH+= ${TOPDIR}/common/time + +SRCS+= time.cc diff --git a/common/time/time.cc b/common/time/time.cc new file mode 100644 index 0000000..cc917fd --- /dev/null +++ b/common/time/time.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include + +NanoTime +NanoTime::current_time(void) +{ + NanoTime nt; + int rv; + +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) + struct timespec ts; + + rv = ::clock_gettime(CLOCK_MONOTONIC, &ts); + ASSERT("/nano/time/posix", rv != -1); + + nt.seconds_ = ts.tv_sec; + nt.nanoseconds_ = ts.tv_nsec; +#else + struct timeval tv; + + rv = ::gettimeofday(&tv, NULL); + ASSERT("/nano/time/gtod", rv != -1); + nt.seconds_ = tv.tv_sec; + nt.nanoseconds_ = tv.tv_usec * 1000; +#endif + + return (nt); +} + +void +NanoTime::sleep(int ms) +{ + struct timespec ts; + + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms - ts.tv_sec) * 1000000; + + nanosleep(&ts, NULL); +} diff --git a/common/time/time.h b/common/time/time.h new file mode 100644 index 0000000..caf1512 --- /dev/null +++ b/common/time/time.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_TIME_TIME_H +#define COMMON_TIME_TIME_H + +#include + +struct NanoTime { + uintmax_t seconds_; + uintmax_t nanoseconds_; + + NanoTime(void) + : seconds_(0), + nanoseconds_(0) + { } + + NanoTime(const NanoTime& src) + : seconds_(src.seconds_), + nanoseconds_(src.nanoseconds_) + { } + + bool operator< (const NanoTime& b) const + { + if (seconds_ == b.seconds_) + return (nanoseconds_ < b.nanoseconds_); + return (seconds_ < b.seconds_); + } + + bool operator> (const NanoTime& b) const + { + if (seconds_ == b.seconds_) + return (nanoseconds_ > b.nanoseconds_); + return (seconds_ > b.seconds_); + } + + bool operator<= (const NanoTime& b) const + { + if (seconds_ == b.seconds_) + return (nanoseconds_ <= b.nanoseconds_); + return (seconds_ <= b.seconds_); + } + + bool operator>= (const NanoTime& b) const + { + if (seconds_ == b.seconds_) + return (nanoseconds_ >= b.nanoseconds_); + return (seconds_ >= b.seconds_); + } + + NanoTime& operator+= (const NanoTime& b) + { + seconds_ += b.seconds_; + nanoseconds_ += b.nanoseconds_; + + if (nanoseconds_ >= 1000000000) { + seconds_++; + nanoseconds_ -= 1000000000; + } + + return (*this); + } + + NanoTime& operator-= (const NanoTime& b) + { + ASSERT("/nano/time", *this >= b); + + if (nanoseconds_ < b.nanoseconds_) { + nanoseconds_ += 1000000000; + seconds_ -= 1; + } + + seconds_ -= b.seconds_; + nanoseconds_ -= b.nanoseconds_; + + return (*this); + } + + static NanoTime current_time(void); + static void sleep(int ms); +}; + +#endif /* !COMMON_TIME_TIME_H */ diff --git a/common/timer/Makefile b/common/timer/Makefile new file mode 100644 index 0000000..09eeae2 --- /dev/null +++ b/common/timer/Makefile @@ -0,0 +1,3 @@ +SUBDIR+= + +include ../../common/subdir.mk diff --git a/common/timer/lib.mk b/common/timer/lib.mk new file mode 100644 index 0000000..a9aba03 --- /dev/null +++ b/common/timer/lib.mk @@ -0,0 +1,3 @@ +VPATH+= ${TOPDIR}/common/timer + +SRCS+= timer.cc diff --git a/common/timer/timer.cc b/common/timer/timer.cc new file mode 100644 index 0000000..29d5f60 --- /dev/null +++ b/common/timer/timer.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009-2010 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include + +#include + +void +Timer::start(void) +{ + struct timeval tv; + int rv; + + rv = gettimeofday(&tv, NULL); + if (rv == -1) + HALT("/timer") << "Could not gettimeofday."; + start_ = (tv.tv_sec * 1000 * 1000) + tv.tv_usec; +} + +void +Timer::stop(void) +{ + struct timeval tv; + int rv; + + rv = gettimeofday(&tv, NULL); + if (rv == -1) + HALT("/timer") << "Could not gettimeofday."; + stop_ = (tv.tv_sec * 1000 * 1000) + tv.tv_usec; + + samples_.push_back(stop_ - start_); +} diff --git a/common/timer/timer.h b/common/timer/timer.h new file mode 100644 index 0000000..80e3a39 --- /dev/null +++ b/common/timer/timer.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_TIMER_TIMER_H +#define COMMON_TIMER_TIMER_H + +class Timer { + uintmax_t start_; + uintmax_t stop_; + + std::vector samples_; +public: + Timer(void) + : start_(), + stop_(), + samples_() + { } + + ~Timer() + { } + + void reset(void) + { + samples_.clear(); + } + + void start(void); + void stop(void); + + uintmax_t sample(void) const + { + if (samples_.size() != 1) + HALT("/timer") << "Requested 1 sample but " << samples_.size() << " available."; + return (samples_[0]); + } + + std::vector samples(void) const + { + return (samples_); + } +}; + +#endif /* !COMMON_TIMER_TIMER_H */ diff --git a/common/types.h b/common/types.h new file mode 100644 index 0000000..2561626 --- /dev/null +++ b/common/types.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_TYPES_H +#define COMMON_TYPES_H + +#if !defined(__OPENNT) +#include /* XXX cstdint? */ +#endif + +#if defined(__OPENNT) +typedef unsigned long long uintmax_t; +typedef long long intmax_t; +typedef unsigned int uintptr_t; /* XXX Appears to be in stddef.h! */ +typedef int socklen_t; +typedef unsigned long long uint64_t; +#endif + +#endif /* !COMMON_TYPES_H */ diff --git a/common/uuid/Makefile b/common/uuid/Makefile new file mode 100644 index 0000000..09eeae2 --- /dev/null +++ b/common/uuid/Makefile @@ -0,0 +1,3 @@ +SUBDIR+= + +include ../../common/subdir.mk diff --git a/common/uuid/lib.mk b/common/uuid/lib.mk new file mode 100644 index 0000000..219a8bb --- /dev/null +++ b/common/uuid/lib.mk @@ -0,0 +1,7 @@ +VPATH+= ${TOPDIR}/common/uuid + +SRCS+= uuid.cc + +ifeq "${OSNAME}" "Linux" +LDADD+= -luuid +endif diff --git a/common/uuid/uuid.cc b/common/uuid/uuid.cc new file mode 100644 index 0000000..9bb0b07 --- /dev/null +++ b/common/uuid/uuid.cc @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: uuid.cc // +// Description: basic handling of standard UUID objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +bool UUID::from_string (const uint8_t* str) +{ +#ifdef USE_LIBUUID + int rv = uuid_parse ((const char*) str, uuid_); + if (rv == -1) + return (false); + ASSERT("/uuid/libuuid", rv == 0); +#else + uint32_t status; + uuid_from_string ((const char*) str, &uuid_, &status); + if (status != uuid_s_ok) + return (false); +#endif + + return (true); +} + +bool UUID::to_string (uint8_t* str) const +{ +#ifdef USE_LIBUUID + uuid_unparse (uuid_, (char*) str); +#else + char *p; + uuid_to_string (&uuid_, &p, NULL); + ASSERT("/uuid/libc", p != NULL); + strcpy ((char*) str, p); + free (p); +#endif + return (true); +} + +bool UUID::from_file (std::string& path) +{ + std::fstream file; + std::string s; + file.open (path.c_str(), std::ios::in); + if (file.good()) + { + file >> s; + return from_string ((const uint8_t*) s.c_str()); + } + return false; +} + +bool UUID::to_file (std::string& path) const +{ + std::fstream file; + file.open (path.c_str(), std::ios::out); + if (file.good()) + { + uint8_t str[UUID_STRING_SIZE + 1]; + to_string (str); + std::string s ((const char*) str); + file << s; + return true; + } + return false; +} + +bool UUID::decode (Buffer& buf) +{ + if (buf.length() < UUID_STRING_SIZE) + return (false); + + uint8_t str[UUID_STRING_SIZE + 1]; + buf.moveout (str, UUID_STRING_SIZE); + str[UUID_STRING_SIZE] = 0; + return from_string (str); +} + +bool UUID::encode (Buffer& buf) const +{ + uint8_t str[UUID_STRING_SIZE + 1]; + to_string (str); + buf.append (str, UUID_STRING_SIZE); + return (true); +} + +void UUID::generate(void) +{ +#ifdef USE_LIBUUID + uuid_generate (uuid_); +#else + uuid_create (&uuid_, NULL); +#endif +} + +std::ostream& operator<< (std::ostream& os, const UUID& uuid) +{ + uint8_t str[UUID_STRING_SIZE + 1]; + uuid.to_string (str); + return os << str; +} diff --git a/common/uuid/uuid.h b/common/uuid/uuid.h new file mode 100644 index 0000000..7494b85 --- /dev/null +++ b/common/uuid/uuid.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef COMMON_UUID_UUID_H +#define COMMON_UUID_UUID_H + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: uuid.h // +// Description: basic handling of standard UUID objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#if defined(__FreeBSD__) +#else +#define USE_LIBUUID +#endif + +#ifdef USE_LIBUUID +#include +#else +#include +#endif + +#define UUID_STRING_SIZE (sizeof (uuid_t) * 2 + 4) + +struct UUID +{ + uuid_t uuid_; + + bool from_string (const uint8_t* str); + bool to_string (uint8_t* str) const; + bool from_file (std::string& path); + bool to_file (std::string& path) const; + bool decode (Buffer&); + bool encode (Buffer& buf) const; + void generate (void); + + UUID () + { + memset (&uuid_, 0, sizeof uuid_); + } + + bool operator< (const UUID& b) const + { + return (memcmp (&uuid_, &b.uuid_, sizeof uuid_) < 0); + } + + bool is_valid () const + { + uuid_t u; return (memcmp (&uuid_, &u, sizeof uuid_) != 0); + } +}; + +std::ostream& operator<< (std::ostream& os, const UUID& uuid); + +#endif /* !COMMON_UUID_UUID_H */ diff --git a/config/Makefile b/config/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/config/config.cc b/config/config.cc new file mode 100644 index 0000000..3d20319 --- /dev/null +++ b/config/config.cc @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: config.cc // +// Description: main configuration parser // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +bool +Config::activate(const std::string& oname) +{ + if (object_map_.find(oname) == object_map_.end()) { + ERROR(log_) << "Object (" << oname << ") can not be activated as it does not exist."; + return (false); + } + + ConfigObject *co = object_map_[oname]; + return (co->activate()); +} + +bool +Config::create(const std::string& cname, const std::string& oname) +{ + if (class_map_.find(cname) == class_map_.end()) { + ERROR(log_) << "Class (" << cname << ") not present."; + return (false); + } + if (object_map_.find(oname) != object_map_.end()) { + ERROR(log_) << "Refusing to create object (" << oname << ") twice."; + return (false); + } + + ConfigClass *cc = class_map_[cname]; + + object_map_[oname] = new ConfigObject(this, oname, cc, cc->allocate()); + + return (true); +} + +bool +Config::set(const std::string& oname, const std::string& mname, + const std::string& vstr) +{ + if (object_map_.find(oname) == object_map_.end()) { + ERROR(log_) << "Can not set member value on object (" << oname << ") that does not exist."; + return (false); + } + + ConfigObject *co = object_map_[oname]; + +#if 0 + ConfigType *ct = cc->member(mname); + if (ct == NULL) { + ERROR(log_) << "No such member (" << mname << ") in object (" << oname << ")"; + return (false); + } +#endif + + if (vstr[0] == '$') { + std::string::const_iterator dot; + + dot = std::find(vstr.begin(), vstr.end(), '.'); + + std::string ooname(vstr.begin() + 1, dot); + std::string omname(dot + 1, vstr.end()); + + if (ooname == "" || omname == "") { + ERROR(log_) << "Refernece to invalid object (" << ooname << ") or member (" << omname << ") by name."; + return (false); + } + + if (object_map_.find(ooname) == object_map_.end()) { + ERROR(log_) << "Reference to non-existant object (" << ooname << ")"; + return (false); + } + + ConfigObject *oco = object_map_[ooname]; + + object_field_string_map_t::const_iterator fsit; + fsit = field_strings_map_.find(object_field_string_map_t::key_type(oco, omname)); + if (fsit == field_strings_map_.end()) { + ERROR(log_) << "Reference to unset member (" << omname << ") in object (" << ooname << ")"; + return (false); + } + + return (set(oname, mname, fsit->second)); + } + + if (!co->set(mname, vstr)) { + ERROR(log_) << "Member (" << mname << ") in object (" << oname << ") could not be set."; + return (false); + } + + field_strings_map_[object_field_string_map_t::key_type(co, mname)] = vstr; + + return (true); +} + +void +Config::import(ConfigClass *cc) +{ + if (class_map_.find(cc->name_) != class_map_.end()) + return; + + class_map_[cc->name_] = cc; +} + +void +Config::marshall(ConfigExporter *exp) const +{ + std::map::const_iterator it; + + for (it = object_map_.begin(); it != object_map_.end(); ++it) + exp->object(it->second, it->first); +} diff --git a/config/config.h b/config/config.h new file mode 100644 index 0000000..e917403 --- /dev/null +++ b/config/config.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_H +#define CONFIG_CONFIG_H + +#include + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: config.h // +// Description: main configuration parser // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Config { + typedef std::map, std::string> object_field_string_map_t; + + LogHandle log_; + std::map class_map_; + std::map object_map_; + object_field_string_map_t field_strings_map_; + +public: + Config(void) + : log_("/config"), + class_map_(), + object_map_() + { } + + ~Config() + { + std::map::const_iterator it; + for (it = object_map_.begin(); it != object_map_.end(); ++it) + delete it->second; + } + + bool activate(const std::string&); + bool create(const std::string&, const std::string&); + bool set(const std::string&, const std::string&, const std::string&); + + void import(ConfigClass *); + + void marshall(ConfigExporter *) const; + + ConfigObject *lookup(const std::string& oname) const + { + std::map::const_iterator omit; + + omit = object_map_.find(oname); + if (omit == object_map_.end()) + return (NULL); + return (omit->second); + } +}; + +#endif /* !CONFIG_CONFIG_H */ diff --git a/config/config_class.cc b/config/config_class.cc new file mode 100644 index 0000000..c1cb455 --- /dev/null +++ b/config/config_class.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: config_class.cc // +// Description: parser for configuration classes // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +ConfigClass::~ConfigClass() +{ + std::map::iterator it; + + while ((it = members_.begin()) != members_.end()) { + delete it->second; + members_.erase(it); + } + + delete factory_; +} + +bool +ConfigClass::set(ConfigObject *co, const std::string& mname, const std::string& vstr) const +{ +#if 0 + ASSERT("/config/class", co->class_ == this); +#endif + + std::map::const_iterator it = members_.find(mname); + if (it == members_.end()) { + ERROR("/config/class") << "Object member (" << mname << ") does not exist."; + return (false); + } + return (it->second->set(co, vstr)); +} + +void +ConfigClass::marshall(ConfigExporter *exp, const ConfigClassInstance *co) const +{ + std::map::const_iterator it; + for (it = members_.begin(); it != members_.end(); ++it) { + exp->field(co, it->second, it->first); + } +} diff --git a/config/config_class.h b/config/config_class.h new file mode 100644 index 0000000..de54d19 --- /dev/null +++ b/config/config_class.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_CLASS_H +#define CONFIG_CONFIG_CLASS_H + +#include + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: config_class.h // +// Description: parser for configuration classes // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Config; +class ConfigExporter; +class ConfigType; +class ConfigObject; + +/* + * A base type for class instances. + */ +class ConfigClassInstance { +protected: + ConfigClassInstance(void) + { } +public: + virtual ~ConfigClassInstance() + { } + + virtual bool activate(const ConfigObject *) = 0; +}; + +class ConfigClassMember { +protected: + ConfigClassMember(void) + { } +public: + virtual ~ConfigClassMember() + { } + + virtual void marshall(ConfigExporter *, const ConfigClassInstance *) const = 0; + virtual bool set(ConfigObject *, const std::string&) const = 0; + virtual ConfigType *type(void) const = 0; +}; + +class ConfigClass { + friend class Config; + friend struct ConfigObject; + + std::string name_; + Factory *factory_; + std::map members_; +protected: + ConfigClass(const std::string& xname, Factory *factory) + : name_(xname), + factory_(factory), + members_() + { } + + virtual ~ConfigClass(); + + template + void add_member(const std::string& mname, Tc type, Tf Ti::*fieldp); + +private: + ConfigClassInstance *allocate(void) const + { + return (factory_->create()); + } + + bool set(ConfigObject *, const std::string&, const std::string&) const; + +public: + void marshall(ConfigExporter *, const ConfigClassInstance *) const; + + std::string name(void) const + { + return (name_); + } +}; + +#endif /* !CONFIG_CONFIG_CLASS_H */ diff --git a/config/config_class_address.cc b/config/config_class_address.cc new file mode 100644 index 0000000..cdc7ed4 --- /dev/null +++ b/config/config_class_address.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +ConfigClassAddress config_class_address; + +bool +ConfigClassAddress::Instance::activate(const ConfigObject *) +{ + switch (family_) { + case SocketAddressFamilyIP: + case SocketAddressFamilyIPv4: + case SocketAddressFamilyIPv6: + if (path_ != "") { + ERROR("/config/class/address") << "IP socket has path field set, which is only valid for Unix domain sockets."; + return (false); + } + return (true); + + case SocketAddressFamilyUnix: + if (host_ != "" || port_ != "") { + ERROR("/config/class/address") << "Unix domain socket has host and/or port field set, which is only valid for IP sockets."; + return (false); + } + return (true); + + default: + ERROR("/config/class/address") << "Unsupported address family."; + return (false); + } +} diff --git a/config/config_class_address.h b/config/config_class_address.h new file mode 100644 index 0000000..4e51dd5 --- /dev/null +++ b/config/config_class_address.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_CLASS_ADDRESS_H +#define CONFIG_CONFIG_CLASS_ADDRESS_H + +#include +#include +#include + +class ConfigClassAddress : public ConfigClass { +public: + struct Instance : public ConfigClassInstance { + SocketAddressFamily family_; + std::string host_; + std::string port_; + std::string path_; + ConfigProto proto_; + + Instance(void) + : family_(SocketAddressFamilyUnspecified), + host_(""), + port_(""), + path_(""), + proto_(ConfigProtoNone) + { + } + + bool activate(const ConfigObject *); + }; + + ConfigClassAddress(const std::string& xname = "address") + : ConfigClass(xname, new ConstructorFactory) + { + add_member("family", &config_type_address_family, &Instance::family_); + add_member("host", &config_type_string, &Instance::host_); + add_member("port", &config_type_string, &Instance::port_); /* XXX enum? */ + add_member("path", &config_type_string, &Instance::path_); + add_member("proto", &config_type_proto, &Instance::proto_); + } + + ~ConfigClassAddress() + { } +}; + +extern ConfigClassAddress config_class_address; + +#endif /* !CONFIG_CONFIG_CLASS_ADDRESS_H */ diff --git a/config/config_class_log_mask.cc b/config/config_class_log_mask.cc new file mode 100644 index 0000000..905ae0d --- /dev/null +++ b/config/config_class_log_mask.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +ConfigClassLogMask config_class_log_mask; + +bool +ConfigClassLogMask::Instance::activate(const ConfigObject *) +{ + if (!Log::mask(regex_, mask_)) { + ERROR("/config/class/logmask") << "Could not set log mask."; + return (false); + } + + return (true); +} diff --git a/config/config_class_log_mask.h b/config/config_class_log_mask.h new file mode 100644 index 0000000..8410ac2 --- /dev/null +++ b/config/config_class_log_mask.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_CLASS_LOG_MASK_H +#define CONFIG_CONFIG_CLASS_LOG_MASK_H + +#include +#include + +class ConfigClassLogMask : public ConfigClass { + struct Instance : public ConfigClassInstance { + std::string regex_; + Log::Priority mask_; + + bool activate(const ConfigObject *); + }; +public: + ConfigClassLogMask(void) + : ConfigClass("log-mask", new ConstructorFactory) + { + add_member("regex", &config_type_string, &Instance::regex_); + add_member("mask", &config_type_log_level, &Instance::mask_); + } + + ~ConfigClassLogMask() + { } +}; + +extern ConfigClassLogMask config_class_log_mask; + +#endif /* !CONFIG_CONFIG_CLASS_LOG_MASK_H */ diff --git a/config/config_exporter.h b/config/config_exporter.h new file mode 100644 index 0000000..0dd8cb9 --- /dev/null +++ b/config/config_exporter.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_EXPORTER_H +#define CONFIG_CONFIG_EXPORTER_H + +class ConfigClass; +class ConfigClassInstance; +class ConfigClassMember; +struct ConfigObject; +class ConfigType; + +class ConfigExporter { +protected: + ConfigExporter(void) + { } + + virtual ~ConfigExporter() + { } + +public: + virtual void field(const ConfigClassInstance *, const ConfigClassMember *, const std::string&) = 0; + virtual void object(const ConfigObject *, const std::string&) = 0; + virtual void value(const ConfigType *, const std::string&) = 0; +}; + +#endif /* !CONFIG_CONFIG_EXPORTER_H */ diff --git a/config/config_object.cc b/config/config_object.cc new file mode 100644 index 0000000..04396e6 --- /dev/null +++ b/config/config_object.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: config_object.cc // +// Description: parser for configuration objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +bool +ConfigObject::activate(void) const +{ + return (instance_->activate(this)); +} + +void +ConfigObject::marshall(ConfigExporter *exp) const +{ + class_->marshall(exp, instance_); +} + +bool +ConfigObject::set(const std::string& mname, const std::string& vstr) +{ + return (class_->set(this, mname, vstr)); +} diff --git a/config/config_object.h b/config/config_object.h new file mode 100644 index 0000000..d2f10b8 --- /dev/null +++ b/config/config_object.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_OBJECT_H +#define CONFIG_CONFIG_OBJECT_H + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: config_object.h // +// Description: parser for configuration objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Config; + +struct ConfigObject { + Config *config_; + std::string name_; + const ConfigClass *class_; + ConfigClassInstance *instance_; + + ConfigObject(Config *config, const std::string& name, const ConfigClass *cc, ConfigClassInstance *inst) + : config_(config), + name_(name), + class_(cc), + instance_(inst) + { } + + virtual ~ConfigObject() + { + delete instance_; + } + + bool activate(void) const; + void marshall(ConfigExporter *) const; + bool set(const std::string&, const std::string&); +}; + +template +void ConfigClass::add_member(const std::string& mname, Tc type, Tf Ti::*fieldp) +{ + struct TypedConfigClassMember : public ConfigClassMember { + Tc config_type_; + Tf Ti::*config_field_; + + TypedConfigClassMember(Tc config_type, Tf Ti::*config_field) + : config_type_(config_type), + config_field_(config_field) + { } + virtual ~TypedConfigClassMember() + { } + + void marshall(ConfigExporter *exp, const ConfigClassInstance *instance) const + { + const Ti *inst = dynamic_cast(instance); + ASSERT("/config/class/field", inst != NULL); + config_type_->marshall(exp, &(inst->*config_field_)); + } + + bool set(ConfigObject *co, const std::string& vstr) const + { + Ti *inst = dynamic_cast(co->instance_); + ASSERT("/config/class/field", inst != NULL); + return (config_type_->set(co, vstr, &(inst->*config_field_))); + } + + ConfigType *type(void) const + { + return (config_type_); + } + }; + + ASSERT("/config/class/" + name_, members_.find(mname) == members_.end()); + members_[mname] = new TypedConfigClassMember(type, fieldp); +} + +#endif /* !CONFIG_CONFIG_OBJECT_H */ diff --git a/config/config_type.h b/config/config_type.h new file mode 100644 index 0000000..0cdc992 --- /dev/null +++ b/config/config_type.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_H +#define CONFIG_CONFIG_TYPE_H + +class Config; +class ConfigExporter; + +class ConfigType { + std::string name_; +protected: + ConfigType(const std::string& xname) + : name_(xname) + { } + + virtual ~ConfigType() + { } +public: + std::string name(void) const + { + return (name_); + } +}; + +#endif /* !CONFIG_CONFIG_TYPE_H */ diff --git a/config/config_type_address_family.cc b/config/config_type_address_family.cc new file mode 100644 index 0000000..81709d4 --- /dev/null +++ b/config/config_type_address_family.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +static struct ConfigTypeAddressFamily::Mapping config_type_address_family_map[] = { + { "IP", SocketAddressFamilyIP }, + { "IPv4", SocketAddressFamilyIPv4 }, + { "IPv6", SocketAddressFamilyIPv6 }, + { "Unix", SocketAddressFamilyUnix }, + { NULL, SocketAddressFamilyUnspecified } +}; + +ConfigTypeAddressFamily + config_type_address_family("address-family", config_type_address_family_map); diff --git a/config/config_type_address_family.h b/config/config_type_address_family.h new file mode 100644 index 0000000..230918f --- /dev/null +++ b/config/config_type_address_family.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_ADDRESS_FAMILY_H +#define CONFIG_CONFIG_TYPE_ADDRESS_FAMILY_H + +#include + +#include + +typedef ConfigTypeEnum ConfigTypeAddressFamily; + +extern ConfigTypeAddressFamily config_type_address_family; + +#endif /* !CONFIG_CONFIG_TYPE_ADDRESS_FAMILY_H */ diff --git a/config/config_type_enum.h b/config/config_type_enum.h new file mode 100644 index 0000000..48fc7ce --- /dev/null +++ b/config/config_type_enum.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_ENUM_H +#define CONFIG_CONFIG_TYPE_ENUM_H + +#include + +#include +#include + +template +class ConfigTypeEnum : public ConfigType { +public: + struct Mapping { + const char *string_; + E enum_; + }; +private: + std::map enum_map_; +public: + ConfigTypeEnum(const std::string& xname, struct Mapping *mappings) + : ConfigType(xname), + enum_map_() + { + ASSERT("/config/type/enum", mappings != NULL); + while (mappings->string_ != NULL) { + enum_map_[mappings->string_] = mappings->enum_; + mappings++; + } + } + + ~ConfigTypeEnum() + { } + + void marshall(ConfigExporter *exp, const E *enump) const + { + E xenum = *enump; + + typename std::map::const_iterator it; + for (it = enum_map_.begin(); it != enum_map_.end(); ++it) { + if (it->second != xenum) + continue; + exp->value(this, it->first); + return; + } + HALT("/config/type/enum") << "Trying to marshall unknown enum."; + } + + bool set(ConfigObject *, const std::string& vstr, E *enump) + { + if (enum_map_.find(vstr) == enum_map_.end()) { + ERROR("/config/type/enum") << "Invalid value (" << vstr << ")"; + return (false); + } + + *enump = enum_map_[vstr]; + + return (true); + } +}; + +#endif /* !CONFIG_CONFIG_TYPE_ENUM_H */ diff --git a/config/config_type_flags.h b/config/config_type_flags.h new file mode 100644 index 0000000..7519ad1 --- /dev/null +++ b/config/config_type_flags.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_FLAGS_H +#define CONFIG_CONFIG_TYPE_FLAGS_H + +#include + +#include +#include + +template +class ConfigTypeFlags : public ConfigType { +public: + struct Mapping { + const char *string_; + T flag_; + }; +private: + std::map flag_map_; +public: + ConfigTypeFlags(const std::string& xname, struct Mapping *mappings) + : ConfigType(xname), + flag_map_() + { + ASSERT("/config/type/flags", mappings != NULL); + while (mappings->string_ != NULL) { + flag_map_[mappings->string_] = mappings->flag_; + mappings++; + } + } + + ~ConfigTypeFlags() + { } + + void marshall(ConfigExporter *exp, const T *flagsp) const + { + T flags = *flagsp; + + std::string str; + typename std::map::const_iterator it; + for (it = flag_map_.begin(); it != flag_map_.end(); ++it) { + if ((it->second & flags) == 0) + continue; + flags &= ~it->second; + if (!str.empty()) + str += "|"; + str += it->first; + } + if (flags != 0) + HALT("/config/type/flags") << "Trying to marshall unknown flags."; + if (flags == 0 && str.empty()) + str = "0"; + exp->value(this, str); + } + + bool set(ConfigObject *, const std::string& vstr, T *flagsp) + { + if (flag_map_.find(vstr) == flag_map_.end()) { + ERROR("/config/type/flags") << "Invalid value (" << vstr << ")"; + return (false); + } + + *flagsp |= flag_map_[vstr]; + + return (true); + } +}; + +#endif /* !CONFIG_CONFIG_TYPE_FLAGS_H */ diff --git a/config/config_type_int.cc b/config/config_type_int.cc new file mode 100644 index 0000000..682d895 --- /dev/null +++ b/config/config_type_int.cc @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(__OPENNT) +#include +#endif +#include +#include + +#include +#include + +ConfigTypeInt config_type_int; + +void +ConfigTypeInt::marshall(ConfigExporter *exp, const intmax_t *valp) const +{ + intmax_t val = *valp; + + char buf[sizeof val * 2 + 2 + 1]; + snprintf(buf, sizeof buf, "0x%016jx", val); + exp->value(this, buf); +} + +bool +ConfigTypeInt::set(ConfigObject *, const std::string& vstr, intmax_t *valp) +{ + const char *str = vstr.c_str(); + char *endp; + intmax_t imax; + +#if !defined(__OPENNT) + imax = strtoimax(str, &endp, 0); +#else + imax = strtoll(str, &endp, 0); +#endif + if (*endp != '\0') { + ERROR("/config/type/int") << "Invalid numeric format."; + return (false); + } + + *valp = imax; + + return (true); +} diff --git a/config/config_type_int.h b/config/config_type_int.h new file mode 100644 index 0000000..90c6f4a --- /dev/null +++ b/config/config_type_int.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_INT_H +#define CONFIG_CONFIG_TYPE_INT_H + +#include + +#include + +class ConfigTypeInt : public ConfigType { +public: + ConfigTypeInt(void) + : ConfigType("int") + { } + + ~ConfigTypeInt() + { } + + void marshall(ConfigExporter *, const intmax_t *) const; + + bool set(ConfigObject *, const std::string&, intmax_t *); +}; + +extern ConfigTypeInt config_type_int; + +#endif /* !CONFIG_CONFIG_TYPE_INT_H */ diff --git a/config/config_type_log_level.cc b/config/config_type_log_level.cc new file mode 100644 index 0000000..8136688 --- /dev/null +++ b/config/config_type_log_level.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +struct ConfigTypeLogLevel::Mapping config_type_log_level_map[] = { + { "EMERG", Log::Emergency }, + { "ALERT", Log::Alert }, + { "CRIT", Log::Critical }, + { "ERR", Log::Error }, + { "WARNING", Log::Warning }, + { "NOTICE", Log::Notice }, + { "INFO", Log::Info }, + { "DEBUG", Log::Debug }, + { NULL, Log::Debug } +}; + +ConfigTypeLogLevel + config_type_log_level("log-level", config_type_log_level_map); diff --git a/config/config_type_log_level.h b/config/config_type_log_level.h new file mode 100644 index 0000000..1ead2c2 --- /dev/null +++ b/config/config_type_log_level.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_LOG_LEVEL_H +#define CONFIG_CONFIG_TYPE_LOG_LEVEL_H + +#include + +typedef ConfigTypeEnum ConfigTypeLogLevel; + +extern ConfigTypeLogLevel config_type_log_level; + +#endif /* !CONFIG_CONFIG_TYPE_LOG_LEVEL_H */ diff --git a/config/config_type_pointer.cc b/config/config_type_pointer.cc new file mode 100644 index 0000000..3ca9f96 --- /dev/null +++ b/config/config_type_pointer.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +ConfigTypePointer config_type_pointer; + +void +ConfigTypePointer::marshall(ConfigExporter *exp, ConfigObject *const *cop) const +{ + const ConfigObject *co = *cop; + + if (co == NULL) + exp->value(this, "None"); + else + exp->value(this, co->name_); +} + +bool +ConfigTypePointer::set(ConfigObject *co, const std::string& vstr, ConfigObject **cop) +{ + if (vstr == "None") { + *cop = NULL; + return (true); + } + + ConfigObject *target = co->config_->lookup(vstr); + if (target == NULL) { + ERROR("/config/type/pointer") << "Referenced object (" << vstr << ") does not exist."; + return (false); + } + + *cop = target; + + return (true); +} diff --git a/config/config_type_pointer.h b/config/config_type_pointer.h new file mode 100644 index 0000000..2d43766 --- /dev/null +++ b/config/config_type_pointer.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_POINTER_H +#define CONFIG_CONFIG_TYPE_POINTER_H + +#include + +#include + +class ConfigTypePointer : public ConfigType { +public: + ConfigTypePointer(void) + : ConfigType("pointer") + { } + + ~ConfigTypePointer() + { } + + void marshall(ConfigExporter *, ConfigObject *const *) const; + + bool set(ConfigObject *, const std::string&, ConfigObject **); +}; + +extern ConfigTypePointer config_type_pointer; + +#endif /* !CONFIG_CONFIG_TYPE_POINTER_H */ diff --git a/config/config_type_proto.cc b/config/config_type_proto.cc new file mode 100644 index 0000000..f2ef582 --- /dev/null +++ b/config/config_type_proto.cc @@ -0,0 +1,10 @@ +#include "config_type_proto.h" + +static struct ConfigTypeProto::Mapping config_type_proto_map[] = { + { "TCP", ConfigProtoTCP }, + { "TCP_POOL", ConfigProtoTCPPool }, + { NULL, ConfigProtoNone } +}; + +ConfigTypeProto + config_type_proto("proto", config_type_proto_map); diff --git a/config/config_type_proto.h b/config/config_type_proto.h new file mode 100644 index 0000000..5e78ff5 --- /dev/null +++ b/config/config_type_proto.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_CONFIG_TYPE_PROTO_H +#define CONFIG_CONFIG_TYPE_PROTO_H + +#include + +enum ConfigProto { + ConfigProtoTCP, + ConfigProtoTCPPool, + ConfigProtoNone, +}; + +typedef ConfigTypeEnum ConfigTypeProto; + +extern ConfigTypeProto config_type_proto; + +#endif /* !CONFIG_CONFIG_TYPE_PROTO_H */ diff --git a/config/config_type_string.cc b/config/config_type_string.cc new file mode 100644 index 0000000..d67d6f2 --- /dev/null +++ b/config/config_type_string.cc @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2009 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +ConfigTypeString config_type_string; diff --git a/config/config_type_string.h b/config/config_type_string.h new file mode 100644 index 0000000..d133c7e --- /dev/null +++ b/config/config_type_string.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CONFIG_CONFIG_TYPE_STRING_H +#define CONFIG_CONFIG_TYPE_STRING_H + +#include + +#include +#include + +class ConfigTypeString : public ConfigType { +public: + ConfigTypeString(void) + : ConfigType("string") + { } + + ~ConfigTypeString() + { } + + void marshall(ConfigExporter *exp, const std::string *stringp) const + { + exp->value(this, *stringp); + } + + bool set(ConfigObject *, const std::string& vstr, std::string *stringp) + { + if (*vstr.begin() != '"') { + ERROR("/config/type/string") << "String does not begin with '\"'."; + return (false); + } + if (*vstr.rbegin() != '"') { + ERROR("/config/type/string") << "String does not end with '\"'."; + return (false); + } + + std::string str(vstr.begin() + 1, vstr.end() - 1); + if (std::find(str.begin(), str.end(), '"') != str.end()) { + ERROR("/config/type/string") << "String has '\"' other than at beginning or end."; + return (false); + } + + *stringp = str; + return (true); + } +}; + +extern ConfigTypeString config_type_string; + +#endif /* !CONFIG_CONFIG_TYPE_STRING_H */ diff --git a/config/lib.mk b/config/lib.mk new file mode 100644 index 0000000..b4dd9e6 --- /dev/null +++ b/config/lib.mk @@ -0,0 +1,14 @@ +VPATH+= ${TOPDIR}/config + +SRCS+= config.cc +SRCS+= config_class.cc +SRCS+= config_class_log_mask.cc +SRCS+= config_object.cc +SRCS+= config_type_int.cc +SRCS+= config_type_log_level.cc +SRCS+= config_type_pointer.cc +SRCS+= config_type_string.cc +SRCS+= config_type_proto.cc + +SRCS_io_socket+=config_class_address.cc +SRCS_io_socket+=config_type_address_family.cc diff --git a/crypto/Makefile b/crypto/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/crypto/TODO b/crypto/TODO new file mode 100644 index 0000000..e00bb9e --- /dev/null +++ b/crypto/TODO @@ -0,0 +1,5 @@ +o) Find a good way to integrate the CryptoThread, CryptoSystem code. +o) Process all cryptographic operations in the (a?) CryptoThread. +o) DH infrastructure. +o) BIGNUM infrastructure? +o) Lots more algorithms. diff --git a/crypto/crypto_encryption.cc b/crypto/crypto_encryption.cc new file mode 100644 index 0000000..3363b05 --- /dev/null +++ b/crypto/crypto_encryption.cc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include + +namespace { + struct MethodRegistrarKey; + typedef Registrar MethodRegistrar; +} + +CryptoEncryption::Method::Method(const std::string& name) +: name_(name) +{ + MethodRegistrar::instance()->enter(this); +} + +const CryptoEncryption::Method * +CryptoEncryption::Method::method(CryptoEncryption::Cipher cipher) +{ + std::set method_set = MethodRegistrar::instance()->enumerate(); + std::set::const_iterator it; + + for (it = method_set.begin(); it != method_set.end(); ++it) { + const CryptoEncryption::Method *m = *it; + std::set cipher_set = m->ciphers(); + if (cipher_set.find(cipher) == cipher_set.end()) + continue; + + return (*it); + } + + ERROR("/crypto/encryption/method") << "Could not find a method for cipher: " << cipher; + + return (NULL); +} + +std::ostream& +operator<< (std::ostream& os, CryptoEncryption::Algorithm algorithm) +{ + switch (algorithm) { + case CryptoEncryption::TripleDES: + return (os << "3DES"); + case CryptoEncryption::AES128: + return (os << "AES128"); + case CryptoEncryption::AES192: + return (os << "AES192"); + case CryptoEncryption::AES256: + return (os << "AES256"); + case CryptoEncryption::Blowfish: + return (os << "Blowfish"); + case CryptoEncryption::CAST: + return (os << "CAST"); + case CryptoEncryption::IDEA: + return (os << "IDEA"); + case CryptoEncryption::RC4: + return (os << "RC4"); + } + NOTREACHED("/crypto/encryption"); +} + +std::ostream& +operator<< (std::ostream& os, CryptoEncryption::Mode mode) +{ + switch (mode) { + case CryptoEncryption::CBC: + return (os << "CBC"); + case CryptoEncryption::CTR: + return (os << "CTR"); + case CryptoEncryption::Stream: + return (os << "Stream"); + } + NOTREACHED("/crypto/encryption"); +} + +std::ostream& +operator<< (std::ostream& os, CryptoEncryption::Cipher cipher) +{ + return (os << cipher.first << "/" << cipher.second); +} diff --git a/crypto/crypto_encryption.h b/crypto/crypto_encryption.h new file mode 100644 index 0000000..d147817 --- /dev/null +++ b/crypto/crypto_encryption.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CRYPTO_CRYPTO_ENCRYPTION_H +#define CRYPTO_CRYPTO_ENCRYPTION_H + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_encryption.h // +// Description: basic encryption machinery // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace CryptoEncryption { + class Method; + + enum Algorithm { + TripleDES, + AES128, + AES192, + AES256, + Blowfish, + CAST, + IDEA, + RC4, + }; + + enum Mode { + CBC, + CTR, + Stream, + }; + typedef std::pair Cipher; + + enum Operation { + Encrypt, + Decrypt, + }; + + class Session { + protected: + Session(void) + { } + + public: + virtual ~Session() + { } + + virtual unsigned block_size(void) const = 0; + virtual unsigned key_size(void) const = 0; + virtual unsigned iv_size(void) const = 0; + + virtual Session *clone(void) const = 0; + + virtual bool initialize(Operation, const Buffer *, const Buffer *) = 0; + + virtual bool cipher(Buffer *, const Buffer *) = 0; + + //virtual Action *submit(Buffer *, EventCallback *) = 0; + }; + + class Method { + std::string name_; + protected: + Method(const std::string&); + + virtual ~Method() + { } + public: + virtual std::set ciphers(void) const = 0; + virtual Session *session(Cipher) const = 0; + + static const Method *method(Cipher); + }; +} + +std::ostream& operator<< (std::ostream&, CryptoEncryption::Algorithm); +std::ostream& operator<< (std::ostream&, CryptoEncryption::Mode); +std::ostream& operator<< (std::ostream&, CryptoEncryption::Cipher); + +#endif /* !CRYPTO_CRYPTO_ENCRYPTION_H */ diff --git a/crypto/crypto_encryption_openssl.cc b/crypto/crypto_encryption_openssl.cc new file mode 100644 index 0000000..e956229 --- /dev/null +++ b/crypto/crypto_encryption_openssl.cc @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_encryption_openssl.cc // +// Description: interface to the OpenSSL encryption functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace { + class SessionEVP : public CryptoEncryption::Session { + LogHandle log_; + const EVP_CIPHER *cipher_; + EVP_CIPHER_CTX ctx_; + public: + SessionEVP(const EVP_CIPHER *xcipher) + : log_("/crypto/encryption/session/openssl"), + cipher_(xcipher), + ctx_() + { + EVP_CIPHER_CTX_init(&ctx_); + } + + ~SessionEVP() + { + EVP_CIPHER_CTX_cleanup(&ctx_); + } + + unsigned block_size(void) const + { + return (EVP_CIPHER_block_size(cipher_)); + } + + unsigned key_size(void) const + { + return (EVP_CIPHER_key_length(cipher_)); + } + + unsigned iv_size(void) const + { + return (EVP_CIPHER_iv_length(cipher_)); + } + + Session *clone(void) const + { + return (new SessionEVP(cipher_)); + } + + bool initialize(CryptoEncryption::Operation operation, const Buffer *key, const Buffer *iv) + { + if (key->length() < (size_t)EVP_CIPHER_key_length(cipher_)) + return (false); + + if (iv->length() < (size_t)EVP_CIPHER_iv_length(cipher_)) + return (false); + + int enc; + switch (operation) { + case CryptoEncryption::Encrypt: + enc = 1; + break; + case CryptoEncryption::Decrypt: + enc = 0; + break; + default: + return (false); + } + + uint8_t keydata[key->length()]; + key->copyout(keydata, sizeof keydata); + + uint8_t ivdata[iv->length()]; + iv->copyout(ivdata, sizeof ivdata); + + int rv = EVP_CipherInit(&ctx_, cipher_, keydata, ivdata, enc); + if (rv == 0) + return (false); + + return (true); + } + + bool cipher(Buffer *out, const Buffer *in) + { + /* + * We process a single, large, linear byte buffer here rather + * than going a BufferSegment at a time, even though the byte + * buffer is less efficient than some alternatives, because + * there are padding and buffering implications if each + * BufferSegment's length is not modular to the block size. + */ + uint8_t indata[in->length()]; + in->copyout(indata, sizeof indata); + + uint8_t outdata[sizeof indata]; + int rv = EVP_Cipher(&ctx_, outdata, indata, sizeof indata); + if (rv == 0) + return (false); + out->append(outdata, sizeof outdata); + return (true); + } + + /* + Action *submit(Buffer *in, EventCallback *cb) + { + Buffer out; + if (!cipher(&out, in)) { + in->clear(); + cb->param(Event::Error); + return (cb->schedule()); + } + in->clear(); + cb->param(Event(Event::Done, out)); + return (cb->schedule()); + } + */ + }; + + class SessionAES128CTR : public CryptoEncryption::Session { + LogHandle log_; + AES_KEY key_; + uint8_t iv_[AES_BLOCK_SIZE]; + public: + SessionAES128CTR(void) + : log_("/crypto/encryption/session/openssl"), + key_(), + iv_() + { } + + ~SessionAES128CTR() + { } + + unsigned block_size(void) const + { + return (AES_BLOCK_SIZE); + } + + unsigned key_size(void) const + { + return (AES_BLOCK_SIZE); + } + + unsigned iv_size(void) const + { + return (AES_BLOCK_SIZE); + } + + Session *clone(void) const + { + return (new SessionAES128CTR()); + } + + bool initialize(CryptoEncryption::Operation operation, const Buffer *key, const Buffer *iv) + { + (void)operation; + + if (key->length() != AES_BLOCK_SIZE) + return (false); + + if (iv->length() != AES_BLOCK_SIZE) + return (false); + + uint8_t keydata[key->length()]; + key->copyout(keydata, sizeof keydata); + + AES_set_encrypt_key(keydata, AES_BLOCK_SIZE * 8, &key_); + + iv->copyout(iv_, sizeof iv_); + + return (true); + } + + bool cipher(Buffer *out, const Buffer *in) + { + ASSERT(log_, in->length() % AES_BLOCK_SIZE == 0); + + /* + * Temporaries for AES_ctr128_encrypt. + * + * Their values only need to persist if we aren't using block-sized + * buffers, which we are. We could just use AES_ctr128_inc and do + * the crypt operation by hand here. + */ + uint8_t counterbuf[AES_BLOCK_SIZE]; /* Will be initialized if countern==0. */ + unsigned countern = 0; + + /* + * We process a single, large, linear byte buffer here rather + * than going a BufferSegment at a time, even though the byte + * buffer is less efficient than some alternatives, because + * there are padding and buffering implications if each + * BufferSegment's length is not modular to the block size. + */ + uint8_t indata[in->length()]; + in->copyout(indata, sizeof indata); + + uint8_t outdata[sizeof indata]; + AES_ctr128_encrypt(indata, outdata, sizeof indata, &key_, iv_, counterbuf, &countern); + + out->append(outdata, sizeof outdata); + return (true); + } + + /* + Action *submit(Buffer *in, EventCallback *cb) + { + Buffer out; + if (!cipher(&out, in)) { + in->clear(); + cb->param(Event::Error); + return (cb->schedule()); + } + in->clear(); + cb->param(Event(Event::Done, out)); + return (cb->schedule()); + } + */ + }; + + class MethodOpenSSL : public CryptoEncryption::Method { + LogHandle log_; + FactoryMap cipher_map_; + public: + MethodOpenSSL(void) + : CryptoEncryption::Method("OpenSSL"), + log_("/crypto/encryption/openssl"), + cipher_map_() + { + OpenSSL_add_all_algorithms(); + + factory evp_factory; + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::TripleDES, CryptoEncryption::CBC), evp_factory(EVP_des_ede3_cbc())); + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::AES128, CryptoEncryption::CBC), evp_factory(EVP_aes_128_cbc())); + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::AES192, CryptoEncryption::CBC), evp_factory(EVP_aes_192_cbc())); + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::AES256, CryptoEncryption::CBC), evp_factory(EVP_aes_256_cbc())); +#if 0 + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::AES128, CryptoEncryption::CTR), evp_factory(EVP_aes_128_ctr())); + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::AES192, CryptoEncryption::CTR), evp_factory(EVP_aes_192_ctr())); + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::AES256, CryptoEncryption::CTR), evp_factory(EVP_aes_256_ctr())); +#else + factory aes128ctr_factory; + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::AES128, CryptoEncryption::CTR), aes128ctr_factory()); +#endif +#ifndef OPENSSL_NO_BF + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::Blowfish, CryptoEncryption::CBC), evp_factory(EVP_bf_cbc())); +#endif +#ifndef OPENSSL_NO_CAST + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::CAST, CryptoEncryption::CBC), evp_factory(EVP_cast5_cbc())); +#endif +#ifndef OPENSSL_NO_IDEA + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::IDEA, CryptoEncryption::CBC), evp_factory(EVP_idea_cbc())); +#endif + cipher_map_.enter(CryptoEncryption::Cipher(CryptoEncryption::RC4, CryptoEncryption::Stream), evp_factory(EVP_rc4())); + + /* XXX Register. */ + } + + ~MethodOpenSSL() + { + /* XXX Unregister. */ + } + + std::set ciphers(void) const + { + return (cipher_map_.keys()); + } + + CryptoEncryption::Session *session(CryptoEncryption::Cipher cipher) const + { + return (cipher_map_.create(cipher)); + } + }; + + static MethodOpenSSL crypto_encryption_method_openssl; +} diff --git a/crypto/crypto_hash.cc b/crypto/crypto_hash.cc new file mode 100644 index 0000000..e2c1545 --- /dev/null +++ b/crypto/crypto_hash.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include + +namespace { + struct MethodRegistrarKey; + typedef Registrar MethodRegistrar; +} + +CryptoHash::Method::Method(const std::string& name) +: name_(name) +{ + MethodRegistrar::instance()->enter(this); +} + +const CryptoHash::Method * +CryptoHash::Method::method(CryptoHash::Algorithm algorithm) +{ + std::set method_set = MethodRegistrar::instance()->enumerate(); + std::set::const_iterator it; + + for (it = method_set.begin(); it != method_set.end(); ++it) { + const CryptoHash::Method *m = *it; + std::set algorithm_set = m->algorithms(); + if (algorithm_set.find(algorithm) == algorithm_set.end()) + continue; + + return (*it); + } + + ERROR("/crypto/encryption/method") << "Could not find a method for algorithm: " << algorithm; + + return (NULL); +} + +std::ostream& +operator<< (std::ostream& os, CryptoHash::Algorithm algorithm) +{ + switch (algorithm) { + case CryptoHash::MD5: + return (os << "MD5"); + case CryptoHash::SHA1: + return (os << "SHA1"); + case CryptoHash::SHA256: + return (os << "SHA256"); + case CryptoHash::SHA512: + return (os << "SHA512"); + case CryptoHash::RIPEMD160: + return (os << "RIPEMD160"); + } + NOTREACHED("/crypto/encryption"); +} diff --git a/crypto/crypto_hash.h b/crypto/crypto_hash.h new file mode 100644 index 0000000..cadcb03 --- /dev/null +++ b/crypto/crypto_hash.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CRYPTO_CRYPTO_HASH_H +#define CRYPTO_CRYPTO_HASH_H + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_hash.h // +// Description: basic encryption machinery // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace CryptoHash { + class Method; + + enum Algorithm { + MD5, + SHA1, + SHA256, + SHA512, + RIPEMD160, + }; + + class Instance { + protected: + Instance(void) + { } + + public: + virtual ~Instance() + { } + + virtual bool hash(Buffer *, const Buffer *) = 0; + + //virtual Action *submit(Buffer *, EventCallback *) = 0; + }; + + class Method { + std::string name_; + protected: + Method(const std::string&); + + virtual ~Method() + { } + public: + virtual std::set algorithms(void) const = 0; + virtual Instance *instance(Algorithm) const = 0; + + static const Method *method(Algorithm); + }; + + static inline Instance *instance(Algorithm algorithm) + { + const Method *method = Method::method(algorithm); + if (method == NULL) + return (NULL); + return (method->instance(algorithm)); + } + + static inline bool hash(Algorithm algorithm, Buffer *out, const Buffer *in) + { + Instance *i = instance(algorithm); + if (i == NULL) + return (false); + bool ok = i->hash(out, in); + delete i; + return (ok); + } +} + +std::ostream& operator<< (std::ostream&, CryptoHash::Algorithm); + +#endif /* !CRYPTO_CRYPTO_HASH_H */ diff --git a/crypto/crypto_hash_openssl.cc b/crypto/crypto_hash_openssl.cc new file mode 100644 index 0000000..28eb3bf --- /dev/null +++ b/crypto/crypto_hash_openssl.cc @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_hash_openssl.cc // +// Description: interface to the OpenSSL encryption functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace { + class InstanceEVP : public CryptoHash::Instance { + LogHandle log_; + const EVP_MD *algorithm_; + public: + InstanceEVP(const EVP_MD *algorithm) + : log_("/crypto/hash/instance/openssl"), + algorithm_(algorithm) + { } + + ~InstanceEVP() + { } + + virtual bool hash(Buffer *out, const Buffer *in) + { + /* + * We process a single, large, linear byte buffer here rather + * than going a BufferSegment at a time, even though the byte + * buffer is less efficient than some alternatives, because + * there are padding and buffering implications if each + * BufferSegment's length is not modular to the block size. + */ + uint8_t indata[in->length()]; + in->copyout(indata, sizeof indata); + + uint8_t macdata[EVP_MD_size(algorithm_)]; + unsigned maclen; + if (!EVP_Digest(indata, sizeof indata, macdata, &maclen, algorithm_, NULL)) + return (false); + ASSERT(log_, maclen == sizeof macdata); + out->append(macdata, maclen); + return (true); + } + + /* + Action *submit(Buffer *in, EventCallback *cb) + { + Buffer out; + if (!hash(&out, in)) { + in->clear(); + cb->param(Event::Error); + return (cb->schedule()); + } + in->clear(); + cb->param(Event(Event::Done, out)); + return (cb->schedule()); + } + */ + }; + + class MethodOpenSSL : public CryptoHash::Method { + LogHandle log_; + FactoryMap algorithm_map_; + public: + MethodOpenSSL(void) + : CryptoHash::Method("OpenSSL"), + log_("/crypto/hash/openssl"), + algorithm_map_() + { + OpenSSL_add_all_algorithms(); + + factory evp_factory; + algorithm_map_.enter(CryptoHash::MD5, evp_factory(EVP_md5())); + algorithm_map_.enter(CryptoHash::SHA1, evp_factory(EVP_sha1())); + algorithm_map_.enter(CryptoHash::SHA256, evp_factory(EVP_sha256())); + algorithm_map_.enter(CryptoHash::SHA512, evp_factory(EVP_sha512())); + algorithm_map_.enter(CryptoHash::RIPEMD160, evp_factory(EVP_ripemd160())); + + /* XXX Register. */ + } + + ~MethodOpenSSL() + { + /* XXX Unregister. */ + } + + std::set algorithms(void) const + { + return (algorithm_map_.keys()); + } + + CryptoHash::Instance *instance(CryptoHash::Algorithm algorithm) const + { + return (algorithm_map_.create(algorithm)); + } + }; + + static MethodOpenSSL crypto_hash_method_openssl; +} diff --git a/crypto/crypto_mac.cc b/crypto/crypto_mac.cc new file mode 100644 index 0000000..be9ad1d --- /dev/null +++ b/crypto/crypto_mac.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include + +namespace { + struct MethodRegistrarKey; + typedef Registrar MethodRegistrar; +} + +CryptoMAC::Method::Method(const std::string& name) +: name_(name) +{ + MethodRegistrar::instance()->enter(this); +} + +const CryptoMAC::Method * +CryptoMAC::Method::method(CryptoMAC::Algorithm algorithm) +{ + std::set method_set = MethodRegistrar::instance()->enumerate(); + std::set::const_iterator it; + + for (it = method_set.begin(); it != method_set.end(); ++it) { + const CryptoMAC::Method *m = *it; + std::set algorithm_set = m->algorithms(); + if (algorithm_set.find(algorithm) == algorithm_set.end()) + continue; + + return (*it); + } + + ERROR("/crypto/encryption/method") << "Could not find a method for algorithm: " << algorithm; + + return (NULL); +} + +std::ostream& +operator<< (std::ostream& os, CryptoMAC::Algorithm algorithm) +{ + switch (algorithm) { + case CryptoMAC::MD5: + return (os << "HMAC-MD5"); + case CryptoMAC::SHA1: + return (os << "HMAC-SHA1"); + case CryptoMAC::SHA256: + return (os << "HMAC-SHA256"); + case CryptoMAC::SHA512: + return (os << "HMAC-SHA512"); + case CryptoMAC::RIPEMD160: + return (os << "HMAC-RIPEMD160"); + } + NOTREACHED("/crypto/encryption"); +} diff --git a/crypto/crypto_mac.h b/crypto/crypto_mac.h new file mode 100644 index 0000000..a525909 --- /dev/null +++ b/crypto/crypto_mac.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CRYPTO_CRYPTO_MAC_H +#define CRYPTO_CRYPTO_MAC_H + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_mac.h // +// Description: basic encryption machinery // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace CryptoMAC { + class Method; + + enum Algorithm { + MD5, + SHA1, + SHA256, + SHA512, + RIPEMD160, + }; + + class Instance { + protected: + Instance(void) + { } + + public: + virtual ~Instance() + { } + + virtual unsigned size(void) const = 0; + + virtual Instance *clone(void) const = 0; + + virtual bool initialize(const Buffer * = NULL) = 0; + + virtual bool mac(Buffer *, const Buffer *) = 0; + + //virtual Action *submit(Buffer *, EventCallback *) = 0; + }; + + class Method { + std::string name_; + protected: + Method(const std::string&); + + virtual ~Method() + { } + public: + virtual std::set algorithms(void) const = 0; + virtual Instance *instance(Algorithm) const = 0; + + static const Method *method(Algorithm); + }; +} + +std::ostream& operator<< (std::ostream&, CryptoMAC::Algorithm); + +#endif /* !CRYPTO_CRYPTO_MAC_H */ diff --git a/crypto/crypto_mac_openssl.cc b/crypto/crypto_mac_openssl.cc new file mode 100644 index 0000000..4604e46 --- /dev/null +++ b/crypto/crypto_mac_openssl.cc @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_mac_openssl.cc // +// Description: interface to the OpenSSL encryption functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace { + class InstanceEVP : public CryptoMAC::Instance { + LogHandle log_; + const EVP_MD *algorithm_; + uint8_t key_[EVP_MAX_KEY_LENGTH]; + size_t key_length_; + public: + InstanceEVP(const EVP_MD *algorithm) + : log_("/crypto/mac/instance/openssl"), + algorithm_(algorithm), + key_(), + key_length_(0) + { } + + ~InstanceEVP() + { } + + unsigned size(void) const + { + return (EVP_MD_size(algorithm_)); + } + + Instance *clone(void) const + { + ASSERT(log_, key_length_ == 0); + return (new InstanceEVP(algorithm_)); + } + + bool initialize(const Buffer *key) + { + if (key->length() > EVP_MAX_KEY_LENGTH) + return (false); + + key->copyout(key_, key->length()); + key_length_ = key->length(); + + return (true); + } + + bool mac(Buffer *out, const Buffer *in) + { + /* + * We process a single, large, linear byte buffer here rather + * than going a BufferSegment at a time, even though the byte + * buffer is less efficient than some alternatives, because + * there are padding and buffering implications if each + * BufferSegment's length is not modular to the block size. + */ + uint8_t indata[in->length()]; + in->copyout(indata, sizeof indata); + + uint8_t macdata[EVP_MD_size(algorithm_)]; + unsigned maclen; + if (HMAC(algorithm_, key_, key_length_, indata, sizeof indata, macdata, &maclen) == NULL) + return (false); + ASSERT(log_, maclen == sizeof macdata); + out->append(macdata, maclen); + return (true); + } + + /* + Action *submit(Buffer *in, EventCallback *cb) + { + Buffer out; + if (!mac(&out, in)) { + in->clear(); + cb->param(Event::Error); + return (cb->schedule()); + } + in->clear(); + cb->param(Event(Event::Done, out)); + return (cb->schedule()); + } + */ + }; + + class MethodOpenSSL : public CryptoMAC::Method { + LogHandle log_; + FactoryMap algorithm_map_; + public: + MethodOpenSSL(void) + : CryptoMAC::Method("OpenSSL"), + log_("/crypto/mac/openssl"), + algorithm_map_() + { + OpenSSL_add_all_algorithms(); + + factory evp_factory; + algorithm_map_.enter(CryptoMAC::MD5, evp_factory(EVP_md5())); + algorithm_map_.enter(CryptoMAC::SHA1, evp_factory(EVP_sha1())); + algorithm_map_.enter(CryptoMAC::SHA256, evp_factory(EVP_sha256())); + algorithm_map_.enter(CryptoMAC::SHA512, evp_factory(EVP_sha512())); + algorithm_map_.enter(CryptoMAC::RIPEMD160, evp_factory(EVP_ripemd160())); + + /* XXX Register. */ + } + + ~MethodOpenSSL() + { + /* XXX Unregister. */ + } + + std::set algorithms(void) const + { + return (algorithm_map_.keys()); + } + + CryptoMAC::Instance *instance(CryptoMAC::Algorithm algorithm) const + { + return (algorithm_map_.create(algorithm)); + } + }; + + static MethodOpenSSL crypto_mac_method_openssl; +} diff --git a/crypto/crypto_random.h b/crypto/crypto_random.h new file mode 100644 index 0000000..f7463b6 --- /dev/null +++ b/crypto/crypto_random.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef CRYPTO_CRYPTO_RANDOM_H +#define CRYPTO_CRYPTO_RANDOM_H + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_random.h // +// Description: basic encryption machinery // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class CryptoRandomMethod; + +/* + * XXX + * Add a CryptoTypeBest, which will use whatever the best, available method is. + */ +enum CryptoRandomType { + CryptoTypeRNG, + CryptoTypePRNG, +}; + +class CryptoRandomSession { +protected: + CryptoRandomSession(void) + { } + +public: + virtual ~CryptoRandomSession() + { } + + //virtual Action *generate(size_t, EventCallback *) = 0; +}; + +class CryptoRandomMethod { +protected: + CryptoRandomMethod(void) + { } + + virtual ~CryptoRandomMethod() + { } +public: + virtual bool generate(CryptoRandomType, size_t, Buffer *) const = 0; + virtual CryptoRandomSession *session(CryptoRandomType) const = 0; + + /* XXX Registration API somehow. */ + static const CryptoRandomMethod *default_method; +}; + +#endif /* !CRYPTO_CRYPTO_RANDOM_H */ diff --git a/crypto/crypto_random_openssl.cc b/crypto/crypto_random_openssl.cc new file mode 100644 index 0000000..4cc3567 --- /dev/null +++ b/crypto/crypto_random_openssl.cc @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: crypto_random_openssl.cc // +// Description: interface to the OpenSSL encryption functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace { + typedef int (RAND_func)(unsigned char *, int); +} + +class CryptoRandomSessionRAND : public CryptoRandomSession { + LogHandle log_; + RAND_func *func_; +public: + CryptoRandomSessionRAND(RAND_func *func) + : log_("/crypto/random/session/openssl"), + func_(func) + { } + + ~CryptoRandomSessionRAND() + { } + + /* + Action *generate(size_t len, EventCallback *cb) + { + ASSERT(log_, len != 0); + + uint8_t bytes[len]; + int rv = func_(bytes, sizeof bytes); + if (rv == 0) { + cb->param(Event::Error); + return (cb->schedule()); + } + cb->param(Event(Event::Done, Buffer(bytes, sizeof bytes))); + return (cb->schedule()); + } + */ +}; + +class CryptoRandomMethodOpenSSL : public CryptoRandomMethod { + LogHandle log_; + std::map func_map_; +public: + CryptoRandomMethodOpenSSL(void) + : log_("/crypto/random/openssl"), + func_map_() + { + OpenSSL_add_all_algorithms(); + + func_map_[CryptoTypeRNG] = RAND_bytes; + func_map_[CryptoTypePRNG] = RAND_pseudo_bytes; + + /* XXX Register. */ + } + + ~CryptoRandomMethodOpenSSL() + { + /* XXX Unregister. */ + } + + /* + * Synchronous randomness generation. May not succeed. + */ + bool generate(CryptoRandomType func, size_t len, Buffer *out) const + { + std::map::const_iterator it; + + it = func_map_.find(func); + if (it == func_map_.end()) + return (false); + + uint8_t bytes[len]; + int rv = it->second(bytes, sizeof bytes); + if (rv == 0) + return (false); + + out->append(bytes, sizeof bytes); + return (true); + } + + CryptoRandomSession *session(CryptoRandomType func) const + { + std::map::const_iterator it; + + it = func_map_.find(func); + if (it != func_map_.end()) + return (new CryptoRandomSessionRAND(it->second)); + return (NULL); + } +}; + +namespace { + static CryptoRandomMethodOpenSSL crypto_random_method_openssl; +} +const CryptoRandomMethod *CryptoRandomMethod::default_method = &crypto_random_method_openssl; /* XXX */ diff --git a/crypto/lib.mk b/crypto/lib.mk new file mode 100644 index 0000000..b0ed682 --- /dev/null +++ b/crypto/lib.mk @@ -0,0 +1,17 @@ +VPATH+= ${TOPDIR}/crypto + +SRCS+= crypto_encryption.cc +SRCS+= crypto_hash.cc +SRCS+= crypto_mac.cc + +SRCS+= crypto_encryption_openssl.cc +SRCS+= crypto_hash_openssl.cc +SRCS+= crypto_mac_openssl.cc +SRCS+= crypto_random_openssl.cc + +# Apple has decided to deprecate all of OpenSSL in Mac OS X ~Lion~. Brilliant. +ifeq "${OSNAME}" "Darwin" +CFLAGS+= -Wno-deprecated-declarations +endif + +LDADD+= -lcrypto diff --git a/event/Makefile b/event/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/event/action.h b/event/action.h new file mode 100644 index 0000000..b6236b9 --- /dev/null +++ b/event/action.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_ACTION_H +#define EVENT_ACTION_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: action.h // +// Description: basic classes for event callback management // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Action +{ +protected: + bool cancelled_; + + Action () : cancelled_(false) + { + } + +public: + virtual ~Action () + { + ASSERT("/action", cancelled_); + } + + virtual void cancel () = 0; + + bool is_cancelled () + { + return cancelled_; + } +}; + + +template class CallbackAction : public Action +{ + typedef void (S::*const method_t)(void); + +public: + S* const obj_; + method_t method_; + C* callback_; + +public: + CallbackAction (S* obj, method_t method, C* cb) : obj_(obj), method_(method) + { + ASSERT("/action", obj && method); + callback_ = cb; + } + + ~CallbackAction() + { + delete callback_; + } + + virtual void cancel () + { + cancelled_ = true; + (obj_->*method_) (); + } +}; + +#endif /* !EVENT_ACTION_H */ diff --git a/event/callback.h b/event/callback.h new file mode 100644 index 0000000..12c0634 --- /dev/null +++ b/event/callback.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_CALLBACK_H +#define EVENT_CALLBACK_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: callback.h // +// Description: base class for event callback specializations // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Callback +{ +public: + virtual ~Callback () {} + virtual void execute () = 0; +}; + +#endif /* !EVENT_CALLBACK_H */ diff --git a/event/callback_queue.h b/event/callback_queue.h new file mode 100644 index 0000000..c733dda --- /dev/null +++ b/event/callback_queue.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_CALLBACK_QUEUE_H +#define EVENT_CALLBACK_QUEUE_H + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: callback_queue.h // +// Description: collection classes for event callback management // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class CallbackQueue +{ + class QueuedAction : public Action + { + CallbackQueue& queue_; + uint64_t generation_; + Callback* callback_; + + public: + QueuedAction (CallbackQueue& q, uint64_t g, Callback* cb) + : queue_(q), + generation_(g), + callback_(cb) + { + } + + ~QueuedAction () + { + delete callback_; + } + + virtual void cancel () + { + cancelled_ = true; + queue_.cancel (this); + } + + friend class CallbackQueue; + }; + + std::deque queue_; + uint64_t generation_; + +public: + CallbackQueue () + : queue_(), + generation_(0) + { } + + ~CallbackQueue () + { + std::deque::iterator it; + for (it = queue_.begin (); it != queue_.end (); ++it) + { + delete *it; + *it = 0; + } + } + + Action* schedule (Callback* cb) + { + QueuedAction* a = new QueuedAction (*this, generation_, cb); + queue_.push_back (a); + return (a); + } + + /* + * Runs all callbacks that have already been queued, but none that + * are added by callbacks that are called as part of the drain + * operation. Returns true if there are queued callbacks that were + * added during drain. + */ + bool drain (void) + { + generation_++; + while (! queue_.empty ()) + { + QueuedAction* a = queue_.front (); + if (a->generation_ >= generation_) + return (true); + queue_.pop_front (); + if (a->callback_) + a->callback_->execute (); + } + return (false); + } + + bool empty () const + { + return (queue_.empty ()); + } + + void perform () + { + if (! queue_.empty ()) + { + QueuedAction* a = queue_.front (); + if (a->callback_) + a->callback_->execute (); + } + } + + void cancel (QueuedAction* a) + { + std::deque::iterator it; + for (it = queue_.begin (); it != queue_.end (); ++it) + { + if (*it == a) + { + queue_.erase (it); + break; + } + } + delete a; + } +}; + +#endif /* !EVENT_CALLBACK_QUEUE_H */ diff --git a/event/event.h b/event/event.h new file mode 100644 index 0000000..67c6664 --- /dev/null +++ b/event/event.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_EVENT_H +#define EVENT_EVENT_H + +#include + +/* + * The general-purpose event type. Never extended or anything like that. + * Tracking a user-specified pointer is handled by the callback, providers of + * Events can pass extra data by extending their Callback type, e.g. the + * SocketEventCallback used by Socket::accept() to pass back a Socket pointer + * along with an Event. + * XXX In light of this extension, it may make sense to move the Buffer out + * of Event now, since the Callback can handle Buffers independently. + * + * Because we are primarily a data-movement/processing system, a Buffer is an + * integral part of every Event. Plus, Buffers with no data are basically + * free to copy, etc. + * + * Event handlers/callbacks always take a copy of the Event, which is subpar + * but necessary since the first thing most of those callbacks do is to cancel + * the Action that called them, which in turn deletes the underlying SimpleCallback + * object, which would in turn delete the holder of the associated Event if a + * reference or pointer were passed. One can argue that the right thing to do + * is process the event fully before cancelling the Action, but that is not + * how things are done at present. + */ +struct Event { + enum Type { + Invalid, + Done, + EOS, + Error, + }; + + Type type_; + int error_; + Buffer buffer_; + + Event(void) + : type_(Event::Invalid), + error_(0), + buffer_() + { } + + Event(Type type) + : type_(type), + error_(0), + buffer_() + { } + + Event(Type type, int error) + : type_(type), + error_(error), + buffer_() + { } + + + Event(Type type, const Buffer& buffer) + : type_(type), + error_(0), + buffer_(buffer) + { } + + Event(Type type, int error, const Buffer& buffer) + : type_(type), + error_(error), + buffer_(buffer) + { } + + Event(const Event& e) + : type_(e.type_), + error_(e.error_), + buffer_(e.buffer_) + { } + + Event& operator= (const Event& e) + { + type_ = e.type_; + error_ = e.error_; + buffer_ = e.buffer_; + return (*this); + } +}; + +static inline std::ostream& +operator<< (std::ostream& os, Event::Type type) +{ + switch (type) { + case Event::Invalid: + return (os << ""); + case Event::Done: + return (os << ""); + case Event::EOS: + return (os << ""); + case Event::Error: + return (os << ""); + default: + return (os << ""); + } +} + +static inline std::ostream& +operator<< (std::ostream& os, Event e) +{ + if (e.type_ != Event::Error && e.error_ == 0) + return (os << e.type_); + return (os << e.type_ << '/' << e.error_ << " [" << + strerror(e.error_) << "]"); +} + +#endif /* !EVENT_EVENT_H */ diff --git a/event/event_callback.h b/event/event_callback.h new file mode 100644 index 0000000..9873e9c --- /dev/null +++ b/event/event_callback.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_EVENT_CALLBACK_H +#define EVENT_EVENT_CALLBACK_H + +#include +#include + +typedef class TypedCallback EventCallback; + +#endif /* !EVENT_EVENT_CALLBACK_H */ diff --git a/event/event_message.h b/event/event_message.h new file mode 100644 index 0000000..a1deefe --- /dev/null +++ b/event/event_message.h @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_message.h // +// Description: sructures for data exchange in shared buffers // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef EVENT_EVENT_MESSAGE_H +#define EVENT_EVENT_MESSAGE_H + +class EventAction; + +struct EventMessage +{ + int op; + EventAction* action; +}; + +#endif /* !EVENT_EVENT_MESSAGE_H */ diff --git a/event/event_poll_epoll.cc b/event/event_poll_epoll.cc new file mode 100644 index 0000000..e998a09 --- /dev/null +++ b/event/event_poll_epoll.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_poll_epoll.cc // +// Description: IO event handling using Linux epoll functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +void IoService::open_resources () +{ + handle_ = ::epoll_create (IO_POLL_EVENT_COUNT); + ASSERT(log_, handle_ != -1); +} + +void IoService::close_resources () +{ + if (handle_ >= 0) + ::close (handle_); +} + +void IoService::set_fd (int fd, int rd, int wr, IoNode* node) +{ + struct epoll_event eev; + int rv; + eev.events = ((rd > 0 ? EPOLLIN : 0) | (wr > 0 ? EPOLLOUT : 0)); + eev.data.ptr = node; + if (eev.events) + rv = ::epoll_ctl (handle_, (rd && wr ? EPOLL_CTL_MOD : EPOLL_CTL_ADD), fd, &eev); + else + rv = ::epoll_ctl (handle_, EPOLL_CTL_DEL, fd, &eev); + if (rv < 0 && errno != EEXIST && errno != ENOENT) + CRITICAL(log_) << "Could not add event to epoll."; +} + +void IoService::poll (int ms) +{ + struct epoll_event eev[IO_POLL_EVENT_COUNT]; + int evcnt; + + evcnt = ::epoll_wait (handle_, eev, IO_POLL_EVENT_COUNT, ms); + if (evcnt < 0 && errno != EINTR) + CRITICAL(log_) << "Could not poll epoll."; + + for (int i = 0; i < evcnt; ++i) + { + int flg = eev[i].events; + IoNode* node = (IoNode*) eev[i].data.ptr; + EventAction* act; + Event ok; + + if ((flg & EPOLLIN)) + { + if (node && (act = node->read_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeAccept && (ev.type_ = Event::Done)) || read_channel (node->fd, ev, 1)) + { + schedule (act); + node->reading = false; + set_fd (node->fd, -1, (node->writing ? 2 : 0), node); + } + } + else if (node) + read_channel (node->fd, ok, 0); + } + + if ((flg & EPOLLOUT)) + { + if (node && (act = node->write_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeConnect && (ev.type_ = Event::Done)) || + (act->mode_ == StreamModeWrite && write_channel (node->fd, ev)) || + (act->mode_ == StreamModeEnd && close_channel (node->fd, ev))) + { + schedule (act); + node->writing = false; + set_fd (node->fd, (node->reading ? 2 : 0), -1, node); + } + } + } + + if (! (flg & (EPOLLIN | EPOLLOUT))) + { + if ((flg & EPOLLERR)) + { + if (node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->reading = false; + set_fd (node->fd, -1, 0, node); + } + if (node && (act = node->write_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->writing = false; + set_fd (node->fd, 0, -1, node); + } + } + else if ((flg & EPOLLHUP)) + { + if (node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::EOS; + schedule (act); + node->reading = false; + set_fd (node->fd, -1, (node->writing ? 2 : 0), node); + } + } + } + } +} diff --git a/event/event_poll_kqueue.cc b/event/event_poll_kqueue.cc new file mode 100644 index 0000000..1c32c34 --- /dev/null +++ b/event/event_poll_kqueue.cc @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_poll_kqueue.cc // +// Description: IO event handling using FreeBSD kevent functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +void IoService::open_resources () +{ + handle_ = kqueue (); + ASSERT(log_, handle_ != -1); +} + +void IoService::close_resources () +{ + if (handle_ >= 0) + ::close (handle_); +} + +void IoService::set_fd (int fd, int rd, int wr, IoNode* node) +{ + struct kevent kev; + int rv; + if (rd == 1) + EV_SET (&kev, fd, EVFILT_READ, EV_ADD, 0, 0, node); + else if (wr == 1) + EV_SET (&kev, fd, EVFILT_WRITE, EV_ADD, 0, 0, node); + else if (rd == -1) + EV_SET (&kev, fd, EVFILT_READ, EV_DELETE, 0, 0, node); + else if (wr == -1) + EV_SET (&kev, fd, EVFILT_WRITE, EV_DELETE, 0, 0, node); + rv = ::kevent (handle_, &kev, 1, 0, 0, 0); + if (rv < 0 && errno != EEXIST && errno != ENOENT) + CRITICAL(log_) << "Could not add event to kqueue."; +} + +void IoService::poll (int ms) +{ + struct kevent kev[IO_POLL_EVENT_COUNT]; + struct timespec ts; + int evcnt; + + if (ms != -1) + { + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + } + + evcnt = ::kevent (handle_, 0, 0, kev, IO_POLL_EVENT_COUNT, (ms == -1 ? 0 : &ts)); + if (evcnt < 0 && errno != EINTR) + CRITICAL(log_) << "Could not poll kqueue."; + + for (int i = 0; i < evcnt; i++) + { + int sck = kev[i].ident; + int flt = kev[i].filter; + int flg = kev[i].flags; + IoNode* node = (IoNode*) kev[i].udata; + EventAction* act; + Event ok; + + if ((flg & EV_ERROR)) + { + if (flt == EVFILT_READ && node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->reading = false; + set_fd (sck, -1, (node->writing ? 2 : 0), node); + } + else if (flt == EVFILT_WRITE && node && (act = node->write_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->writing = false; + set_fd (sck, (node->reading ? 2 : 0), -1, node); + } + } + else if ((flg & EV_EOF)) + { + if (flt == EVFILT_READ && node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::EOS; + schedule (act); + node->reading = false; + set_fd (sck, -1, (node->writing ? 2 : 0), node); + } + } + else if ((flt == EVFILT_READ)) + { + if (node && (act = node->read_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeAccept && (ev.type_ = Event::Done)) || read_channel (sck, ev, 1)) + { + schedule (act); + node->reading = false; + set_fd (sck, -1, (node->writing ? 2 : 0), node); + } + } + else if (node) + read_channel (sck, ok, 0); + } + else if ((flt == EVFILT_WRITE)) + { + if (node && (act = node->write_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeConnect && (ev.type_ = Event::Done)) || + (act->mode_ == StreamModeWrite && write_channel (sck, ev)) || + (act->mode_ == StreamModeEnd && close_channel (sck, ev))) + { + schedule (act); + node->writing = false; + set_fd (sck, (node->reading ? 2 : 0), -1, node); + } + } + } + } +} diff --git a/event/event_poll_poll.cc b/event/event_poll_poll.cc new file mode 100644 index 0000000..e14a664 --- /dev/null +++ b/event/event_poll_poll.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_poll_poll.cc // +// Description: IO event handling using standard poll functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +void IoService::open_resources () +{ +} + +void IoService::close_resources () +{ +} + +void IoService::set_fd (int fd, int rd, int wr, IoNode* node) +{ +} + +void IoService::poll (int ms) +{ + struct pollfd fds[fd_map_.size () + 1]; + int i, j, f; + int cnt; + + fds[0].fd = rfd_; + fds[0].events = POLLIN; + fds[0].revents = 0; + j = 1; + + std::map::iterator it; + for (it = fd_map_.begin (); it != fd_map_.end (); ++it) + { + if ((f = (it->second.reading ? POLLIN : 0) | (it->second.writing ? POLLOUT : 0))) + { + fds[j].fd = it->first; + fds[j].events = f; + fds[j].revents = 0; + ++j; + } + } + + cnt = ::poll (fds, j, ms); + if (cnt < 0 && errno != EINTR) + CRITICAL(log_) << "Could not poll."; + + for (i = 0; i < j; ++i) + { + if (fds[i].revents == 0) + continue; + if (cnt-- == 0) + break; + + int sck = fds[i].fd; + int flg = fds[i].revents; + it = fd_map_.find (sck); + IoNode* node = (it != fd_map_.end () ? &it->second : 0); + EventAction* act; + Event ok; + + if ((flg & POLLIN)) + { + if (node && (act = node->read_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeAccept && (ev.type_ = Event::Done)) || read_channel (sck, ev, 1)) + { + schedule (act); + node->reading = false; + } + } + else if (node) + read_channel (sck, ok, 0); + } + + if ((flg & POLLOUT)) + { + if (node && (act = node->write_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeConnect && (ev.type_ = Event::Done)) || + (act->mode_ == StreamModeWrite && write_channel (sck, ev)) || + (act->mode_ == StreamModeEnd && close_channel (sck, ev))) + { + schedule (act); + node->writing = false; + } + } + } + + if (! (flg & (POLLIN | POLLOUT))) + { + if ((flg & (POLLERR | POLLNVAL))) + { + if (node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->reading = false; + } + if (node && (act = node->write_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->writing = false; + } + } + else if ((flg & POLLHUP)) + { + if (node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::EOS; + schedule (act); + node->reading = false; + } + } + } + } +} diff --git a/event/event_poll_port.cc b/event/event_poll_port.cc new file mode 100644 index 0000000..7af1825 --- /dev/null +++ b/event/event_poll_port.cc @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_poll_port.cc // +// Description: IO event handling using SunOS port functions // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +void IoService::open_resources () +{ + handle_ = port_create (); + ASSERT(log_, handle_ != -1); +} + +void IoService::close_resources () +{ + if (handle_ >= 0) + ::close (handle_); +} + +void IoService::set_fd (int fd, int rd, int wr, IoNode* node) +{ + int events; + int rv; + events = ((rd > 0 ? EPOLLIN : 0) | (wr > 0 ? EPOLLOUT : 0)); + if (events) + { + rv = ::port_associate (handle_, PORT_SOURCE_FD, fd, events, node); + if (rv < 0 && errno != EEXIST) + CRITICAL(log_) << "Could not add event to port."; + } + else + { + rv = ::port_dissociate (handle_, PORT_SOURCE_FD, fd); + if (rv < 0 && errno != ENOENT) + CRITICAL(log_) << "Could not add event to port."; + } +} + +void IoService::poll (int ms) +{ + port_event_t pev[IO_POLL_EVENT_COUNT]; + unsigned int evcnt = 1; + struct timespec ts; + int rv; + + if (ms != -1) + { + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + } + + rv = ::port_getn (handle_, pev, IO_POLL_EVENT_COUNT, &evcnt, (ms == -1 ? 0 : &ts)); + if (rv < 0) + { + if (errno != EINTR) + CRITICAL(log_) << "Could not poll port."; + evcnt = 0; + } + + for (unsigned int i = 0; i < evcnt; ++i) + { + int sck = pev[i].portev_object; + int flg = pev[i].portev_events; + IoNode* node = (IoNode*) pev[i].portev_user; + EventAction* act; + Event ok; + + if ((flg & POLLIN)) + { + if (node && (act = node->read_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeAccept && (ev.type_ = Event::Done)) || read_channel (sck, ev, 1)) + { + schedule (act); + node->reading = false; + } + else + set_fd (sck, 1, (node->writing ? 2 : 0), node); + } + else if (node) + { + read_channel (sck, ok, 0); + set_fd (sck, 1, (node->writing ? 2 : 0), node); + } + } + + if ((flg & POLLOUT)) + { + if (node && (act = node->write_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeConnect && (ev.type_ = Event::Done)) || + (act->mode_ == StreamModeWrite && write_channel (sck, ev)) || + (act->mode_ == StreamModeEnd && close_channel (sck, ev))) + { + schedule (act); + node->writing = false; + } + else + set_fd (sck, (node->reading ? 2 : 0), 1, node); + } + } + + if (! (flg & (POLLIN | POLLOUT))) + { + if ((flg & POLLERR)) + { + if (node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->reading = false; + } + if (node && (act = node->write_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::Error; + schedule (act); + node->writing = false; + } + } + else if ((flg & POLLHUP)) + { + if (node && (act = node->read_action)) + { + if (act->callback_) + act->callback_->param ().type_ = Event::EOS; + schedule (act); + node->reading = false; + } + } + } + } +} diff --git a/event/event_poll_select.cc b/event/event_poll_select.cc new file mode 100644 index 0000000..55052b1 --- /dev/null +++ b/event/event_poll_select.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_poll_select.cc // +// Description: IO event handling using traditional select calls // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +void IoService::open_resources () +{ +} + +void IoService::close_resources () +{ +} + +void IoService::set_fd (int fd, int rd, int wr, IoNode* node) +{ +} + +void IoService::poll (int ms) +{ + fd_set read_set, write_set; + struct timeval tv; + int maxfd; + int fdcnt; + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_SET(rfd_, &read_set); + maxfd = rfd_; + + std::map::iterator it; + for (it = fd_map_.begin (); it != fd_map_.end (); ++it) + { + if (it->second.reading) + FD_SET (it->first, &read_set); + if (it->second.writing) + FD_SET (it->first, &write_set); + if (maxfd < it->first) + maxfd = it->first; + } + + ASSERT(log_, maxfd != -1); + + if (ms != -1) + { + tv.tv_sec = ms / 1000; + tv.tv_usec = ms % 1000; + } + + fdcnt = ::select (maxfd + 1, &read_set, &write_set, 0, (ms == -1 ? 0 : &tv)); + if (fdcnt < 0 && errno != EINTR) + CRITICAL(log_) << "Could not poll select."; + + for (int sck = 0; sck <= maxfd && fdcnt > 0; ++sck) + { + if (FD_ISSET (sck, &read_set)) + { + it = fd_map_.find (sck); + IoNode* node = (it != fd_map_.end () ? &it->second : 0); + EventAction* act; + Event ok; + + if (node && (act = node->read_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeAccept && (ev.type_ = Event::Done)) || read_channel (sck, ev, 1)) + { + schedule (act); + node->reading = false; + } + } + else if (node) + read_channel (sck, ok, 0); + + ASSERT(log_, fdcnt > 0); + fdcnt--; + } + + if (FD_ISSET (sck, &write_set)) + { + it = fd_map_.find (sck); + IoNode* node = (it != fd_map_.end () ? &it->second : 0); + EventAction* act; + Event ok; + + if (node && (act = node->write_action)) + { + Event& ev = (act->callback_ ? act->callback_->param () : ok); + if ((act->mode_ == StreamModeConnect && (ev.type_ = Event::Done)) || + (act->mode_ == StreamModeWrite && write_channel (sck, ev)) || + (act->mode_ == StreamModeEnd && close_channel (sck, ev))) + { + schedule (act); + node->writing = false; + } + } + + ASSERT(log_, fdcnt > 0); + fdcnt--; + } + } +} diff --git a/event/event_system.cc b/event/event_system.cc new file mode 100644 index 0000000..78c53af --- /dev/null +++ b/event/event_system.cc @@ -0,0 +1,133 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_system.cc // +// Description: global event handling core class implementation // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +namespace +{ + static void signal_reload (int) { event_system.reload (); } + static void signal_stop (int) { event_system.stop (); } +} + +EventSystem::EventSystem () : log_ ("/event/system"), reload_ (false), stop_ (false) +{ + ::signal (SIGHUP, signal_reload); + ::signal (SIGINT, signal_stop); + ::signal (SIGPIPE, SIG_IGN); + + struct rlimit rlim; + int rv = ::getrlimit (RLIMIT_NOFILE, &rlim); + if (rv == 0 && rlim.rlim_cur < rlim.rlim_max) + { + rlim.rlim_cur = rlim.rlim_max; + rv = ::setrlimit (RLIMIT_NOFILE, &rlim); + } +} + +void EventSystem::run () +{ + EventMessage msg; + + INFO(log_) << "Starting event system."; + + io_service_.start (); + + while (1) + { + if (gateway_.read (msg)) + { + if (msg.op >= 0) + { + if (msg.action && ! msg.action->is_cancelled ()) + { + if (msg.action->callback_) + msg.action->callback_->execute (); + else + msg.action->cancel (); + } + } + else + { + delete msg.action; + } + } + + if (reload_) + { + if (! interest_queue_[EventInterestReload].empty ()) + { + INFO(log_) << "Running reload handlers."; + interest_queue_[EventInterestReload].drain (); + INFO(log_) << "Reload handlers have been run."; + } + reload_ = false; + ::signal (SIGHUP, signal_reload); + } + + if (stop_) + { + if (! interest_queue_[EventInterestStop].empty ()) + { + INFO(log_) << "Running stop handlers."; + interest_queue_[EventInterestStop].drain (); + INFO(log_) << "Stop handlers have been run."; + } + break; + } + } + + io_service_.stop (); +} + +void EventSystem::reload () +{ + ::signal (SIGHUP, SIG_IGN); + reload_ = true; + gateway_.wakeup (); +} + +void EventSystem::stop () +{ + ::signal (SIGINT, SIG_IGN); + stop_ = true; + gateway_.wakeup (); +} + +Action* EventSystem::register_interest (EventInterest interest, Callback* cb) +{ + return interest_queue_[interest].schedule (cb); +} + +Action* EventSystem::track (int fd, StreamMode mode, EventCallback* cb) +{ + EventAction* act; + + if ((act = new EventAction (*this, fd, mode, cb))) + { + EventMessage msg = {1, act}; + io_service_.take_message (msg); + } + + return (cb ? act : 0); +} + +void EventSystem::cancel (EventAction* act) +{ + if (act) + { + EventMessage msg = {-1, act}; + io_service_.take_message (msg); + } +} + +EventSystem event_system; diff --git a/event/event_system.h b/event/event_system.h new file mode 100644 index 0000000..eb7cddd --- /dev/null +++ b/event/event_system.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: event_system.h // +// Description: global event handling core class definition // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef EVENT_EVENT_SYSTEM_H +#define EVENT_EVENT_SYSTEM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class EventAction; + +enum EventInterest +{ + EventInterestReload, + EventInterestStop, + EventInterests +}; + +enum StreamMode +{ + StreamModeConnect, + StreamModeAccept, + StreamModeRead, + StreamModeWrite, + StreamModeEnd +}; + +class EventSystem +{ +private: + LogHandle log_; + IoService io_service_; + WaitBuffer gateway_; + CallbackQueue interest_queue_[EventInterests]; + bool reload_, stop_; + +public: + EventSystem (); + + void run (); + void reload (); + void stop (); + + Action* register_interest (EventInterest interest, Callback* cb); + Action* track (int fd, StreamMode mode, EventCallback* cb); + void cancel (EventAction* act); + + int take_message (const EventMessage& msg) { return gateway_.write (msg); } +}; + +class EventAction : public Action +{ +public: + EventSystem& system_; + int fd_; + StreamMode mode_; + EventCallback* callback_; + +public: + EventAction (EventSystem& sys, int fd, StreamMode mode, EventCallback* cb) : system_ (sys) + { + fd_ = fd; mode_ = mode; callback_ = cb; + } + + ~EventAction () + { + delete callback_; + } + + virtual void cancel () + { + cancelled_ = true; + system_.cancel (this); + } +}; + +extern EventSystem event_system; + +#endif /* !EVENT_EVENT_SYSTEM_H */ diff --git a/event/io_service.cc b/event/io_service.cc new file mode 100644 index 0000000..cbe1032 --- /dev/null +++ b/event/io_service.cc @@ -0,0 +1,329 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: io_service.h // +// Description: servicing of network IO requests for the event system // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +IoService::IoService () : Thread ("IoService"), log_ ("/io/thread") +{ + handle_ = rfd_ = wfd_ = -1; + + int fd[2]; + if (::pipe (fd) == 0) + rfd_ = fd[0], wfd_ = fd[1]; +} + +IoService::~IoService () +{ + if (rfd_ >= 0) + ::close (rfd_); + if (wfd_ >= 0) + ::close (wfd_); +} + +void IoService::main () +{ + EventMessage msg; + + INFO(log_) << "Starting IO thread."; + + open_resources (); + IoNode node = {rfd_, true, false, 0, 0}; + set_fd (rfd_, 1, 0, &node); + + while (! stop_) + { + while (gateway_.read (msg)) + { + if (msg.op >= 0) + handle_request (msg.action); + else + cancel (msg.action); + } + + poll (-1); + } + + set_fd (rfd_, -1, 0); + close_resources (); +} + +void IoService::stop () +{ + stop_ = true; + wakeup (); + Thread::stop (); +} + +void IoService::handle_request (EventAction* act) +{ + Event ev; + + if (act) + { + switch (act->mode_) + { + case StreamModeConnect: + if (connect_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev))) + schedule (act); + else + track (act); + return; + + case StreamModeAccept: + track (act); + return; + + case StreamModeRead: + if (read_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev), 1)) + schedule (act); + else + track (act); + return; + + case StreamModeWrite: + if (write_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev))) + schedule (act); + else + track (act); + return; + + case StreamModeEnd: + if (close_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev))) + schedule (act); + else + track (act); + return; + } + } +} + +bool IoService::connect_channel (int fd, Event& ev) +{ + struct sockaddr adr; + int n = 0; + + if (ev.buffer_.length () <= sizeof adr) + ev.buffer_.copyout ((uint8_t*) &adr, (n = ev.buffer_.length ())); + + int rv = ::connect (fd, &adr, n); + switch (rv) + { + case 0: + ev.type_ = Event::Done; + break; + case -1: + switch (errno) + { + case EINPROGRESS: + return false; + default: + ev.type_ = Event::Error; + ev.error_ = errno; + break; + } + break; + } + + return true; +} + +bool IoService::read_channel (int fd, Event& ev, int flg) +{ + ssize_t len; + + len = ::read (fd, read_pool_, sizeof read_pool_); + if (len < 0) + { + switch (errno) + { + case EAGAIN: + return false; + default: + ev.type_ = Event::Error; + ev.error_ = errno; + break; + } + } + else if (len == 0) + { + ev.type_ = Event::EOS; + } + else if (flg & 1) + { + ev.type_ = Event::Done; + ev.buffer_.append (read_pool_, len); + } + + return true; +} + +bool IoService::write_channel (int fd, Event& ev) +{ + struct iovec iov[IOV_MAX]; + size_t iovcnt; + ssize_t len; + + if (ev.buffer_.empty ()) + { + ev.type_ = Event::Done; + } + else + { + iovcnt = ev.buffer_.fill_iovec (iov, IOV_MAX); + len = ::writev (fd, iov, iovcnt); + if (len < 0) + { + switch (errno) + { + case EAGAIN: + return false; + default: + ev.type_ = Event::Error; + ev.error_ = errno; + break; + } + } + else if ((size_t) len < ev.buffer_.length ()) + { + ev.buffer_.skip (len); + return false; + } + else + { + ev.type_ = Event::Done; + } + } + + return true; +} + +bool IoService::close_channel (int fd, Event& ev) +{ + int rv = ::close (fd); + if (rv == -1 && errno == EAGAIN) + return false; + + ev.type_ = Event::Done; + + return true; +} + +void IoService::track (EventAction* act) +{ + std::map::iterator it; + int fd; + + if (act) + { + fd = act->fd_; + it = fd_map_.find (fd); + + switch (act->mode_) + { + case StreamModeAccept: + case StreamModeRead: + if (it == fd_map_.end ()) + { + IoNode node = {fd, true, false, act, 0}; + set_fd (fd, 1, 0, &(fd_map_[fd] = node)); + } + else if (act->mode_ == StreamModeRead) + { + it->second.reading = true, it->second.read_action = act; + set_fd (fd, 1, (it->second.writing ? 2 : 0), &it->second); + } + else + { + if (act->callback_) act->callback_->param ().type_ = Event::Error; + schedule (act); + } + break; + + case StreamModeConnect: + case StreamModeWrite: + case StreamModeEnd: + if (it == fd_map_.end ()) + { + IoNode node = {fd, false, true, 0, act}; + set_fd (fd, 0, 1, &(fd_map_[fd] = node)); + } + else if (act->mode_ == StreamModeWrite) + { + it->second.writing = true, it->second.write_action = act; + set_fd (fd, (it->second.reading ? 2 : 0), 1, &it->second); + } + else + { + if (act->callback_) act->callback_->param ().type_ = Event::Error; + schedule (act); + } + break; + } + } +} + +void IoService::cancel (EventAction* act) +{ + std::map::iterator it; + int fd; + + if (act) + { + fd = act->fd_; + it = fd_map_.find (fd); + + switch (act->mode_) + { + case StreamModeAccept: + case StreamModeRead: + if (it != fd_map_.end () && it->second.read_action == act) + { + it->second.reading = false, it->second.read_action = 0; + if (it->second.write_action == 0) + fd_map_.erase (it); + set_fd (fd, -1, (it->second.writing ? 2 : 0), &it->second); + } + break; + + case StreamModeConnect: + case StreamModeWrite: + case StreamModeEnd: + if (it != fd_map_.end () && it->second.write_action == act) + { + it->second.writing = false, it->second.write_action = 0; + if (it->second.read_action == 0) + fd_map_.erase (it); + set_fd (fd, (it->second.reading ? 2 : 0), -1, &it->second); + } + break; + } + + terminate (act); + } +} + +void IoService::schedule (EventAction* act) +{ + EventMessage msg = {1, act}; + event_system.take_message (msg); +} + +void IoService::terminate (EventAction* act) +{ + EventMessage msg = {-1, act}; + event_system.take_message (msg); +} diff --git a/event/io_service.h b/event/io_service.h new file mode 100644 index 0000000..8b38d45 --- /dev/null +++ b/event/io_service.h @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: io_service.h // +// Description: servicing of network IO requests for the event system // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef EVENT_IO_SERVICE_H +#define EVENT_IO_SERVICE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IO_READ_BUFFER_SIZE 0x10000 +#define IO_POLL_EVENT_COUNT 512 + +struct IoNode +{ + int fd; + bool reading; + bool writing; + EventAction* read_action; + EventAction* write_action; +}; + +class IoService : public Thread +{ +private: + LogHandle log_; + RingBuffer gateway_; + uint8_t read_pool_[IO_READ_BUFFER_SIZE]; + std::map fd_map_; + int handle_, rfd_, wfd_; + +public: + IoService (); + virtual ~IoService (); + + virtual void main (); + virtual void stop (); + +private: + void handle_request (EventAction* act); + bool connect_channel (int fd, Event& ev); + bool read_channel (int fd, Event& ev, int flg); + bool write_channel (int fd, Event& ev); + bool close_channel (int fd, Event& ev); + void track (EventAction* act); + void cancel (EventAction* act); + void schedule (EventAction* act); + void terminate (EventAction* act); + + void open_resources (); + void close_resources (); + void set_fd (int fd, int rd, int wr, IoNode* node = 0); + void poll (int ms); + +public: + bool idle () const { return fd_map_.empty (); } + void wakeup () { ::write (wfd_, "*", 1); } + void take_message (const EventMessage& msg) { gateway_.write (msg); wakeup (); } +}; + +#endif /* !EVENT_IO_SERVICE_H */ diff --git a/event/lib.mk b/event/lib.mk new file mode 100644 index 0000000..4eb2e88 --- /dev/null +++ b/event/lib.mk @@ -0,0 +1,41 @@ +VPATH+= ${TOPDIR}/event + +SRCS+= event_system.cc +SRCS+= io_service.cc + +ifndef USE_POLL +ifeq "${OSNAME}" "Darwin" +USE_POLL= kqueue +endif + +ifeq "${OSNAME}" "FreeBSD" +USE_POLL= kqueue +endif + +ifeq "${OSNAME}" "OpenBSD" +USE_POLL= kqueue +endif + +ifeq "${OSNAME}" "Linux" +USE_POLL= epoll +endif + +ifeq "${OSNAME}" "Interix" +USE_POLL= select +endif + +ifeq "${OSNAME}" "SunOS" +USE_POLL= port +endif + +ifndef USE_POLL +USE_POLL= poll +endif +endif + +ifeq "${OSNAME}" "Linux" +# Required for clock_gettime(3). +LDADD+= -lrt +endif + +SRCS+= event_poll_${USE_POLL}.cc diff --git a/event/object_callback.h b/event/object_callback.h new file mode 100644 index 0000000..a3b8f72 --- /dev/null +++ b/event/object_callback.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_OBJECT_CALLBACK_H +#define EVENT_OBJECT_CALLBACK_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: object_callback.h // +// Description: template classes for event callback handling // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +template +class ObjectMethodCallback : public Callback { +public: + typedef void (C::*const method_t)(void); + +private: + C *const obj_; + method_t method_; +public: + template + ObjectMethodCallback(C *obj, T method) + : Callback(), + obj_(obj), + method_(method) + { } + + ~ObjectMethodCallback() + { } + + virtual void execute () + { + (obj_->*method_)(); + } +}; + +template +class ObjectMethodArgCallback : public Callback { +public: + typedef void (C::*const method_t)(A); + +private: + C *const obj_; + method_t method_; + A arg_; +public: + template + ObjectMethodArgCallback(C *obj, Tm method, A arg) + : Callback(), + obj_(obj), + method_(method), + arg_(arg) + { } + + ~ObjectMethodArgCallback() + { } + + virtual void execute () + { + (obj_->*method_)(arg_); + } +}; + +template +Callback *callback(C *obj, typename ObjectMethodCallback::method_t method) +{ + Callback *cb = new ObjectMethodCallback(obj, method); + return (cb); +} + +template +Callback *callback(C *obj, void (C::*const method)(A), A arg) +{ + Callback *cb = new ObjectMethodArgCallback(obj, method, arg); + return (cb); +} + +#endif /* !EVENT_OBJECT_CALLBACK_H */ diff --git a/event/typed_callback.h b/event/typed_callback.h new file mode 100644 index 0000000..e3c0572 --- /dev/null +++ b/event/typed_callback.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_TYPED_CALLBACK_H +#define EVENT_TYPED_CALLBACK_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: typed_callback.h // +// Description: template classes for event callback handling // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +template +class TypedCallback : public Callback { +protected: + T param_; + +protected: + TypedCallback() + : Callback(), + param_() + { } + +public: + virtual ~TypedCallback() + { } + + void param(T p) + { + param_ = p; + } + + T& param() + { + return param_; + } + + void reset(void) + { + param_ = T(); + } +}; + +template +class ObjectTypedCallback : public TypedCallback { +public: + typedef void (C::*const method_t)(T); + +private: + C *const obj_; + method_t method_; +public: + template + ObjectTypedCallback(C *obj, Tm method) + : TypedCallback(), + obj_(obj), + method_(method) + { } + + ~ObjectTypedCallback() + { } + + virtual void execute () + { + (obj_->*method_)(TypedCallback::param_); + } +}; + +template +class ObjectTypedArgCallback : public TypedCallback { +public: + typedef void (C::*const method_t)(T, A); + +private: + C *const obj_; + method_t method_; + A arg_; +public: + template + ObjectTypedArgCallback(C *obj, Tm method, A arg) + : TypedCallback(), + obj_(obj), + method_(method), + arg_(arg) + { } + + ~ObjectTypedArgCallback() + { } + + virtual void execute () + { + (obj_->*method_)(TypedCallback::param_, arg_); + } +}; + +template +TypedCallback *callback(C *obj, void (C::*const method)(T)) +{ + TypedCallback *cb = new ObjectTypedCallback(obj, method); + return (cb); +} + +template +TypedCallback *callback(C *obj, void (C::*const method)(T, A), A arg) +{ + TypedCallback *cb = new ObjectTypedArgCallback(obj, method, arg); + return (cb); +} + +#endif /* !EVENT_TYPED_CALLBACK_H */ diff --git a/event/typed_pair_callback.h b/event/typed_pair_callback.h new file mode 100644 index 0000000..c840dc6 --- /dev/null +++ b/event/typed_pair_callback.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef EVENT_TYPED_PAIR_CALLBACK_H +#define EVENT_TYPED_PAIR_CALLBACK_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: typed_pair_callback.h // +// Description: template classes for event callback handling // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +/* + * XXX + * Feels like I can get std::pair and some sort + * of application template to do the heavy lifting + * here to avoid duplication of TypedCallback. + */ +template +class TypedPairCallback : public Callback { +protected: + bool have_param_; + std::pair param_; + +protected: + TypedPairCallback() + : Callback(), + have_param_(false), + param_() + { } + +public: + virtual ~TypedPairCallback() + { } + +public: + void param(Ta a, Tb b) + { + param_.first = a; + param_.second = b; + have_param_ = true; + } +}; + +template +class ObjectTypedPairCallback : public TypedPairCallback { +public: + typedef void (C::*const method_t)(Ta, Tb); + +private: + C *const obj_; + method_t method_; +public: + template + ObjectTypedPairCallback(C *obj, Tm method) + : TypedPairCallback(), + obj_(obj), + method_(method) + { } + + ~ObjectTypedPairCallback() + { } + + virtual void execute () + { + ASSERT("/typed/pair/callback", (TypedPairCallback::have_param_)); + (obj_->*method_)((TypedPairCallback::param_.first), (TypedPairCallback::param_.second)); + } +}; + +template +class ObjectTypedPairArgCallback : public TypedPairCallback { +public: + typedef void (C::*const method_t)(Ta, Tb, A); + +private: + C *const obj_; + method_t method_; + A arg_; +public: + template + ObjectTypedPairArgCallback(C *obj, Tm method, A arg) + : TypedPairCallback(), + obj_(obj), + method_(method), + arg_(arg) + { } + + ~ObjectTypedPairArgCallback() + { } + + virtual void execute () + { + ASSERT("/typed/pair/callback", (TypedPairCallback::have_param_)); + (obj_->*method_)((TypedPairCallback::param_.first), (TypedPairCallback::param_.second), arg_); + } +}; + +template +TypedPairCallback *callback(C *obj, void (C::*const method)(Ta, Tb)) +{ + TypedPairCallback *cb = new ObjectTypedPairCallback(obj, method); + return (cb); +} + +template +TypedPairCallback *callback(C *obj, void (C::*const method)(Ta, Tb, A), A arg) +{ + TypedPairCallback *cb = new ObjectTypedPairArgCallback(obj, method, arg); + return (cb); +} + +#endif /* !EVENT_TYPED_PAIR_CALLBACK_H */ diff --git a/http/Makefile b/http/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/http/http_protocol.cc b/http/http_protocol.cc new file mode 100644 index 0000000..39d3cac --- /dev/null +++ b/http/http_protocol.cc @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include + +namespace { + static bool decode_nibble(uint8_t *out, uint8_t ch) + { + if (ch >= '0' && ch <= '9') { + *out |= 0x00 + (ch - '0'); + return (true); + } + if (ch >= 'a' && ch <= 'f') { + *out |= 0x0a + (ch - 'a'); + return (true); + } + if (ch >= 'A' && ch <= 'F') { + *out |= 0x0a + (ch - 'A'); + return (true); + } + return (false); + } +} + +bool +HTTPProtocol::Message::decode(Buffer *input) +{ + if (start_line_.empty()) { + start_line_.clear(); + headers_.clear(); + body_.clear(); + } + + Buffer line; + if (ExtractLine(&line, input) != ParseSuccess) { + ERROR("/http/protocol/message") << "Could not get start line."; + return (false); + } + if (line.empty()) { + ERROR("/http/protocol/message") << "Premature end of headers."; + return (false); + } + start_line_ = line; + + if (type_ == Request) { + /* + * There are two kinds of request line. The first has two + * words, the second has three. Anything else is malformed. + * + * The first kind is HTTP/0.9. The second kind can be + * anything, especially HTTP/1.0 and HTTP/1.1. + */ + std::vector words = line.split(' ', false); + if (words.empty()) { + ERROR("/http/protocol/message") << "Empty start line."; + return (false); + } + if (words.size() == 2) { + /* + * HTTP/0.9. This is all we should get from the client. + */ + return (true); + } + + if (words.size() != 3) { + ERROR("/http/protocol/message") << "Too many request parameters."; + return (false); + } + } else { + ASSERT("/http/protocol/message", type_ == Response); + /* + * No parsing behavior is conditional for responses. + */ + line.clear(); + } + + /* + * HTTP/1.0 or HTTP/1.1. Get headers. + */ + std::string last_header; + for (;;) { + ASSERT("/http/protocol/message", line.empty()); + if (ExtractLine(&line, input) != ParseSuccess) { + ERROR("/http/protocol/message") << "Could not extract line for headers."; + return (false); + } + + /* + * Process end of headers! + */ + if (line.empty()) { + /* + * XXX + * Use Content-Length, Transfer-Encoding, etc. + */ + if (!input->empty()) + input->moveout(&body_); + return (true); + } + + /* + * Process header. + */ + if (line.peek() == ' ') { /* XXX isspace? */ + /* + * Fold headers per RFC822. + * + * XXX Always forget how to handle leading whitespace. + */ + if (last_header == "") { + ERROR("/http/protocol/message") << "Folded header sent before any others."; + return (false); + } + + line.moveout(&headers_[last_header].back()); + continue; + } + + unsigned pos; + if (!line.find(':', &pos)) { + ERROR("/http/protocol/message") << "Empty header name."; + return (false); + } + + Buffer key; + line.moveout(&key, pos); + line.skip(1); + + Buffer value; + while (!line.empty() && line.peek() == ' ') + line.skip(1); + value = line; + + std::string header; + key.extract(header); + + headers_[header].push_back(value); + last_header = header; + + line.clear(); + } +} + +bool +HTTPProtocol::DecodeURI(Buffer *encoded, Buffer *decoded) +{ + if (encoded->empty()) + return (true); + + for (;;) { + unsigned pos; + if (!encoded->find('%', &pos)) { + encoded->moveout(decoded); + return (true); + } + if (pos != 0) + encoded->moveout(decoded, pos); + if (encoded->length() < 3) + return (false); + uint8_t vis[2]; + encoded->copyout(vis, 1, 2); + uint8_t byte = 0x00; + if (!decode_nibble(&byte, vis[0])) + return (false); + byte <<= 4; + if (!decode_nibble(&byte, vis[1])) + return (false); + decoded->append(byte); + encoded->skip(3); + } +} + +HTTPProtocol::ParseStatus +HTTPProtocol::ExtractLine(Buffer *line, Buffer *input, Buffer *line_ending) +{ + ASSERT("/http/protocol/extract/line", line->empty()); + + if (input->empty()) { + DEBUG("/http/protocol/extract/line") << "Empty buffer."; + return (ParseIncomplete); + } + + unsigned pos; + uint8_t found; + if (!input->find_any("\r\n", &pos, &found)) { + DEBUG("/http/protocol/extract/line") << "Incomplete line."; + return (ParseIncomplete); + } + + /* + * XXX + * We should pick line ending from the start line and require it to + * be consistent for remaining lines, rather than using find_any over + * and over, which is non-trivial. Handling of the start line can be + * quite easily and cheaply before the loop. + */ + switch (found) { + case '\r': + /* CRLF line endings. */ + ASSERT("/http/protocol/extract/line", input->length() > pos); + if (input->length() == pos + 1) { + DEBUG("/http/protocol/extract/line") << "Carriage return at end of buffer, need following line feed."; + return (ParseIncomplete); + } + if (pos != 0) + input->moveout(line, pos); + input->skip(1); + if (input->peek() != '\n') { + ERROR("/http/protocol/extract/line") << "Carriage return not followed by line feed."; + return (ParseFailure); + } + input->skip(1); + if (line_ending != NULL) + line_ending->append("\r\n"); + break; + case '\n': + /* Unix line endings. */ + if (pos != 0) + input->moveout(line, pos); + input->skip(1); + if (line_ending != NULL) + line_ending->append("\n"); + break; + default: + NOTREACHED("/http/protocol/extract/line"); + } + + return (ParseSuccess); +} diff --git a/http/http_protocol.h b/http/http_protocol.h new file mode 100644 index 0000000..5d76ae1 --- /dev/null +++ b/http/http_protocol.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef HTTP_HTTP_PROTOCOL_H +#define HTTP_HTTP_PROTOCOL_H + +#include + +namespace HTTPProtocol { + enum ParseStatus { + ParseSuccess, + ParseFailure, + ParseIncomplete, + }; + + struct Message { + enum Type { + Request, + Response, + }; + + Type type_; + Buffer start_line_; + std::map > headers_; + Buffer body_; +#if 0 + std::map > trailers_; +#endif + + Message(Type type) + : type_(type), + start_line_(), + headers_(), + body_() + { } + + ~Message() + { } + + bool decode(Buffer *); + }; + + struct Request : public Message { + Request(void) + : Message(Message::Request) + { } + + ~Request() + { } + }; + + struct Response : public Message { + Response(void) + : Message(Message::Response) + { } + + ~Response() + { } + }; + + enum Status { + OK, + BadRequest, + NotFound, + NotImplemented, + VersionNotSupported, + }; + + bool DecodeURI(Buffer *, Buffer *); + ParseStatus ExtractLine(Buffer *, Buffer *, Buffer * = NULL); +} + +#endif /* !HTTP_HTTP_PROTOCOL_H */ diff --git a/http/lib.mk b/http/lib.mk new file mode 100644 index 0000000..63f1b8e --- /dev/null +++ b/http/lib.mk @@ -0,0 +1,3 @@ +VPATH+= ${TOPDIR}/http + +SRCS+= http_protocol.cc diff --git a/io/Makefile b/io/Makefile new file mode 100644 index 0000000..da02667 --- /dev/null +++ b/io/Makefile @@ -0,0 +1,4 @@ +SUBDIR+=net +SUBDIR+=socket + +include ../common/subdir.mk diff --git a/io/lib.mk b/io/lib.mk new file mode 100644 index 0000000..d142880 --- /dev/null +++ b/io/lib.mk @@ -0,0 +1,5 @@ +VPATH+= ${TOPDIR}/io + +SRCS+= stream_handle.cc +SRCS+= sink_filter.cc + diff --git a/io/net/Makefile b/io/net/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/io/net/lib.mk b/io/net/lib.mk new file mode 100644 index 0000000..b18226b --- /dev/null +++ b/io/net/lib.mk @@ -0,0 +1,4 @@ +VPATH+= ${TOPDIR}/io/net + +SRCS+= tcp_server.cc +SRCS+= udp_server.cc diff --git a/io/net/tcp_server.cc b/io/net/tcp_server.cc new file mode 100644 index 0000000..891f366 --- /dev/null +++ b/io/net/tcp_server.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: tcp_server.cc // +// Description: base class for a connection oriented network server // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +bool TCPServer::listen (SocketAddressFamily family, const std::string& name) +{ + if (socket_) + { + socket_->close (0); + delete socket_; + } + + socket_ = Socket::create (family, SocketTypeStream, "tcp", name); + if (! socket_) + { + ERROR("/tcp/server") << "Unable to create socket."; + return false; + } + if (! socket_->bind (name)) + { + ERROR("/tcp/server") << "Socket bind failed"; + return false; + } + if (! socket_->listen ()) + { + ERROR("/tcp/server") << "Socket listen failed"; + return false; + } + + return true; +} diff --git a/io/net/tcp_server.h b/io/net/tcp_server.h new file mode 100644 index 0000000..a43be9e --- /dev/null +++ b/io/net/tcp_server.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef IO_NET_TCP_SERVER_H +#define IO_NET_TCP_SERVER_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: tcp_server.h // +// Description: base class for a connection oriented network server // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class TCPServer +{ + LogHandle log_; + Socket* socket_; + +public: + TCPServer () : log_("/tcp/server"), socket_(0) + { } + + ~TCPServer () + { + delete socket_; + } + + bool listen (SocketAddressFamily family, const std::string& name); + + Action* accept (SocketEventCallback* cb) + { + return (socket_ ? socket_->accept (cb) : 0); + } + + Action* close (EventCallback* cb = 0) + { + return (socket_ ? socket_->close (cb) : 0); + } + + std::string getsockname () const + { + return (socket_ ? socket_->getsockname () : std::string ()); + } +}; + +#endif /* !IO_NET_TCP_SERVER_H */ diff --git a/io/net/udp_server.cc b/io/net/udp_server.cc new file mode 100644 index 0000000..e993e5b --- /dev/null +++ b/io/net/udp_server.cc @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009-2010 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: udp_server.cc // +// Description: base class for a datagram network server // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +bool UDPServer::listen (SocketAddressFamily family, const std::string& name) +{ + if (socket_) + { + socket_->close (0); + delete socket_; + } + + socket_ = Socket::create (family, SocketTypeDatagram, "udp", name); + if (! socket_) + { + ERROR("/udp/server") << "Unable to create socket."; + return false; + } + if (! socket_->bind (name)) + { + ERROR("/udp/server") << "Socket bind failed"; + return false; + } + + return true; +} diff --git a/io/net/udp_server.h b/io/net/udp_server.h new file mode 100644 index 0000000..5d9488d --- /dev/null +++ b/io/net/udp_server.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef IO_NET_UDP_SERVER_H +#define IO_NET_UDP_SERVER_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: udp_server.h // +// Description: base class for a datagram network server // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class UDPServer +{ + LogHandle log_; + Socket* socket_; + +public: + UDPServer () : log_("/udp/server"), socket_(0) + { } + + ~UDPServer () + { + delete socket_; + } + + bool listen (SocketAddressFamily family, const std::string& name); + + Action* read (EventCallback* cb) + { + return (socket_ ? socket_->read (cb) : 0); + } + + Action* close (EventCallback* cb) + { + return (socket_ ? socket_->close (cb) : 0); + } + + std::string getsockname (void) const + { + return (socket_ ? socket_->getsockname () : std::string ()); + } +}; + +#endif /* !IO_NET_UDP_SERVER_H */ diff --git a/io/sink_filter.cc b/io/sink_filter.cc new file mode 100644 index 0000000..498b319 --- /dev/null +++ b/io/sink_filter.cc @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: sink_filter.cc // +// Description: a filter to write into a target device // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "sink_filter.h" + +SinkFilter::SinkFilter (const LogHandle& log, Socket* sck, bool cln) : BufferedFilter (log) +{ + sink_ = sck; write_action_ = 0; client_ = cln, down_ = closing_ = false; +} + +SinkFilter::~SinkFilter () +{ + if (write_action_) write_action_->cancel (); +} + +bool SinkFilter::consume (Buffer& buf) +{ + if (! sink_ || closing_) + return false; + + if (write_action_) + pending_.append (buf); + else + write_action_ = sink_->write (buf, callback (this, &SinkFilter::write_complete)); + + return (write_action_ != 0); +} + +void SinkFilter::write_complete (Event e) +{ + if (write_action_) + write_action_->cancel (), write_action_ = 0; + + switch (e.type_) + { + case Event::Done: + if (! pending_.empty ()) + { + write_action_ = sink_->write (pending_, callback (this, &SinkFilter::write_complete)); + pending_.clear (); + } + else if (flushing_) + flush (0); + break; + case Event::Error: + if (e.error_ == EPIPE && client_) + DEBUG(log_) << "Client closed connection"; + else + ERROR(log_) << "Write failed: " << e; + closing_ = true; + break; + } +} + +void SinkFilter::flush (int flg) +{ + flushing_ = true; + flush_flags_ |= flg; + if (flushing_ && ! write_action_) + { + if (! down_) + down_ = (sink_->shutdown (false, true) == 0); + Filter::flush (flush_flags_); + } +} diff --git a/io/sink_filter.h b/io/sink_filter.h new file mode 100644 index 0000000..7ed55cf --- /dev/null +++ b/io/sink_filter.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: sink_filter.h // +// Description: a filter to write into a target device // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +class SinkFilter : public BufferedFilter +{ +private: + Socket* sink_; + Action* write_action_; + bool client_, down_, closing_; + +public: + SinkFilter (const LogHandle& log, Socket* sck, bool cln = 0); + virtual ~SinkFilter (); + + virtual bool consume (Buffer& buf); + void write_complete (Event e); + virtual void flush (int flg); +}; + diff --git a/io/socket/Makefile b/io/socket/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/io/socket/lib.mk b/io/socket/lib.mk new file mode 100644 index 0000000..15f6bcf --- /dev/null +++ b/io/socket/lib.mk @@ -0,0 +1,15 @@ +VPATH+= ${TOPDIR}/io/socket + +SRCS+= socket.cc +SRCS+= unix_server.cc + +ifeq "${OSNAME}" "Haiku" +# Required for sockets. +LDADD+= -lnetwork +endif + +ifeq "${OSNAME}" "SunOS" +# Required for sockets. +LDADD+= -lnsl +LDADD+= -lsocket +endif diff --git a/io/socket/socket.cc b/io/socket/socket.cc new file mode 100644 index 0000000..a207a02 --- /dev/null +++ b/io/socket/socket.cc @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: socket.cc // +// Description: a stream handle for network connections // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +/* + * XXX Presently using AF_INET6 as the test for what is supported, but that is + * wrong in many, many ways. + */ + +#if defined(__OPENNT) +struct addrinfo { + int ai_family; + int ai_socktype; + int ai_protocol; + struct sockaddr *ai_addr; + socklen_t ai_addrlen; + + struct sockaddr_in _ai_inet; +}; + +static int +getaddrinfo(const char *host, const char *serv, const struct addrinfo *hints, + struct addrinfo **aip) +{ + struct addrinfo *ai; + struct hostent *he; + + he = gethostbyname(host); + if (he == NULL) + return (1); + + ai = new addrinfo; + ai->ai_family = AF_INET; + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + ai->ai_addr = (struct sockaddr *)&ai->_ai_inet; + ai->ai_addrlen = sizeof ai->_ai_inet; + + memset(&ai->_ai_inet, 0, sizeof ai->_ai_inet); + ai->_ai_inet.sin_family = AF_INET; + /* XXX _ai_inet.sin_addr on non-__OPENNT */ + + ASSERT(log_, he->h_length == sizeof ai->_ai_inet.sin_addr); + memcpy(&ai->_ai_inet.sin_addr, he->h_addr_list[0], he->h_length); + + if (serv != NULL) { + unsigned long port; + char *end; + + port = strtoul(serv, &end, 0); + if (*end == '\0') { + ai->_ai_inet.sin_port = htons(port); + } else { + struct protoent *pe; + struct servent *se; + + pe = getprotobynumber(ai->ai_protocol); + if (pe == NULL) { + free(ai); + return (2); + } + + se = getservbyname(serv, pe->p_name); + if (se == NULL) { + free(ai); + return (3); + } + + ai->_ai_inet.sin_port = se->s_port; + } + } + + *aip = ai; + return (0); +} + +static const char * +gai_strerror(int rv) +{ + switch (rv) { + case 1: + return "could not look up host"; + case 2: + return "could not look up protocol"; + case 3: + return "could not look up service"; + default: + NOTREACHED(log_); + return "internal error"; + } +} + +static void +freeaddrinfo(struct addrinfo *ai) +{ + delete ai; +} + +#define NI_MAXHOST 1024 +#define NI_MAXSERV 16 +#define NI_NUMERICHOST 0 +#define NI_NUMERICSERV 0 + +static int +getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct sockaddr_in *inet = (struct sockaddr_in *)sa; + char *p; + + if (inet->sin_family != AF_INET || salen != sizeof *inet || flags != 0) + return (ENOTSUP); + + p = inet_ntoa(inet->sin_addr); + if (p == NULL) + return (EINVAL); + + snprintf(host, hostlen, "%s", p); + snprintf(serv, servlen, "%hu", ntohs(inet->sin_port)); + + return (0); +} +#endif + +struct socket_address { + union address_union { + struct sockaddr sockaddr_; + struct sockaddr_in inet_; +#if defined(AF_INET6) + struct sockaddr_in6 inet6_; +#endif + struct sockaddr_un unix_; + } addr_; + socklen_t addrlen_; + + socket_address(void) + : addr_(), + addrlen_(0) + { + memset(&addr_, 0, sizeof addr_); + } + + bool operator() (int domain, int socktype, int protocol, const std::string& str) + { + switch (domain) { +#if defined(AF_INET6) + case AF_UNSPEC: + case AF_INET6: +#endif + case AF_INET: + { + std::string name; + std::string service; + + std::string::size_type pos = str.find(']'); + if (pos != std::string::npos) { + if (pos < 3 || str[0] != '[' || str[pos + 1] != ':') + return (false); + name = str.substr(1, pos - 1); + service = str.substr(pos + 2); + } else { + pos = str.find(':'); + if (pos == std::string::npos) + return (false); + name = str.substr(0, pos); + service = str.substr(pos + 1); + } + + struct addrinfo hints; + + memset(&hints, 0, sizeof hints); + hints.ai_family = domain; + hints.ai_socktype = socktype; + hints.ai_protocol = protocol; + + /* + * Mac OS X ~Snow Leopard~ cannot handle a service name + * of "0" where older Mac OS X could. Don't know if + * that is because it's not in services(5) or due to + * some attempt at input validation. So work around it + * here, sigh. + */ + const char *servptr; + if (service == "" || service == "0") + servptr = NULL; + else + servptr = service.c_str(); + + struct addrinfo *ai; + int rv = getaddrinfo(name.c_str(), servptr, &hints, &ai); + if (rv != 0) { + ERROR("/socket/address") << "Could not look up " << str << ": " << gai_strerror(rv); + return (false); + } + + /* + * Just use the first one. + * XXX Will we ever get one in the wrong family? Is the hint mandatory? + */ + memcpy(&addr_.sockaddr_, ai->ai_addr, ai->ai_addrlen); + addrlen_ = ai->ai_addrlen; + + freeaddrinfo(ai); + break; + } + case AF_UNIX: + addr_.unix_.sun_family = domain; + snprintf(addr_.unix_.sun_path, sizeof addr_.unix_.sun_path, "%s", str.c_str()); + addrlen_ = sizeof addr_.unix_; +#if !defined(__linux__) && !defined(__sun__) && !defined(__OPENNT) + addr_.unix_.sun_len = addrlen_; +#endif + break; + default: + ERROR("/socket/address") << "Addresss family not supported: " << domain; + return (false); + } + + return (true); + } + + operator std::string (void) const + { + std::ostringstream str; + + switch (addr_.sockaddr_.sa_family) { +#if defined(AF_INET6) + case AF_INET6: +#endif + case AF_INET: + { + char host[NI_MAXHOST], serv[NI_MAXSERV]; + int flags; + int rv; + + /* XXX If we ever support !NI_NUMERICSERV, need NI_DGRAM for UDP. */ + flags = NI_NUMERICHOST | NI_NUMERICSERV; + + rv = getnameinfo(&addr_.sockaddr_, addrlen_, host, sizeof host, serv, sizeof serv, flags); + if (rv == -1) + return (""); + + str << '[' << host << ']' << ':' << serv; + break; + } + case AF_UNIX: + str << addr_.unix_.sun_path; + break; + default: + return (""); + } + + return (str.str()); + } +}; + +Socket::Socket (int fd, int domain, int socktype, int protocol) +: StreamHandle(fd), + log_("/socket"), + domain_(domain), + socktype_(socktype), + protocol_(protocol), + accept_request_(0), + accept_check_(0) +{ + ASSERT(log_, fd_ != -1); +} + +Action* Socket::accept (SocketEventCallback* cb) +{ + ASSERT(log_, accept_request_ == 0); + ASSERT(log_, accept_check_ == 0); + + accept_request_ = new SocketEventAction (this, &Socket::accept_cancel, cb); + accept_check_ = event_system.track (fd_, StreamModeAccept, callback (this, &Socket::accept_complete)); + + return accept_request_; +} + +void Socket::accept_complete (Event e) +{ + SocketEventCallback* cb; + + if (accept_check_) + accept_check_->cancel (), accept_check_ = 0; + + if (accept_request_ && (cb = accept_request_->callback_)) + { + switch (e.type_) + { + case Event::Done: + break; + case Event::EOS: + DEBUG(log_) << "Got EOS while waiting for accept: " << e; + e.type_ = Event::Error; + case Event::Error: + cb->param (e, 0); + cb->execute (); + return; + default: + HALT(log_) << "Unexpected event: " << e; + } + + int s = ::accept (fd_, 0, 0); + if (s == -1) + { + switch (errno) + { + case EAGAIN: + return; + default: + cb->param (Event(Event::Error, errno), 0); + cb->execute (); + return; + } + } + + Socket* sck = new Socket (s, domain_, socktype_, protocol_); + cb->param (Event::Done, sck); + cb->execute (); + + accept_check_ = event_system.track (fd_, StreamModeAccept, callback (this, &Socket::accept_complete)); + } +} + +void Socket::accept_cancel (void) +{ + if (accept_check_) + accept_check_->cancel (), accept_check_ = 0; + + delete accept_request_; + accept_request_ = 0; +} + +Action* Socket::connect (const std::string& name, EventCallback* cb) +{ + socket_address addr; + + if (! addr (domain_, socktype_, protocol_, name)) + { + ERROR(log_) << "Invalid name for connect: " << name; + return 0; + } + + if (cb) + cb->param ().buffer_ = Buffer ((uint8_t*) &addr.addr_.sockaddr_, addr.addrlen_); + + return event_system.track (fd_, StreamModeConnect, cb); +} + +bool Socket::bind (const std::string& name) +{ + socket_address addr; + + if (! addr (domain_, socktype_, protocol_, name)) + { + ERROR(log_) << "Invalid name for bind: " << name; + return (false); + } + + int on = 1; + int rv = setsockopt (fd_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on); + if (rv == -1) + ERROR(log_) << "Could not setsockopt(SO_REUSEADDR): " << strerror(errno); + + rv = ::bind (fd_, &addr.addr_.sockaddr_, addr.addrlen_); + if (rv == -1) + { + ERROR(log_) << "Could not bind: " << strerror(errno); + return (false); + } + + return (true); +} + +bool Socket::listen (void) +{ + int rv = ::listen (fd_, 128); + return (rv != -1); +} + +bool Socket::shutdown (bool shut_read, bool shut_write) +{ + int how; + + if (shut_read && shut_write) + how = SHUT_RDWR; + else if (shut_read) + how = SHUT_RD; + else if (shut_write) + how = SHUT_WR; + else + how = 0; + + int rv = ::shutdown (fd_, how); + return (rv != -1); +} + +std::string Socket::getpeername (void) const +{ + socket_address sa; + int rv; + + sa.addrlen_ = sizeof sa.addr_; + rv = ::getpeername(fd_, &sa.addr_.sockaddr_, &sa.addrlen_); + if (rv == -1) + return (""); + + /* XXX Check len. */ + + return ((std::string)sa); +} + +std::string Socket::getsockname (void) const +{ + socket_address sa; + int rv; + + sa.addrlen_ = sizeof sa.addr_; + rv = ::getsockname(fd_, &sa.addr_.sockaddr_, &sa.addrlen_); + if (rv == -1) + return (""); + + /* XXX Check len. */ + + return ((std::string)sa); +} + +Socket* Socket::create(SocketAddressFamily family, SocketType type, const std::string& protocol, const std::string& hint) +{ + int typenum; + + switch (type) { + case SocketTypeStream: + typenum = SOCK_STREAM; + break; + + case SocketTypeDatagram: + typenum = SOCK_DGRAM; + break; + + default: + ERROR("/socket") << "Unsupported socket type."; + return (NULL); + } + + int protonum; + + if (protocol == "") { + protonum = 0; + } else { + struct protoent *proto = getprotobyname(protocol.c_str()); + if (proto == NULL) { + ERROR("/socket") << "Invalid protocol: " << protocol; + return (NULL); + } + protonum = proto->p_proto; + } + + int domainnum; + + switch (family) { + case SocketAddressFamilyIP: +#if defined(AF_INET6) + if (hint == "") { + ERROR("/socket") << "Must specify hint address for IP sockets or specify IPv4 or IPv6 explicitly."; + return (NULL); + } else { + socket_address addr; + + if (!addr(AF_UNSPEC, typenum, protonum, hint)) { + ERROR("/socket") << "Invalid hint: " << hint; + return (NULL); + } + + /* XXX Just make socket_address::operator() smarter about AF_UNSPEC? */ + switch (addr.addr_.sockaddr_.sa_family) { + case AF_INET: + domainnum = AF_INET; + break; + + case AF_INET6: + domainnum = AF_INET6; + break; + + default: + ERROR("/socket") << "Unsupported address family for hint: " << hint; + return (NULL); + } + break; + } +#else + (void)hint; + domainnum = AF_INET; + break; +#endif + + case SocketAddressFamilyIPv4: + domainnum = AF_INET; + break; + +#if defined(AF_INET6) + case SocketAddressFamilyIPv6: + domainnum = AF_INET6; + break; +#endif + + case SocketAddressFamilyUnix: + domainnum = AF_UNIX; + break; + + default: + ERROR("/socket") << "Unsupported address family."; + return (NULL); + } + + int s = ::socket(domainnum, typenum, protonum); + if (s == -1) { +#if defined(AF_INET6) + /* + * If we were trying to create an IPv6 socket for a request that + * did not specify IPv4 vs. IPv6 and the system claims that the + * protocol is not supported, try explicitly creating an IPv4 + * socket. + */ + if (errno == EPROTONOSUPPORT && domainnum == AF_INET6 && + family == SocketAddressFamilyIP) { + DEBUG("/socket") << "IPv6 socket create failed; trying IPv4."; + return (Socket::create(SocketAddressFamilyIPv4, type, protocol, hint)); + } +#endif + ERROR("/socket") << "Could not create socket: " << strerror(errno); + return (NULL); + } + + return (new Socket(s, domainnum, typenum, protonum)); +} diff --git a/io/socket/socket.h b/io/socket/socket.h new file mode 100644 index 0000000..5f69219 --- /dev/null +++ b/io/socket/socket.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef IO_SOCKET_SOCKET_H +#define IO_SOCKET_SOCKET_H + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: socket.h // +// Description: a stream handle for network connections // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +typedef class TypedPairCallback SocketEventCallback; +typedef class CallbackAction SocketEventAction; + +class Socket : public StreamHandle +{ + LogHandle log_; + int domain_; + int socktype_; + int protocol_; + SocketEventAction* accept_request_; + Action* accept_check_; + +private: + Socket (int, int, int, int); + +public: + Action* accept (SocketEventCallback*); + void accept_complete (Event); + void accept_cancel (); + Action* connect (const std::string&, EventCallback*); + bool bind (const std::string&); + bool listen (); + bool shutdown (bool, bool); + + std::string getpeername () const; + std::string getsockname () const; + + static Socket* create (SocketAddressFamily, SocketType, const std::string& = "", const std::string& = ""); +}; + +#endif /* !IO_SOCKET_SOCKET_H */ diff --git a/io/socket/socket_types.h b/io/socket/socket_types.h new file mode 100644 index 0000000..0b1abcf --- /dev/null +++ b/io/socket/socket_types.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef IO_SOCKET_SOCKET_TYPES_H +#define IO_SOCKET_SOCKET_TYPES_H + +enum SocketAddressFamily { + SocketAddressFamilyIP, + SocketAddressFamilyIPv4, + SocketAddressFamilyIPv6, + SocketAddressFamilyUnix, + SocketAddressFamilyUnspecified +}; + +enum SocketType { + SocketTypeStream, + SocketTypeDatagram, +}; + +class Socket; + +#endif /* !IO_SOCKET_SOCKET_TYPES_H */ diff --git a/io/socket/unix_server.cc b/io/socket/unix_server.cc new file mode 100644 index 0000000..88ab820 --- /dev/null +++ b/io/socket/unix_server.cc @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2009-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: unix_server.cc // +// Description: base class for a UNIX domain server // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +bool UnixServer::listen (const std::string& name) +{ + if (socket_) + { + socket_->close (0); + delete socket_; + } + + socket_ = Socket::create (SocketAddressFamilyUnix, SocketTypeStream); + if (! socket_) + { + ERROR("/unix/server") << "Unable to create socket."; + return false; + } + if (! socket_->bind (name)) + { + ERROR("/unix/server") << "Socket bind failed"; + return false; + } + if (! socket_->listen ()) + { + ERROR("/unix/server") << "Socket listen failed"; + return false; + } + + return true; +} diff --git a/io/socket/unix_server.h b/io/socket/unix_server.h new file mode 100644 index 0000000..2509473 --- /dev/null +++ b/io/socket/unix_server.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef IO_SOCKET_UNIX_SERVER_H +#define IO_SOCKET_UNIX_SERVER_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: unix_server.h // +// Description: base class for a UNIX domain server // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class UnixServer +{ + LogHandle log_; + Socket* socket_; + +public: + UnixServer () : log_("/unix/server"), socket_(0) + { } + + ~UnixServer() + { + delete socket_; + } + + bool listen (const std::string& name); + + Action* accept (SocketEventCallback* cb) + { + return (socket_ ? socket_->accept (cb) : 0); + } + + Action* close (EventCallback* cb) + { + return (socket_ ? socket_->close (cb) : 0); + } +}; + +#endif /* !IO_SOCKET_UNIX_SERVER_H */ diff --git a/io/stream_handle.cc b/io/stream_handle.cc new file mode 100644 index 0000000..09a9275 --- /dev/null +++ b/io/stream_handle.cc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: stream_handle.cc // +// Description: basic operations on stream objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +StreamHandle::StreamHandle (int fd) : log_("/file/descriptor"), fd_(fd) +{ + int flags = ::fcntl (fd_, F_GETFL, 0); + if (flags == -1) + ERROR(log_) << "Could not get flags for file descriptor."; + else + { + flags = ::fcntl (fd_, F_SETFL, flags | O_NONBLOCK); + if (flags == -1) + ERROR(log_) << "Could not set flags for file descriptor, some operations may block."; + } +} + +Action* StreamHandle::read (EventCallback* cb) +{ + return event_system.track (fd_, StreamModeRead, cb); +} + +Action* StreamHandle::write (Buffer& buf, EventCallback* cb) +{ + if (cb) + cb->param ().buffer_ = buf; + + return event_system.track (fd_, StreamModeWrite, cb); +} + +Action* StreamHandle::close (EventCallback* cb) +{ + return event_system.track (fd_, StreamModeEnd, cb); +} diff --git a/io/stream_handle.h b/io/stream_handle.h new file mode 100644 index 0000000..04cdfb5 --- /dev/null +++ b/io/stream_handle.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef IO_STREAM_HANDLE_H +#define IO_STREAM_HANDLE_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: stream_handle.h // +// Description: basic operations on stream objects // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class StreamHandle +{ + LogHandle log_; +protected: + int fd_; + +public: + StreamHandle (int); + virtual ~StreamHandle () {} + + virtual Action* read (EventCallback* cb); + virtual Action* write (Buffer& buf, EventCallback* cb); + virtual Action* close (EventCallback* cb = 0); +}; + +#endif /* !IO_STREAM_HANDLE_H */ diff --git a/programs/Makefile b/programs/Makefile new file mode 100644 index 0000000..b832689 --- /dev/null +++ b/programs/Makefile @@ -0,0 +1,3 @@ +SUBDIR+=wanproxy + +include ../common/subdir.mk diff --git a/programs/wanproxy/Makefile b/programs/wanproxy/Makefile new file mode 100644 index 0000000..1769eca --- /dev/null +++ b/programs/wanproxy/Makefile @@ -0,0 +1,19 @@ +PROGRAM=wanproxy + +SRCS+= proxy_connector.cc +SRCS+= proxy_listener.cc + +SRCS+= wanproxy.cc +SRCS+= wanproxy_config.cc +SRCS+= wanproxy_config_class_codec.cc +SRCS+= wanproxy_config_class_interface.cc +SRCS+= wanproxy_config_class_peer.cc +SRCS+= wanproxy_config_class_proxy.cc +SRCS+= wanproxy_config_type_codec.cc +SRCS+= wanproxy_config_type_compressor.cc +SRCS+= wanproxy_config_type_proxy_type.cc +SRCS+= wanproxy_config_type_proxy_role.cc + +TOPDIR=../.. +USE_LIBS=common common/thread common/time common/uuid config crypto event http io io/net io/socket ssh xcodec xcodec/cache/coss zlib +include ${TOPDIR}/common/program.mk diff --git a/programs/wanproxy/proxy_connector.cc b/programs/wanproxy/proxy_connector.cc new file mode 100644 index 0000000..eed8154 --- /dev/null +++ b/programs/wanproxy/proxy_connector.cc @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include "proxy_connector.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: proxy_connector.cc // +// Description: carries data between endpoints through a filter chain // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +ProxyConnector::ProxyConnector (const std::string& name, + WANProxyCodec* interface_codec, + WANProxyCodec* remote_codec, + Socket* local_socket, + SocketAddressFamily family, + const std::string& remote_name, + bool cln, bool ssh) + : log_("/wanproxy/proxy/" + name + "/connector"), + interface_codec_(interface_codec), + remote_codec_(remote_codec), + local_socket_(local_socket), + remote_socket_(0), + is_cln_(cln), + is_ssh_(ssh), + request_chain_(this), + response_chain_(this), + connect_action_(0), + stop_action_(0), + request_action_(0), + response_action_(0), + chain_ready_(0) +{ + if (local_socket_ && (remote_socket_ = Socket::create (family, SocketTypeStream, "tcp", remote_name))) + { + connect_action_ = remote_socket_->connect (remote_name, callback (this, &ProxyConnector::connect_complete)); + stop_action_ = event_system.register_interest (EventInterestStop, callback (this, &ProxyConnector::conclude)); + } + else + { + conclude (); + } +} + +ProxyConnector::~ProxyConnector () +{ + if (connect_action_) + connect_action_->cancel (); + if (stop_action_) + stop_action_->cancel (); + if (request_action_) + request_action_->cancel (); + if (response_action_) + response_action_->cancel (); + if (local_socket_) + local_socket_->close (); + if (remote_socket_) + remote_socket_->close (); + delete local_socket_; + delete remote_socket_; +} + +void ProxyConnector::connect_complete (Event e) +{ + if (connect_action_) + connect_action_->cancel (), connect_action_ = 0; + + switch (e.type_) + { + case Event::Done: + break; + case Event::Error: + INFO(log_) << "Connect failed: " << e; + conclude (); + return; + default: + ERROR(log_) << "Unexpected event: " << e; + conclude (); + return; + } + + if (build_chains (interface_codec_, remote_codec_, local_socket_, remote_socket_)) + { + request_action_ = local_socket_->read (callback (this, &ProxyConnector::on_request_data)); + response_action_ = remote_socket_->read (callback (this, &ProxyConnector::on_response_data)); + } +} + +bool ProxyConnector::build_chains (WANProxyCodec* cdc1, WANProxyCodec* cdc2, Socket* sck1, Socket* sck2) +{ + if (! sck1 || ! sck2) + return false; + + response_chain_.prepend (new SinkFilter ("/wanproxy/response", sck1, is_cln_)); + + if (is_ssh_) + { + SSH::EncryptFilter* enc; SSH::DecryptFilter* dec; + request_chain_.append ((dec = new SSH::DecryptFilter ((cdc1 ? SSH::SOURCE_ENCODED : 0)))); + response_chain_.prepend ((enc = new SSH::EncryptFilter (SSH::ServerRole, (cdc1 ? SSH::SOURCE_ENCODED : 0)))); + dec->set_encrypter (enc); + } + + if (cdc1) + { + if (cdc1->counting_) + { + request_chain_.append (new CountFilter (cdc1->request_input_bytes_)); + response_chain_.prepend (new CountFilter (cdc1->response_output_bytes_)); + } + + if (cdc1->compressor_) + { + request_chain_.append (new InflateFilter ()); + response_chain_.prepend (new DeflateFilter (cdc1->compressor_level_)); + } + + if (cdc1->xcache_) + { + EncodeFilter* enc; DecodeFilter* dec; + request_chain_.append ((dec = new DecodeFilter ("/wanproxy/" + cdc1->name_ + "/dec", cdc1->xcache_))); + response_chain_.prepend ((enc = new EncodeFilter ("/wanproxy/" + cdc1->name_ + "/enc", cdc1->xcache_))); + dec->set_upstream (enc); + } + + if (cdc1->counting_) + { + request_chain_.append (new CountFilter (cdc1->request_output_bytes_)); + response_chain_.prepend (new CountFilter (cdc1->response_input_bytes_)); + } + } + + if (cdc2) + { + if (cdc2->counting_) + { + request_chain_.append (new CountFilter (cdc2->request_input_bytes_)); + response_chain_.prepend (new CountFilter (cdc2->response_output_bytes_)); + } + + if (cdc2->xcache_) + { + EncodeFilter* enc; DecodeFilter* dec; + request_chain_.append ((enc = new EncodeFilter ("/wanproxy/" + cdc2->name_ + "/enc", cdc2->xcache_))); + response_chain_.prepend ((dec = new DecodeFilter ("/wanproxy/" + cdc2->name_ + "/dec", cdc2->xcache_))); + dec->set_upstream (enc); + } + + if (cdc2->compressor_) + { + request_chain_.append (new DeflateFilter (cdc2->compressor_level_)); + response_chain_.prepend (new InflateFilter ()); + } + + if (cdc2->counting_) + { + request_chain_.append (new CountFilter (cdc2->request_output_bytes_)); + response_chain_.prepend (new CountFilter (cdc2->response_input_bytes_)); + } + } + + if (is_ssh_) + { + SSH::EncryptFilter* enc; SSH::DecryptFilter* dec; + request_chain_.append ((enc = new SSH::EncryptFilter (SSH::ClientRole, (cdc2 ? SSH::SOURCE_ENCODED : 0)))); + response_chain_.prepend ((dec = new SSH::DecryptFilter ((cdc2 ? SSH::SOURCE_ENCODED : 0)))); + dec->set_encrypter (enc); + } + + request_chain_.append (new SinkFilter ("/wanproxy/request", sck2)); + + return true; +} + +void ProxyConnector::on_request_data (Event e) +{ + if (request_action_) + request_action_->cancel (), request_action_ = 0; + + switch (e.type_) + { + case Event::Done: + request_action_ = local_socket_->read (callback (this, &ProxyConnector::on_request_data)); + if (request_chain_.consume (e.buffer_)) + break; + case Event::EOS: + DEBUG(log_) << "Flushing request"; + request_chain_.flush (REQUEST_CHAIN_READY); + break; + default: + DEBUG(log_) << "Unexpected event: " << e; + conclude (); + return; + } +} + +void ProxyConnector::on_response_data (Event e) +{ + if (response_action_) + response_action_->cancel (), response_action_ = 0; + + switch (e.type_) + { + case Event::Done: + response_action_ = remote_socket_->read (callback (this, &ProxyConnector::on_response_data)); + if (response_chain_.consume (e.buffer_)) + break; + case Event::EOS: + DEBUG(log_) << "Flushing response"; + response_chain_.flush (RESPONSE_CHAIN_READY); + break; + default: + DEBUG(log_) << "Unexpected event: " << e; + conclude (); + return; + } +} + +void ProxyConnector::flush (int flg) +{ + chain_ready_ |= flg; + if ((chain_ready_ & REQUEST_CHAIN_READY) && (chain_ready_ & RESPONSE_CHAIN_READY)) + conclude (); +} + +void ProxyConnector::conclude () +{ + delete this; +} diff --git a/programs/wanproxy/proxy_connector.h b/programs/wanproxy/proxy_connector.h new file mode 100644 index 0000000..d949b1a --- /dev/null +++ b/programs/wanproxy/proxy_connector.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_PROXY_CONNECTOR_H +#define PROGRAMS_WANPROXY_PROXY_CONNECTOR_H + +#define REQUEST_CHAIN_READY 0x10000 +#define RESPONSE_CHAIN_READY 0x20000 + +#include +#include +#include +#include +#include "wanproxy_codec.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: proxy_connector.h // +// Description: carries data between endpoints through a filter chain // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class ProxyConnector : public Filter +{ + LogHandle log_; + WANProxyCodec* interface_codec_; + WANProxyCodec* remote_codec_; + Socket* local_socket_; + Socket* remote_socket_; + bool is_cln_, is_ssh_; + FilterChain request_chain_; + FilterChain response_chain_; + Action* connect_action_; + Action* stop_action_; + Action* request_action_; + Action* response_action_; + int chain_ready_; + +public: + ProxyConnector (const std::string&, WANProxyCodec*, WANProxyCodec*, + Socket*, SocketAddressFamily, const std::string&, bool cln, bool ssh); + virtual ~ProxyConnector (); + + void connect_complete (Event e); + bool build_chains (WANProxyCodec* cdc1, WANProxyCodec* cdc2, Socket* sck1, Socket* sck2); + void on_request_data (Event e); + void on_response_data (Event e); + virtual void flush (int flg); + void conclude (); +}; + +#endif /* !PROGRAMS_WANPROXY_PROXY_CONNECTOR_H */ diff --git a/programs/wanproxy/proxy_listener.cc b/programs/wanproxy/proxy_listener.cc new file mode 100644 index 0000000..5f9efb0 --- /dev/null +++ b/programs/wanproxy/proxy_listener.cc @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include "proxy_connector.h" +#include "proxy_listener.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: proxy_listener.cc // +// Description: listens on a port spawning a connector for each client // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +ProxyListener::ProxyListener (const std::string& name, + WANProxyCodec* local_codec, + WANProxyCodec* remote_codec, + SocketAddressFamily local_family, + const std::string& local_address, + SocketAddressFamily remote_family, + const std::string& remote_address, + bool cln, bool ssh) + : log_("/wanproxy/proxy/" + name + "/listener"), + name_(name), + local_codec_(local_codec), + remote_codec_(remote_codec), + local_family_(local_family), + local_address_(local_address), + remote_family_(remote_family), + remote_address_(remote_address), + is_cln_(cln), + is_ssh_(ssh), + accept_action_(0), + stop_action_(0) +{ + launch_service (); +} + +ProxyListener::~ProxyListener () +{ + if (accept_action_) + accept_action_->cancel (); + if (stop_action_) + stop_action_->cancel (); + close (); +} + +void ProxyListener::launch_service () +{ + if (listen (local_family_, local_address_)) + { + accept_action_ = accept (callback (this, &ProxyListener::accept_complete)); + INFO(log_) << "Listening on: " << getsockname (); + } + else + { + HALT(log_) << "Unable to create listener."; + } +} + +void ProxyListener::refresh (const std::string& name, + WANProxyCodec* local_codec, + WANProxyCodec* remote_codec, + SocketAddressFamily local_family, + const std::string& local_address, + SocketAddressFamily remote_family, + const std::string& remote_address, + bool cln, bool ssh) +{ + bool relaunch = (local_address != local_address_); + bool redirect = (remote_address != remote_address_); + + name_ = name; + local_codec_ = local_codec; + remote_codec_ = remote_codec; + local_family_ = local_family; + local_address_ = local_address; + remote_family_ = remote_family; + remote_address_ = remote_address; + is_cln_ = cln; + is_ssh_ = ssh; + + if (relaunch) + { + if (accept_action_) + accept_action_->cancel (), accept_action_ = 0; + launch_service (); + } + + if (redirect) + { + INFO(log_) << "Peer address: " << remote_address_; + } +} + +void ProxyListener::accept_complete (Event e, Socket* sck) +{ + switch (e.type_) + { + case Event::Done: + DEBUG(log_) << "Accepted client: " << sck->getpeername (); + new ProxyConnector (name_, local_codec_, remote_codec_, sck, remote_family_, remote_address_, is_cln_, is_ssh_); + break; + case Event::Error: + ERROR(log_) << "Accept error: " << e; + break; + default: + ERROR(log_) << "Unexpected event: " << e; + break; + } +} + diff --git a/programs/wanproxy/proxy_listener.h b/programs/wanproxy/proxy_listener.h new file mode 100644 index 0000000..20d5e14 --- /dev/null +++ b/programs/wanproxy/proxy_listener.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_PROXY_LISTENER_H +#define PROGRAMS_WANPROXY_PROXY_LISTENER_H + +#include +#include +#include +#include "wanproxy_codec.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: proxy_listener.h // +// Description: listens on a port spawning a connector for each client // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class ProxyListener : public TCPServer +{ + LogHandle log_; + std::string name_; + WANProxyCodec* local_codec_; + WANProxyCodec* remote_codec_; + SocketAddressFamily local_family_; + std::string local_address_; + SocketAddressFamily remote_family_; + std::string remote_address_; + bool is_cln_, is_ssh_; + Action* accept_action_; + Action* stop_action_; + +public: + ProxyListener (const std::string&, WANProxyCodec*, WANProxyCodec*, SocketAddressFamily, const std::string&, + SocketAddressFamily, const std::string&, bool cln, bool ssh); + ~ProxyListener (); + + void launch_service (); + void refresh (const std::string&, WANProxyCodec*, WANProxyCodec*, SocketAddressFamily, const std::string&, + SocketAddressFamily, const std::string&, bool cln, bool ssh); + void accept_complete (Event e, Socket* client); +}; + +#endif /* !PROGRAMS_WANPROXY_PROXY_LISTENER_H */ diff --git a/programs/wanproxy/wanproxy.cc b/programs/wanproxy/wanproxy.cc new file mode 100644 index 0000000..9d04e6a --- /dev/null +++ b/programs/wanproxy/wanproxy.cc @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include "wanproxy.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy.cc // +// Description: session start and global application management // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#define PROGRAM_VERSION "3.0" + +WanProxyCore wanproxy; + +static void usage(void); + + +int main (int argc, char *argv[]) +{ + std::string configfile; + bool quiet, verbose; + int ch; + + quiet = verbose = false; + + INFO("/wanproxy") << "WANProxy MT " << PROGRAM_VERSION; + INFO("/wanproxy") << "Copyright (c) 2008-2013 WANProxy.org"; + INFO("/wanproxy") << "Copyright (c) 2013-2015 Bramfeld-Software"; + INFO("/wanproxy") << "All rights reserved."; + + while ((ch = getopt(argc, argv, "c:qv")) != -1) + { + switch (ch) + { + case 'c': + configfile = optarg; + break; + case 'q': + quiet = true; + break; + case 'v': + verbose = true; + break; + default: + usage(); + } + } + + if (configfile.empty ()) + usage(); + + if (quiet && verbose) + usage(); + + if (verbose) + Log::mask (".?", Log::Debug); + else if (quiet) + Log::mask (".?", Log::Error); + else + Log::mask (".?", Log::Info); + + if (! wanproxy.configure (configfile)) + { + ERROR("/wanproxy") << "Could not configure proxies."; + return (1); + } + + wanproxy.launch_listener (); + + event_system.run (); + + wanproxy.terminate (); + + return 0; +} + +static void usage(void) +{ + INFO("/wanproxy/usage") << "wanproxy [-q | -v] -c configfile"; + exit(1); +} diff --git a/programs/wanproxy/wanproxy.conf b/programs/wanproxy/wanproxy.conf new file mode 100644 index 0000000..c9d261f --- /dev/null +++ b/programs/wanproxy/wanproxy.conf @@ -0,0 +1,85 @@ +# Set log-mask before doing anything else so that there isn't a lot of noise. +# Uses an informative mask in non-release builds. Errors-only in release builds. +create log-mask catch-all +set catch-all.regex "^/" +set catch-all.mask INFO +activate catch-all + +# Set up codec instances. +create codec codec0 +set codec0.codec XCodec +set codec0.compressor zlib +set codec0.compressor_level 6 +activate codec0 + +create codec codec1 +set codec1.codec XCodec +set codec1.compressor zlib +set codec1.compressor_level 6 +activate codec1 + +# Set up interfaces. +create interface if0 +set if0.family IP +set if0.host "localhost" +set if0.port "3300" +activate if0 + +create interface if1 +set if1.family $if0.family +set if1.host $if0.host +set if1.port "3301" +activate if1 + +create interface if2 +set if2.family $if0.family +set if2.host $if0.host +set if2.port "3302" +activate if2 + +# And peers to connect to them. +create peer peer0 +set peer0.family $if1.family +set peer0.host $if1.host +set peer0.port $if1.port +activate peer0 + +create peer peer1 +set peer1.family $if2.family +set peer1.host $if2.host +set peer1.port $if2.port +activate peer1 + +# Set up proxy from the client that encodes. +create proxy proxy0 +set proxy0.type TCP-TCP +set proxy0.interface if0 +set proxy0.interface_codec None +set proxy0.peer peer0 +set proxy0.peer_codec codec0 +activate proxy0 + +# Which feeds into this, which decodes. +create proxy proxy1 +set proxy1.type TCP-TCP +set proxy1.interface if1 +set proxy1.interface_codec codec1 +set proxy1.peer peer1 +set proxy1.peer_codec None +activate proxy1 + +# Which feeds into this, which spawns connections from SOCKS. +create proxy-socks proxy-socks0 +set proxy-socks0.interface if2 +activate proxy-socks0 + +# And set up a monitoring interface on port 9900. +create interface if3 +set if3.family $if0.family +set if3.host $if0.host +set if3.port "9900" +activate if3 + +create monitor monitor0 +set monitor0.interface if3 +activate monitor0 diff --git a/programs/wanproxy/wanproxy.h b/programs/wanproxy/wanproxy.h new file mode 100644 index 0000000..0a4d271 --- /dev/null +++ b/programs/wanproxy/wanproxy.h @@ -0,0 +1,121 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy.h // +// Description: global data for the wanproxy application // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CORE_H +#define PROGRAMS_WANPROXY_WANPROXY_CORE_H + +#include +#include +#include +#include +#include +#include "wanproxy_codec.h" +#include "wanproxy_config.h" +#include "wanproxy_config_type_codec.h" +#include "proxy_listener.h" + + +struct WanProxyCore +{ +private: + std::string config_file_; + Action* reload_action_; + ProxyListener* listener_; + std::map caches_; + +public: + std::string proxy_name_; + bool proxy_client_; + bool proxy_secure_; + SocketAddressFamily local_protocol_; + std::string local_address_; + WANProxyCodec local_codec_; + SocketAddressFamily remote_protocol_; + std::string remote_address_; + WANProxyCodec remote_codec_; + WANProxyConfigCache cache_type_; + std::string cache_path_; + size_t cache_size_; + UUID cache_uuid_; + + WanProxyCore () + { + reload_action_ = 0; + listener_ = 0; + proxy_client_ = proxy_secure_ = false; local_protocol_ = remote_protocol_ = SocketAddressFamilyIP; + cache_type_ = WANProxyConfigCacheMemory; cache_size_ = 0; + } + + bool configure (const std::string& file) + { + WANProxyConfig config; + config_file_ = file; + if (reload_action_) + reload_action_->cancel (); + reload_action_ = event_system.register_interest (EventInterestReload, callback (this, &WanProxyCore::reload)); + return (! config_file_.empty () && config.configure (config_file_)); + } + + void launch_listener () + { + listener_ = new ProxyListener (proxy_name_, &local_codec_, &remote_codec_, local_protocol_, local_address_, + remote_protocol_, remote_address_, proxy_client_, proxy_secure_); + } + + void reload () + { + if (configure (config_file_) && listener_) + listener_->refresh (proxy_name_, &local_codec_, &remote_codec_, local_protocol_, local_address_, + remote_protocol_, remote_address_, proxy_client_, proxy_secure_); + else + INFO("wanproxy/core") << "Could not reconfigure proxies."; + } + + XCodecCache* add_cache (UUID uuid, size_t size) + { + XCodecCache* cache = 0; + switch (cache_type_) + { + case WANProxyConfigCacheMemory: + cache = new XCodecMemoryCache (uuid, size); + break; + case WANProxyConfigCacheCOSS: + cache = new XCodecCacheCOSS (uuid, cache_path_, size); + break; + } + ASSERT("/xcodec/cache", caches_.find(uuid) == caches_.end()); + if (cache) + caches_[uuid] = cache; + return cache; + } + + XCodecCache* find_cache (UUID uuid) + { + std::map::const_iterator it = caches_.find (uuid); + if (it != caches_.end ()) + return it->second; + return 0; + } + + void terminate () + { + if (reload_action_) + reload_action_->cancel (), reload_action_ = 0; + delete listener_; listener_ = 0; + std::map::iterator it; + for (it = caches_.begin(); it != caches_.end(); it++) + delete it->second; + caches_.clear (); + } +}; + +extern WanProxyCore wanproxy; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CORE_H */ diff --git a/programs/wanproxy/wanproxy_codec.h b/programs/wanproxy/wanproxy_codec.h new file mode 100644 index 0000000..c7cdb06 --- /dev/null +++ b/programs/wanproxy/wanproxy_codec.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CODEC_H +#define PROGRAMS_WANPROXY_WANPROXY_CODEC_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_codec.h // +// Description: control parameters for each connection endpoint // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class XCodecCache; + +struct WANProxyCodec { + std::string name_; + XCodecCache* xcache_; + bool compressor_; + char compressor_level_; + bool counting_; + intmax_t request_input_bytes_; + intmax_t request_output_bytes_; + intmax_t response_input_bytes_; + intmax_t response_output_bytes_; + + WANProxyCodec(void) + : name_(""), + xcache_(NULL), + compressor_(false), + compressor_level_(0), + counting_(false), + request_input_bytes_(0), + request_output_bytes_(0), + response_input_bytes_(0), + response_output_bytes_(0) + { } +}; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CODEC_H */ diff --git a/programs/wanproxy/wanproxy_config.cc b/programs/wanproxy/wanproxy_config.cc new file mode 100644 index 0000000..a404bfd --- /dev/null +++ b/programs/wanproxy/wanproxy_config.cc @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "wanproxy_config.h" +#include "wanproxy_config_class_codec.h" +#include "wanproxy_config_class_interface.h" +//#include "wanproxy_config_class_monitor.h" +#include "wanproxy_config_class_peer.h" +#include "wanproxy_config_class_proxy.h" +//#include "wanproxy_config_class_proxy_socks.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_config.cc // +// Description: high-level configuration parser // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +WANProxyConfig::WANProxyConfig(void) +: log_("/wanproxy/config"), + config_(NULL) +{ } + +WANProxyConfig::~WANProxyConfig() +{ + delete config_; +} + +bool +WANProxyConfig::parse(std::deque& tokens) +{ + std::string command = tokens.front(); + tokens.pop_front(); + + if (command == "activate") { + parse_activate(tokens); + } else if (command == "create") { + parse_create(tokens); + } else if (command == "set") { + parse_set(tokens); + } else { + tokens.clear(); + ERROR(log_) << "Unrecognized configuration command: " << command; + } + + return tokens.empty(); +} + +void +WANProxyConfig::parse_activate(std::deque& tokens) +{ + if (tokens.size() != 1) { + ERROR(log_) << "Wrong number of words in activate (" << tokens.size() << ")"; + return; + } + + if (!config_->activate(tokens[0])) { + ERROR(log_) << "Object (" << tokens[0] << ") activation failed."; + return; + } + tokens.clear(); +} + +void +WANProxyConfig::parse_create(std::deque& tokens) +{ + if (tokens.size() != 2) { + ERROR(log_) << "Wrong number of words in create (" << tokens.size() << ")"; + return; + } + + if (!config_->create(tokens[0], tokens[1])) { + ERROR(log_) << "Object (" << tokens[1] << ") could not be created."; + return; + } + tokens.clear(); +} + +void +WANProxyConfig::parse_set(std::deque& tokens) +{ + if (tokens.size() != 2) { + ERROR(log_) << "Wrong number of words in set (" << tokens.size() << ")"; + return; + } + + std::string::iterator dot = std::find(tokens[0].begin(), tokens[0].end(), '.'); + if (dot == tokens[0].end()) { + ERROR(log_) << "Identifier (" << tokens[0] << ") is not an object member identifier."; + return; + } + + std::string object(tokens[0].begin(), dot); + std::string member(dot + 1, tokens[0].end()); + if (object == "" || member == "") { + ERROR(log_) << "Object (" << object << ") or member name (" << member << ") is empty."; + return; + } + + if (!config_->set(object, member, tokens[1])) { + ERROR(log_) << "Set of object member (" << tokens[0] << ") failed."; + return; + } + tokens.clear(); +} + +bool +WANProxyConfig::configure(const std::string& name) +{ + if (config_ != NULL) { + ERROR(log_) << "WANProxy already configured."; + return (false); + } + + INFO(log_) << "Configuring WANProxy."; + + std::fstream in; + in.open(name.c_str(), std::ios::in); + + if (!in.good()) { + ERROR(log_) << "Could not open file: " << name; + return (false); + } + + config_ = new Config(); + config_->import(&config_class_log_mask); + config_->import(&wanproxy_config_class_codec); + config_->import(&wanproxy_config_class_interface); + //config_->import(&wanproxy_config_class_monitor); + config_->import(&wanproxy_config_class_peer); + config_->import(&wanproxy_config_class_proxy); + //config_->import(&wanproxy_config_class_proxy_socks); + + std::deque tokens; + + while (in.good()) { + std::string line; + std::getline(in, line); + + if (line[0] == '#' || line.empty()) + continue; + + std::istringstream is(line); + while (is.good()) { + std::string word; + is >> word; + if (word.empty()) + continue; + if (word[0] == '#') + break; + tokens.push_back(word); + } + if (tokens.empty()) + continue; + ASSERT(log_, !tokens.empty()); + if (!parse(tokens)) { + ERROR(log_) << "Error in configuration directive: " << line; + + delete config_; + config_ = NULL; + + return (false); + } + ASSERT(log_, tokens.empty()); + } + + return (true); +} diff --git a/programs/wanproxy/wanproxy_config.h b/programs/wanproxy/wanproxy_config.h new file mode 100644 index 0000000..c33e6b4 --- /dev/null +++ b/programs/wanproxy/wanproxy_config.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_config.h // +// Description: high-level configuration parser // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Config; + +class WANProxyConfig { + LogHandle log_; + Config *config_; +public: + WANProxyConfig(void); + ~WANProxyConfig(); + +private: + bool parse(std::deque&); + + void parse_activate(std::deque&); + void parse_create(std::deque&); + void parse_set(std::deque&); + +public: + bool configure(const std::string&); +}; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_H */ diff --git a/programs/wanproxy/wanproxy_config_class_codec.cc b/programs/wanproxy/wanproxy_config_class_codec.cc new file mode 100644 index 0000000..5c9cbe1 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_codec.cc @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "wanproxy_config_class_codec.h" +#include "wanproxy.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_config_class_codec.cc // +// Description: high-level parser for xcodec-related options // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +WANProxyConfigClassCodec wanproxy_config_class_codec; + +bool +WANProxyConfigClassCodec::Instance::activate(const ConfigObject *co) +{ + UUID uuid; + XCodecCache* cache; + + codec_.name_ = co->name_; + + switch (codec_type_) + { + case WANProxyConfigCodecXCodec: + /* + * Fetch UUID from permanent storage if there is any. + */ + if (cache_path_.empty()) + { + if (wanproxy.cache_uuid_.is_valid ()) + uuid = wanproxy.cache_uuid_; + else + uuid.generate(); + } + else + { + std::string uuid_path = cache_path_ + "/UUID"; + if (! uuid.from_file (uuid_path)) + { + uuid.generate(); + uuid.to_file (uuid_path); + } + } + + wanproxy.cache_type_ = cache_type_; + wanproxy.cache_path_ = cache_path_; + wanproxy.cache_size_ = local_size_; + wanproxy.cache_uuid_ = uuid; + + if (! (cache = wanproxy.find_cache (uuid))) + cache = wanproxy.add_cache (uuid, local_size_); + codec_.xcache_ = cache; + break; + case WANProxyConfigCodecNone: + codec_.xcache_ = 0; + break; + default: + ERROR("/wanproxy/config/codec") << "Invalid codec type."; + return (false); + } + + INFO("/wanproxy/config/cache/type") << cache_type_; + INFO("/wanproxy/config/cache/path") << cache_path_; + INFO("/wanproxy/config/cache/size") << local_size_; + INFO("/wanproxy/config/cache/uuid") << uuid; + + switch (compressor_) { + case WANProxyConfigCompressorZlib: + if (compressor_level_ < 0 || compressor_level_ > 9) { + ERROR("/wanproxy/config/codec") << "Compressor level must be in range 0..9 (inclusive.)"; + return (false); + } + + codec_.compressor_ = true; + codec_.compressor_level_ = (char) compressor_level_; + break; + case WANProxyConfigCompressorNone: + if (compressor_level_ > 0) { + ERROR("/wanproxy/config/codec") << "Compressor level set but no compressor."; + return (false); + } + + codec_.compressor_ = false; + codec_.compressor_level_ = 0; + break; + default: + ERROR("/wanproxy/config/codec") << "Invalid compressor type."; + return (false); + } + + codec_.counting_ = (byte_counts_ != 0); + + return (true); +} diff --git a/programs/wanproxy/wanproxy_config_class_codec.h b/programs/wanproxy/wanproxy_config_class_codec.h new file mode 100644 index 0000000..604cc7c --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_codec.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_CODEC_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_CODEC_H + +#include +#include +#include "wanproxy_codec.h" +#include "wanproxy_config_type_codec.h" +#include "wanproxy_config_type_compressor.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_config_class_codec.h // +// Description: high-level parser for xcodec-related options // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class WANProxyConfigClassCodec : public ConfigClass { + +public: + struct Instance : public ConfigClassInstance { + + WANProxyCodec codec_; + WANProxyConfigCodec codec_type_; + WANProxyConfigCompressor compressor_; + intmax_t compressor_level_; + intmax_t byte_counts_; + WANProxyConfigCache cache_type_; + std::string cache_path_; + intmax_t local_size_; + intmax_t remote_size_; + + Instance(void) + : codec_type_(WANProxyConfigCodecNone), + compressor_(WANProxyConfigCompressorNone), + compressor_level_(0), + byte_counts_(0), + cache_type_(WANProxyConfigCacheMemory), + local_size_(0), + remote_size_(0) + { + } + + bool activate(const ConfigObject *); + }; + + WANProxyConfigClassCodec(void) + : ConfigClass("codec", new ConstructorFactory) + { + add_member("codec", &wanproxy_config_type_codec, &Instance::codec_type_); + add_member("compressor", &wanproxy_config_type_compressor, &Instance::compressor_); + add_member("compressor_level", &config_type_int, &Instance::compressor_level_); + add_member("byte_counts", &config_type_int, &Instance::byte_counts_); + + add_member("cache", &wanproxy_config_type_cache, &Instance::cache_type_); + add_member("cache_path", &config_type_string, &Instance::cache_path_); + add_member("local_size", &config_type_int, &Instance::local_size_); + add_member("remote_size", &config_type_int, &Instance::remote_size_); + } + + ~WANProxyConfigClassCodec() + { } +}; + +extern WANProxyConfigClassCodec wanproxy_config_class_codec; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_CODEC_H */ diff --git a/programs/wanproxy/wanproxy_config_class_interface.cc b/programs/wanproxy/wanproxy_config_class_interface.cc new file mode 100644 index 0000000..bed2696 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_interface.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include "wanproxy_config_class_interface.h" + +WANProxyConfigClassInterface wanproxy_config_class_interface; diff --git a/programs/wanproxy/wanproxy_config_class_interface.h b/programs/wanproxy/wanproxy_config_class_interface.h new file mode 100644 index 0000000..07c1130 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_interface.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_INTERFACE_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_INTERFACE_H + +#include + +class WANProxyConfigClassInterface : public ConfigClassAddress { +public: + WANProxyConfigClassInterface(void) + : ConfigClassAddress("interface") + { } + + ~WANProxyConfigClassInterface() + { } +}; + +extern WANProxyConfigClassInterface wanproxy_config_class_interface; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_INTERFACE_H */ diff --git a/programs/wanproxy/wanproxy_config_class_peer.cc b/programs/wanproxy/wanproxy_config_class_peer.cc new file mode 100644 index 0000000..6003f07 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_peer.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include "wanproxy_config_class_peer.h" + +WANProxyConfigClassPeer wanproxy_config_class_peer; diff --git a/programs/wanproxy/wanproxy_config_class_peer.h b/programs/wanproxy/wanproxy_config_class_peer.h new file mode 100644 index 0000000..1633441 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_peer.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_PEER_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_PEER_H + +#include + +class WANProxyConfigClassPeer : public ConfigClassAddress { +public: + WANProxyConfigClassPeer(void) + : ConfigClassAddress("peer") + { } + + ~WANProxyConfigClassPeer() + { } +}; + +extern WANProxyConfigClassPeer wanproxy_config_class_peer; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_PEER_H */ diff --git a/programs/wanproxy/wanproxy_config_class_proxy.cc b/programs/wanproxy/wanproxy_config_class_proxy.cc new file mode 100644 index 0000000..128011d --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_proxy.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include + +#include + +#include "wanproxy_config_class_codec.h" +#include "wanproxy_config_class_interface.h" +#include "wanproxy_config_class_peer.h" +#include "wanproxy_config_class_proxy.h" +#include "wanproxy.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_config_class_proxy.cc // +// Description: high-level parser for the global proxy object // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +WANProxyConfigClassProxy wanproxy_config_class_proxy; + +bool +WANProxyConfigClassProxy::Instance::activate(const ConfigObject *co) +{ + WANProxyConfigClassInterface::Instance *interface = + dynamic_cast(interface_->instance_); + if (interface == NULL) + return (false); + + if (interface->host_ == "" || interface->port_ == "") + return (false); + + WANProxyCodec *interface_codec; + if (interface_codec_ != NULL) { + WANProxyConfigClassCodec::Instance *codec = + dynamic_cast(interface_codec_->instance_); + if (codec == NULL) + return (false); + interface_codec = &codec->codec_; + } else { + interface_codec = NULL; + } + + WANProxyConfigClassPeer::Instance *peer = + dynamic_cast(peer_->instance_); + if (peer == NULL) + return (false); + + if (peer->host_ == "" || peer->port_ == "") + return (false); + + WANProxyCodec *peer_codec; + if (peer_codec_ != NULL) { + WANProxyConfigClassCodec::Instance *codec = + dynamic_cast(peer_codec_->instance_); + if (codec == NULL) + return (false); + peer_codec = &codec->codec_; + } else { + peer_codec = NULL; + } + + if (role_ == WANProxyConfigProxyRoleUndefined && ! interface_codec && peer_codec) + role_ = WANProxyConfigProxyRoleClient; + + wanproxy.proxy_name_ = co->name_; + wanproxy.proxy_client_ = (role_ == WANProxyConfigProxyRoleClient); + wanproxy.proxy_secure_ = (type_ == WANProxyConfigProxyTypeSSHSSH); + wanproxy.local_protocol_ = interface->family_; + wanproxy.local_address_ = '[' + interface->host_ + ']' + ':' + interface->port_; + wanproxy.local_codec_ = (interface_codec ? *interface_codec : WANProxyCodec ()); + wanproxy.remote_protocol_ = peer->family_; + wanproxy.remote_address_ = '[' + peer->host_ + ']' + ':' + peer->port_; + wanproxy.remote_codec_ = (peer_codec ? *peer_codec : WANProxyCodec ()); + + return (true); +} diff --git a/programs/wanproxy/wanproxy_config_class_proxy.h b/programs/wanproxy/wanproxy_config_class_proxy.h new file mode 100644 index 0000000..915b386 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_class_proxy.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2009-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_PROXY_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_PROXY_H + +#include + +#include "wanproxy_config_type_proxy_type.h" +#include "wanproxy_config_type_proxy_role.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_config_class_proxy.h // +// Description: high-level parser for the global proxy object // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class WANProxyConfigClassProxy : public ConfigClass { + struct Instance : public ConfigClassInstance { + WANProxyConfigProxyType type_; + WANProxyConfigProxyRole role_; + ConfigObject *interface_; + ConfigObject *interface_codec_; + ConfigObject *peer_; + ConfigObject *peer_codec_; + + Instance(void) + : type_(WANProxyConfigProxyTypeTCPTCP), + role_(WANProxyConfigProxyRoleUndefined), + interface_(NULL), + interface_codec_(NULL), + peer_(NULL), + peer_codec_(NULL) + { } + + bool activate(const ConfigObject *); + }; +public: + WANProxyConfigClassProxy(void) + : ConfigClass("proxy", new ConstructorFactory) + { + add_member("type", &wanproxy_config_type_proxy_type, &Instance::type_); + add_member("role", &wanproxy_config_type_proxy_role, &Instance::role_); + add_member("interface", &config_type_pointer, &Instance::interface_); + add_member("interface_codec", &config_type_pointer, &Instance::interface_codec_); + add_member("peer", &config_type_pointer, &Instance::peer_); + add_member("peer_codec", &config_type_pointer, &Instance::peer_codec_); + } + + /* XXX So wrong. */ + ~WANProxyConfigClassProxy() + { } +}; + +extern WANProxyConfigClassProxy wanproxy_config_class_proxy; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_CLASS_PROXY_H */ diff --git a/programs/wanproxy/wanproxy_config_type_codec.cc b/programs/wanproxy/wanproxy_config_type_codec.cc new file mode 100644 index 0000000..9a7106e --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_codec.cc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "wanproxy_config_type_codec.h" + +static struct WANProxyConfigTypeCodec::Mapping wanproxy_config_type_codec_map[] = { + { "XCodec", WANProxyConfigCodecXCodec }, + { "None", WANProxyConfigCodecNone }, + { NULL, WANProxyConfigCodecNone } +}; + +WANProxyConfigTypeCodec + wanproxy_config_type_codec("codec", wanproxy_config_type_codec_map); + +static struct WANProxyConfigTypeCache::Mapping wanproxy_config_type_cache_map[] = { + { "Memory", WANProxyConfigCacheMemory }, + { "COSS", WANProxyConfigCacheCOSS }, + { NULL, WANProxyConfigCacheMemory } +}; + +WANProxyConfigTypeCache + wanproxy_config_type_cache("cache", wanproxy_config_type_cache_map); diff --git a/programs/wanproxy/wanproxy_config_type_codec.h b/programs/wanproxy/wanproxy_config_type_codec.h new file mode 100644 index 0000000..38460e2 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_codec.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_CODEC_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_CODEC_H + +#include + +enum WANProxyConfigCodec { + WANProxyConfigCodecNone, + WANProxyConfigCodecXCodec +}; + +typedef ConfigTypeEnum WANProxyConfigTypeCodec; + +extern WANProxyConfigTypeCodec wanproxy_config_type_codec; + +enum WANProxyConfigCache { + WANProxyConfigCacheMemory, + WANProxyConfigCacheCOSS +}; + +typedef ConfigTypeEnum WANProxyConfigTypeCache; + +extern WANProxyConfigTypeCache wanproxy_config_type_cache; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_CODEC_H */ + diff --git a/programs/wanproxy/wanproxy_config_type_compressor.cc b/programs/wanproxy/wanproxy_config_type_compressor.cc new file mode 100644 index 0000000..8c0dc39 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_compressor.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "wanproxy_config_type_compressor.h" + +static struct WANProxyConfigTypeCompressor::Mapping wanproxy_config_type_compressor_map[] = { + { "zlib", WANProxyConfigCompressorZlib }, + { "None", WANProxyConfigCompressorNone }, + { NULL, WANProxyConfigCompressorNone } +}; + +WANProxyConfigTypeCompressor + wanproxy_config_type_compressor("compressor", wanproxy_config_type_compressor_map); diff --git a/programs/wanproxy/wanproxy_config_type_compressor.h b/programs/wanproxy/wanproxy_config_type_compressor.h new file mode 100644 index 0000000..1333ece --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_compressor.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_COMPRESSOR_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_COMPRESSOR_H + +#include + +enum WANProxyConfigCompressor { + WANProxyConfigCompressorNone, + WANProxyConfigCompressorZlib +}; + +typedef ConfigTypeEnum WANProxyConfigTypeCompressor; + +extern WANProxyConfigTypeCompressor wanproxy_config_type_compressor; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_COMPRESSOR_H */ diff --git a/programs/wanproxy/wanproxy_config_type_proxy_role.cc b/programs/wanproxy/wanproxy_config_type_proxy_role.cc new file mode 100644 index 0000000..58c7c27 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_proxy_role.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "wanproxy_config_type_proxy_role.h" + +static struct WANProxyConfigTypeProxyRole::Mapping wanproxy_config_type_proxy_role_map[] = { + { "Client", WANProxyConfigProxyRoleClient }, + { "Server", WANProxyConfigProxyRoleServer }, + { NULL, WANProxyConfigProxyRoleUndefined } +}; + +WANProxyConfigTypeProxyRole + wanproxy_config_type_proxy_role ("proxy_role", wanproxy_config_type_proxy_role_map); diff --git a/programs/wanproxy/wanproxy_config_type_proxy_role.h b/programs/wanproxy/wanproxy_config_type_proxy_role.h new file mode 100644 index 0000000..9f02fbc --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_proxy_role.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_PROXY_ROLE_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_PROXY_ROLE_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: wanproxy_config_type_proxy_role.h // +// Description: a type to signal if the proxy is acting as a client // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +enum WANProxyConfigProxyRole { + WANProxyConfigProxyRoleUndefined, + WANProxyConfigProxyRoleClient, + WANProxyConfigProxyRoleServer +}; + +typedef ConfigTypeEnum WANProxyConfigTypeProxyRole; + +extern WANProxyConfigTypeProxyRole wanproxy_config_type_proxy_role; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_PROXY_ROLE_H */ diff --git a/programs/wanproxy/wanproxy_config_type_proxy_type.cc b/programs/wanproxy/wanproxy_config_type_proxy_type.cc new file mode 100644 index 0000000..235c8f1 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_proxy_type.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "wanproxy_config_type_proxy_type.h" + +static struct WANProxyConfigTypeProxyType::Mapping wanproxy_config_type_proxy_type_map[] = { + { "TCP", WANProxyConfigProxyTypeTCPTCP }, + { "TCP-TCP", WANProxyConfigProxyTypeTCPTCP }, + { "SSH", WANProxyConfigProxyTypeSSHSSH }, + { "SSH-SSH", WANProxyConfigProxyTypeSSHSSH }, + { NULL, WANProxyConfigProxyTypeTCPTCP } +}; + +WANProxyConfigTypeProxyType + wanproxy_config_type_proxy_type("proxy_type", wanproxy_config_type_proxy_type_map); diff --git a/programs/wanproxy/wanproxy_config_type_proxy_type.h b/programs/wanproxy/wanproxy_config_type_proxy_type.h new file mode 100644 index 0000000..7b05767 --- /dev/null +++ b/programs/wanproxy/wanproxy_config_type_proxy_type.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_PROXY_TYPE_H +#define PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_PROXY_TYPE_H + +#include + +enum WANProxyConfigProxyType { + WANProxyConfigProxyTypeTCPTCP, + WANProxyConfigProxyTypeSSHSSH, +}; + +typedef ConfigTypeEnum WANProxyConfigTypeProxyType; + +extern WANProxyConfigTypeProxyType wanproxy_config_type_proxy_type; + +#endif /* !PROGRAMS_WANPROXY_WANPROXY_CONFIG_TYPE_PROXY_TYPE_H */ diff --git a/ssh/Makefile b/ssh/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/ssh/TODO b/ssh/TODO new file mode 100644 index 0000000..5df8abb --- /dev/null +++ b/ssh/TODO @@ -0,0 +1,18 @@ +Everything has been sort-of hacked together to get something working. Lots to +do to make it not awful. +o) Make the send and receive processes more asynchronous. Really want to be + able to not have to do long, synchronous encryption operations, etc. +o) Make the Crypto* stuff less awful. It seems like the Method abstraction is + perhaps not worth it. +o) Do separate algorithm from instance in the SSH code. It's a mess right now, + what with the clone() methods. Yuck. +o) Make sure all those things created by new have appropriate deletes somewhere. +o) Likewise, go over all the RSA, DH, BIGNUM, etc., stuff and add frees. +o) Maybe don't ever generate random padding? +o) Some kind of server public key verification callback. +o) Figure out a naming scheme. Packet, payload, MACs, etc., all have lots of + different names. Hard because we sometimes need to have a Buffer and a + uint8_t array for the same data. But the code is hard to read due to all the + inconsistency and duplication. +o) Split up the key exchange input method. It's harder to check whether its + variables are used safely since they're in a big, combined lump. diff --git a/ssh/lib.mk b/ssh/lib.mk new file mode 100644 index 0000000..352b7a8 --- /dev/null +++ b/ssh/lib.mk @@ -0,0 +1,12 @@ +VPATH+= ${TOPDIR}/ssh + +SRCS+= ssh_algorithm_negotiation.cc +SRCS+= ssh_filter.cc +SRCS+= ssh_protocol.cc +SRCS+= ssh_session.cc + +SRCS+= ssh_compression.cc +SRCS+= ssh_encryption.cc +SRCS+= ssh_key_exchange.cc +SRCS+= ssh_mac.cc +SRCS+= ssh_server_host_key.cc diff --git a/ssh/ssh_algorithm_negotiation.cc b/ssh/ssh_algorithm_negotiation.cc new file mode 100644 index 0000000..fa0711e --- /dev/null +++ b/ssh/ssh_algorithm_negotiation.cc @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: ssh_algorithm_negotiation.cc // +// Description: SSH algorithm selection at session start // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace { + template + std::vector names(const std::list& list) + { + typename std::list::const_iterator it; + std::vector vec; + + for (it = list.begin(); it != list.end(); ++it) { + const T alg = *it; + vec.push_back(alg->name()); + } + return (vec); + } + + template + bool choose_algorithm(SSH::Role role, + T *chosenp, + std::list& algorithm_list, + Buffer *in, const std::string& type) + { + std::vector local_algorithms = names(algorithm_list); + std::vector remote_algorithms; + if (!SSH::NameList::decode(remote_algorithms, in)) { + ERROR("/ssh/algorithm/negotiation") << "Failed to decode " << type << " name-list."; + return (false); + } + + if (remote_algorithms.empty() && local_algorithms.empty()) { + DEBUG("/ssh/algorithm/negotiation") << "Neither client nor server has any preference in " << type << " algorithms."; + return (true); + } + + const std::vector *client_algorithms; + const std::vector *server_algorithms; + if (role == SSH::ClientRole) { + client_algorithms = &local_algorithms; + server_algorithms = &remote_algorithms; + } else { + client_algorithms = &remote_algorithms; + server_algorithms = &local_algorithms; + } + + std::vector::const_iterator it; + for (it = client_algorithms->begin(); + it != client_algorithms->end(); ++it) { + std::vector::const_iterator it2; + + for (it2 = server_algorithms->begin(); + it2 != server_algorithms->end(); ++it2) { + const Buffer& server = *it2; + if (!it->equal(&server)) + continue; + std::string algorithm; + it->extract(algorithm); + + typename std::list::const_iterator ait; + for (ait = algorithm_list.begin(); ait != algorithm_list.end(); ++ait) { + const T alg = *ait; + if (alg->name() != algorithm) + continue; + + *chosenp = alg->clone(); + + DEBUG("/ssh/algorithm/negotiation") << "Selected " << type << " algorithm " << algorithm; + return (true); + } + NOTREACHED("/ssh/algorithm/negotiation"); + } + } + + ERROR("/ssh/algorithm/negotiation") << "Failed to choose " << type << " algorithm."; + return (false); + } +} + +void +SSH::AlgorithmNegotiation::add_algorithms(void) +{ + SSH::KeyExchange::add_algorithms(session_); + if (session_->role_ == ClientRole) + SSH::ServerHostKey::add_client_algorithms(session_); + SSH::Encryption::add_algorithms(session_); + SSH::MAC::add_algorithms(session_); + add_algorithm(SSH::Compression::none()); + /* XXX Add languages? */ +} + +bool +SSH::AlgorithmNegotiation::input(Filter* sender, Buffer *in) +{ + Buffer packet; + + switch (in->peek()) { + case SSH::Message::KeyExchangeInitializationMessage: + session_->remote_kexinit(*in); + if (!choose_algorithms(in)) { + ERROR(log_) << "Unable to negotiate algorithms."; + return (false); + } + DEBUG(log_) << "Chose algorithms."; + + if (session_->role_ == ClientRole && session_->chosen_algorithms_.key_exchange_ != NULL) { + Buffer out; + if (!session_->chosen_algorithms_.key_exchange_->init(&out)) { + ERROR(log_) << "Could not start new key exchange."; + return (false); + } + if (!out.empty()) + sender->produce(out); + } + return (true); + case SSH::Message::NewKeysMessage: + packet.append(SSH::Message::NewKeysMessage); + sender->produce(packet); + + session_->activate_chosen(); + DEBUG(log_) << "Switched to new keys."; + return (true); + default: + DEBUG(log_) << "Unsupported algorithm negotiation message:" << std::endl << in->hexdump(); + return (false); + } +} + +bool +SSH::AlgorithmNegotiation::init(Buffer *out) +{ + ASSERT(log_, out->empty()); + + Buffer cookie; + if (!CryptoRandomMethod::default_method->generate(CryptoTypeRNG, 16, &cookie)) + return (false); + + out->append(SSH::Message::KeyExchangeInitializationMessage); + out->append(cookie); + SSH::NameList::encode(out, names(algorithms_.key_exchange_list_)); + SSH::NameList::encode(out, names(algorithms_.server_host_key_list_)); + SSH::NameList::encode(out, names(algorithms_.encryption_client_to_server_list_)); + SSH::NameList::encode(out, names(algorithms_.encryption_server_to_client_list_)); + SSH::NameList::encode(out, names(algorithms_.mac_client_to_server_list_)); + SSH::NameList::encode(out, names(algorithms_.mac_server_to_client_list_)); + SSH::NameList::encode(out, names(algorithms_.compression_client_to_server_list_)); + SSH::NameList::encode(out, names(algorithms_.compression_server_to_client_list_)); + SSH::NameList::encode(out, names(algorithms_.language_client_to_server_list_)); + SSH::NameList::encode(out, names(algorithms_.language_server_to_client_list_)); + out->append(SSH::Boolean::False); + uint32_t reserved(0); + out->append(&reserved); + + session_->local_kexinit(*out); + + return (true); +} + +bool +SSH::AlgorithmNegotiation::choose_algorithms(Buffer *in) +{ + in->skip(17); + + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.key_exchange_, algorithms_.key_exchange_list_, in, "Key Exchange")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_host_key_, algorithms_.server_host_key_list_, in, "Server Host Key")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.encryption_, algorithms_.encryption_client_to_server_list_, in, "Encryption (Client->Server)")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.encryption_, algorithms_.encryption_server_to_client_list_, in, "Encryption (Server->Client)")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.mac_, algorithms_.mac_client_to_server_list_, in, "MAC (Client->Server)")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.mac_, algorithms_.mac_server_to_client_list_, in, "MAC (Server->Client)")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.compression_, algorithms_.compression_client_to_server_list_, in, "Compression (Client->Server)")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.compression_, algorithms_.compression_server_to_client_list_, in, "Compression (Server->Client)")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.client_to_server_.language_, algorithms_.language_client_to_server_list_, in, "Language (Client->Server)")) + return (false); + if (!choose_algorithm(session_->role_, &session_->chosen_algorithms_.server_to_client_.language_, algorithms_.language_server_to_client_list_, in, "Language (Server->Client)")) + return (false); + + return (true); +} diff --git a/ssh/ssh_algorithm_negotiation.h b/ssh/ssh_algorithm_negotiation.h new file mode 100644 index 0000000..03c05f6 --- /dev/null +++ b/ssh/ssh_algorithm_negotiation.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_ALGORITHM_NEGOTIATION_H +#define SSH_SSH_ALGORITHM_NEGOTIATION_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: ssh_algorithm_negotiation.h // +// Description: SSH algorithm selection at session start // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Buffer; +class Filter; + +namespace SSH { + class Compression; + class Encryption; + class KeyExchange; + class Language; + class MAC; + class ServerHostKey; + struct Session; + + class AlgorithmNegotiation { + struct Algorithms { + std::list key_exchange_list_; + std::list server_host_key_list_; + std::list encryption_client_to_server_list_; + std::list encryption_server_to_client_list_; + std::list mac_client_to_server_list_; + std::list mac_server_to_client_list_; + std::list compression_client_to_server_list_; + std::list compression_server_to_client_list_; + std::list language_client_to_server_list_; + std::list language_server_to_client_list_; + + Algorithms(void) + : key_exchange_list_(), + server_host_key_list_(), + encryption_client_to_server_list_(), + encryption_server_to_client_list_(), + mac_client_to_server_list_(), + mac_server_to_client_list_(), + compression_client_to_server_list_(), + compression_server_to_client_list_(), + language_client_to_server_list_(), + language_server_to_client_list_() + { } + }; + + LogHandle log_; + Session *session_; + Algorithms algorithms_; + public: + AlgorithmNegotiation(Session *session) + : log_("/ssh/algorithm/negotiation"), + session_(session), + algorithms_() + { } + + /* XXX Add a variant that takes only server_host_key_list and fills in suitable defaults. */ + + ~AlgorithmNegotiation() + { } + + void add_algorithm(KeyExchange *key_exchange) + { + algorithms_.key_exchange_list_.push_back(key_exchange); + } + + void add_algorithm(ServerHostKey *server_host_key) + { + algorithms_.server_host_key_list_.push_back(server_host_key); + } + + void add_algorithm(Encryption *encryption) + { + algorithms_.encryption_client_to_server_list_.push_back(encryption); + algorithms_.encryption_server_to_client_list_.push_back(encryption); + } + + void add_algorithm(MAC *mac) + { + algorithms_.mac_client_to_server_list_.push_back(mac); + algorithms_.mac_server_to_client_list_.push_back(mac); + } + + void add_algorithm(Compression *compression) + { + algorithms_.compression_client_to_server_list_.push_back(compression); + algorithms_.compression_server_to_client_list_.push_back(compression); + } + + void add_algorithm(Language *language) + { + algorithms_.language_client_to_server_list_.push_back(language); + algorithms_.language_server_to_client_list_.push_back(language); + } + + void add_algorithms(void); + + bool input(Filter *, Buffer *); + bool init(Buffer *); + + private: + bool choose_algorithms(Buffer *); + }; +} + +#endif /* !SSH_SSH_ALGORITHM_NEGOTIATION_H */ diff --git a/ssh/ssh_compression.cc b/ssh/ssh_compression.cc new file mode 100644 index 0000000..5c38618 --- /dev/null +++ b/ssh/ssh_compression.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +namespace { + class NoneCompression : public SSH::Compression { + LogHandle log_; + public: + NoneCompression(void) + : SSH::Compression("none"), + log_("/ssh/compression/none") + { } + + ~NoneCompression() + { } + + Compression *clone(void) const + { + return (new NoneCompression(*this)); + } + + bool input(Buffer *) + { + ERROR(log_) << "Not yet implemented."; + return (false); + } + }; +} + +SSH::Compression * +SSH::Compression::none(void) +{ + return (new NoneCompression()); +} diff --git a/ssh/ssh_compression.h b/ssh/ssh_compression.h new file mode 100644 index 0000000..705ad9b --- /dev/null +++ b/ssh/ssh_compression.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_COMPRESSION_H +#define SSH_SSH_COMPRESSION_H + +class Buffer; + +namespace SSH { + class Compression { + std::string name_; + protected: + Compression(const std::string& xname) + : name_(xname) + { } + + public: + virtual ~Compression() + { } + + std::string name(void) const + { + return (name_); + } + + virtual Compression *clone(void) const = 0; + + virtual bool input(Buffer *) = 0; + + static Compression *none(void); + }; +} + +#endif /* !SSH_SSH_COMPRESSION_H */ diff --git a/ssh/ssh_encryption.cc b/ssh/ssh_encryption.cc new file mode 100644 index 0000000..922c9b0 --- /dev/null +++ b/ssh/ssh_encryption.cc @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +namespace { + struct ssh_encryption_algorithm { + const char *rfc4250_name_; + CryptoEncryption::Algorithm crypto_algorithm_; + CryptoEncryption::Mode crypto_mode_; + }; + + static const struct ssh_encryption_algorithm ssh_encryption_algorithms[] = { + { "aes128-ctr", CryptoEncryption::AES128, CryptoEncryption::CTR }, + { "aes128-cbc", CryptoEncryption::AES128, CryptoEncryption::CBC }, + { "aes192-ctr", CryptoEncryption::AES192, CryptoEncryption::CTR }, + { "aes192-cbc", CryptoEncryption::AES192, CryptoEncryption::CBC }, + { "aes256-ctr", CryptoEncryption::AES256, CryptoEncryption::CTR }, + { "aes256-cbc", CryptoEncryption::AES256, CryptoEncryption::CBC }, + { "blowfish-ctr", CryptoEncryption::Blowfish, CryptoEncryption::CTR }, + { "blowfish-cbc", CryptoEncryption::Blowfish, CryptoEncryption::CBC }, + { "3des-ctr", CryptoEncryption::TripleDES, CryptoEncryption::CTR }, + { "3des-cbc", CryptoEncryption::TripleDES, CryptoEncryption::CBC }, + { "cast128-cbc", CryptoEncryption::CAST, CryptoEncryption::CBC }, + { "idea-cbc", CryptoEncryption::IDEA, CryptoEncryption::CBC }, + { "arcfour", CryptoEncryption::RC4, CryptoEncryption::Stream}, + { NULL, CryptoEncryption::AES128, CryptoEncryption::CBC } + }; + + class CryptoSSHEncryption : public SSH::Encryption { + LogHandle log_; + CryptoEncryption::Session *session_; + public: + CryptoSSHEncryption(const std::string& xname, CryptoEncryption::Session *session) + : SSH::Encryption(xname, session->block_size(), session->key_size(), session->iv_size()), + log_("/ssh/encryption/crypto/" + xname), + session_(session) + { } + + ~CryptoSSHEncryption() + { } + + Encryption *clone(void) const + { + return (new CryptoSSHEncryption(name_, session_->clone())); + } + + bool initialize(CryptoEncryption::Operation operation, const Buffer *key, const Buffer *iv) + { + return (session_->initialize(operation, key, iv)); + } + + bool cipher(Buffer *out, Buffer *in) + { + if (!session_->cipher(out, in)) { + in->clear(); + return (false); + } + in->clear(); + return (true); + } + }; +} + +void +SSH::Encryption::add_algorithms(Session *session) +{ + const struct ssh_encryption_algorithm *alg; + + for (alg = ssh_encryption_algorithms; alg->rfc4250_name_ != NULL; alg++) { + Encryption *encryption = cipher(CryptoEncryption::Cipher(alg->crypto_algorithm_, alg->crypto_mode_)); + if (encryption == NULL) + continue; + session->algorithm_negotiation_->add_algorithm(encryption); + } +} + +SSH::Encryption * +SSH::Encryption::cipher(CryptoEncryption::Cipher cipher) +{ + const struct ssh_encryption_algorithm *alg; + + for (alg = ssh_encryption_algorithms; alg->rfc4250_name_ != NULL; alg++) { + if (cipher.first != alg->crypto_algorithm_) + continue; + if (cipher.second != alg->crypto_mode_) + continue; + const CryptoEncryption::Method *method = CryptoEncryption::Method::method(cipher); + if (method == NULL) { + DEBUG("/ssh/encryption") << "Could not get method for cipher: " << cipher; + return (NULL); + } + CryptoEncryption::Session *session = method->session(cipher); + if (session == NULL) { + ERROR("/ssh/encryption") << "Could not get session for cipher: " << cipher; + return (NULL); + } + return (new CryptoSSHEncryption(alg->rfc4250_name_, session)); + } + DEBUG("/ssh/encryption") << "No SSH encryption support is available for cipher: " << cipher; + return (NULL); +} diff --git a/ssh/ssh_encryption.h b/ssh/ssh_encryption.h new file mode 100644 index 0000000..85525fa --- /dev/null +++ b/ssh/ssh_encryption.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_ENCRYPTION_H +#define SSH_SSH_ENCRYPTION_H + +#include + +namespace SSH { + struct Session; + + class Encryption { + protected: + const std::string name_; + const unsigned block_size_; + const unsigned key_size_; + const unsigned iv_size_; + + Encryption(const std::string& xname, unsigned xblock_size, unsigned xkey_size, unsigned xiv_size) + : name_(xname), + block_size_(xblock_size), + key_size_(xkey_size), + iv_size_(xiv_size) + { } + + public: + virtual ~Encryption() + { } + + unsigned block_size(void) const + { + return (block_size_); + } + + unsigned key_size(void) const + { + return (key_size_); + } + + unsigned iv_size(void) const + { + return (iv_size_); + } + + std::string name(void) const + { + return (name_); + } + + virtual Encryption *clone(void) const = 0; + + virtual bool initialize(CryptoEncryption::Operation, const Buffer *, const Buffer *) = 0; + virtual bool cipher(Buffer *, Buffer *) = 0; + + static void add_algorithms(Session *); + static Encryption *cipher(CryptoEncryption::Cipher); + }; +} + +#endif /* !SSH_SSH_ENCRYPTION_H */ diff --git a/ssh/ssh_filter.cc b/ssh/ssh_filter.cc new file mode 100644 index 0000000..783fba2 --- /dev/null +++ b/ssh/ssh_filter.cc @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssh_filter.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: ssh_filter.cc // +// Description: SSH encryption/decryption inside a data filter pair // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +namespace +{ + static const uint8_t SSHStreamPacket = 0xff; + static uint8_t zero_padding[255]; +} + +// Encrypt + +SSH::EncryptFilter::EncryptFilter (SSH::Role role, int flg) : BufferedFilter ("/ssh/encrypt"), session_ (role) +{ + encoded_ = (flg & SOURCE_ENCODED) != 0; + negotiated_ = false; + + session_.algorithm_negotiation_ = new SSH::AlgorithmNegotiation (&session_); + if (session_.role_ == SSH::ServerRole) + session_.algorithm_negotiation_->add_algorithm (SSH::ServerHostKey::server (&session_, "ssh-server1.pem")); + session_.algorithm_negotiation_->add_algorithms (); + + Buffer str ("SSH-2.0-WANProxy " + (std::string)log_); + session_.local_version (str); + str.append ("\r\n"); + Filter::produce (str); +} + +bool SSH::EncryptFilter::consume (Buffer& buf) +{ + buf.moveout (&pending_); + + if (negotiated_) + { + /* + * If we're writing data that has been encoded, we need to tag it. + */ + if (encoded_) + { + Buffer packet; + packet.append (SSHStreamPacket); + pending_.moveout (&packet); + return produce (packet); + } + else + { + uint32_t length; + while (pending_.length () > sizeof length) + { + pending_.extract (&length); + length = BigEndian::decode (length); + if (pending_.length () < sizeof length + length) + { + DEBUG(log_) << "Waiting for more write data."; + return true; + } + + Buffer packet; + pending_.moveout (&packet, sizeof length, length); + if (! produce (packet)) + return false; + } + } + } + + return true; +} + +bool SSH::EncryptFilter::produce (Buffer& buf) +{ + Encryption *encryption_algorithm; + MAC *mac_algorithm; + Buffer packet; + uint8_t padding_len; + uint32_t packet_len; + unsigned block_size; + Buffer mac; + + encryption_algorithm = session_.active_algorithms_.local_to_remote_->encryption_; + if (encryption_algorithm) + { + block_size = encryption_algorithm->block_size(); + if (block_size < 8) + block_size = 8; + } + else + block_size = 8; + mac_algorithm = session_.active_algorithms_.local_to_remote_->mac_; + + packet_len = sizeof padding_len + buf.length(); + padding_len = 4 + (block_size - ((sizeof packet_len + packet_len + 4) % block_size)); + packet_len += padding_len; + + BigEndian::append (&packet, packet_len); + packet.append (padding_len); + buf.moveout (&packet); + packet.append (zero_padding, padding_len); + + if (mac_algorithm) + { + Buffer mac_input; + + SSH::UInt32::encode (&mac_input, session_.local_sequence_number_); + mac_input.append (&packet); + + if (! mac_algorithm->mac (&mac, &mac_input)) + { + ERROR(log_) << "Could not compute outgoing MAC."; + return false; + } + } + + if (encryption_algorithm) + { + Buffer ciphertext; + if (! encryption_algorithm->cipher (&ciphertext, &packet)) + { + ERROR(log_) << "Could not encrypt outgoing packet."; + return false; + } + packet = ciphertext; + } + if (! mac.empty ()) + packet.append (mac); + + session_.local_sequence_number_++; + + return Filter::produce (packet); +} + +void SSH::EncryptFilter::flush (int flg) +{ + if (flg == ALGORITHM_NEGOTIATED) + { + negotiated_ = true; + Buffer bfr; + if (! pending_.empty ()) + consume (bfr); + } + else + { + flushing_ = true; + flush_flags_ |= flg; + } + if (flushing_ && negotiated_) + Filter::flush (flush_flags_); +} + +// Decrypt + +SSH::DecryptFilter::DecryptFilter (int flg) : LogisticFilter ("/ssh/decrypt") +{ + session_ = 0; + encoded_ = (flg & SOURCE_ENCODED) != 0; + identified_ = false; +} + +bool SSH::DecryptFilter::consume (Buffer& buf) +{ + buf.moveout (&pending_); + + if (! identified_) + { + HTTPProtocol::ParseStatus status; + + while (! pending_.empty ()) + { + Buffer line; + status = HTTPProtocol::ExtractLine (&line, &pending_); + switch (status) + { + case HTTPProtocol::ParseSuccess: + break; + case HTTPProtocol::ParseFailure: + ERROR(log_) << "Invalid line while waiting for identification string."; + return false; + case HTTPProtocol::ParseIncomplete: + /* Wait for more. */ + return true; + } + + if (! line.prefix ("SSH-")) + continue; /* Next line. */ + + if (! line.prefix ("SSH-2.0")) + { + ERROR(log_) << "Unsupported version."; + return false; + } + + if (session_ && session_->algorithm_negotiation_ && upstream_) + { + session_->remote_version (line); + Buffer packet; + if (session_->algorithm_negotiation_->init (&packet)) + { + upstream_->produce (packet); + identified_ = true; + break; + } + + } + + return false; + } + + if (! identified_) + return true; + } + + while (! pending_.empty ()) + { + Encryption *encryption_algorithm; + MAC *mac_algorithm; + Buffer packet; + Buffer mac; + unsigned block_size; + unsigned mac_size; + uint32_t packet_len; + uint8_t padding_len; + uint8_t msg; + + encryption_algorithm = session_->active_algorithms_.remote_to_local_->encryption_; + if (encryption_algorithm) + { + block_size = encryption_algorithm->block_size(); + if (block_size < 8) + block_size = 8; + } + else + block_size = 8; + mac_algorithm = session_->active_algorithms_.remote_to_local_->mac_; + if (mac_algorithm) + mac_size = mac_algorithm->size(); + else + mac_size = 0; + + if (pending_.length() <= block_size) + { + DEBUG(log_) << "Waiting for first block of packet."; + return true; + } + + if (encryption_algorithm) + { + if (first_block_.empty ()) + { + Buffer block; + pending_.moveout (&block, block_size); + if (! encryption_algorithm->cipher (&first_block_, &block)) + { + ERROR(log_) << "Decryption of first block failed."; + return false; + } + } + BigEndian::extract (&packet_len, &first_block_); + } + else + { + BigEndian::extract (&packet_len, &pending_); + } + + if (packet_len == 0) + { + ERROR(log_) << "Need to handle 0-length packet."; + return false; + } + + if (encryption_algorithm) + { + ASSERT(log_, !first_block_.empty()); + if (block_size + pending_.length() < sizeof packet_len + packet_len + mac_size) + { + DEBUG(log_) << "Need " << sizeof packet_len + packet_len + mac_size << " bytes to decrypt encrypted packet; have " << (block_size + pending_.length()) << "."; + return true; + } + + first_block_.moveout (&packet); + + if (sizeof packet_len + packet_len > block_size) + { + Buffer ciphertext; + pending_.moveout (&ciphertext, sizeof packet_len + packet_len - block_size); + if (! encryption_algorithm->cipher (&packet, &ciphertext)) + { + ERROR(log_) << "Decryption of packet failed."; + return false; + } + } + else + { + DEBUG(log_) << "Packet of exactly one block."; + } + ASSERT(log_, packet.length() == sizeof packet_len + packet_len); + } + else + { + if (pending_.length() < sizeof packet_len + packet_len + mac_size) + { + DEBUG(log_) << "Need " << sizeof packet_len + packet_len + mac_size << " bytes; have " << pending_.length() << "."; + return true; + } + + pending_.moveout (&packet, sizeof packet_len + packet_len); + } + + if (mac_algorithm) + { + Buffer expected_mac; + Buffer mac_input; + + pending_.moveout (&mac, 0, mac_size); + SSH::UInt32::encode (&mac_input, session_->remote_sequence_number_); + mac_input.append (packet); + if (! mac_algorithm->mac (&expected_mac, &mac_input)) + { + ERROR(log_) << "Could not compute expected MAC."; + return false; + } + if (! expected_mac.equal (&mac)) + { + ERROR(log_) << "Received MAC does not match expected MAC."; + return false; + } + } + packet.skip (sizeof packet_len); + + session_->remote_sequence_number_++; + + padding_len = packet.pop(); + if (padding_len != 0) + { + if (packet.length() < padding_len) + { + ERROR(log_) << "Padding too large for packet."; + return false; + } + packet.trim (padding_len); + } + + if (packet.empty()) + { + ERROR(log_) << "Need to handle empty packet."; + return false; + } + + /* + * Pass by range to registered handlers for each range. + * Unhandled messages go to the receive_callback_, and + * the caller can register key exchange mechanisms, + * and handle (or discard) whatever they don't handle. + * + * NB: The caller could do all this, but it's assumed + * that they usually have better things to do. If + * they register no handlers, they can certainly do + * so by hand. + * + * XXX It seems like having a separate class which handles + * all these details and algorithm negotiation would be + * nice, and to have this one be a bit more oriented + * towards managing just the transport layer. + * + * At the very least, it needs to take responsibility + * for its failures and allow the handler functions + * here to mangle the packet buffer rather than trying + * to send it on to the receiver if decoding fails. + * A decoding failure should result in a disconnect, + * an error. + */ + msg = packet.peek(); + if (msg >= SSH::Message::TransportRangeBegin && + msg <= SSH::Message::TransportRangeEnd) + { + DEBUG(log_) << "Using default handler for transport message."; + } + else if (msg >= SSH::Message::AlgorithmNegotiationRangeBegin && + msg <= SSH::Message::AlgorithmNegotiationRangeEnd) + { + if (session_->algorithm_negotiation_) + { + if (session_->algorithm_negotiation_->input (upstream_, &packet)) + continue; + ERROR(log_) << "Algorithm negotiation message failed."; + return false; + } + DEBUG(log_) << "Using default handler for algorithm negotiation message."; + } + else if (msg >= SSH::Message::KeyExchangeMethodRangeBegin && + msg <= SSH::Message::KeyExchangeMethodRangeEnd) + { + if (session_->chosen_algorithms_.key_exchange_) + { + if (session_->chosen_algorithms_.key_exchange_->input (upstream_, &packet)) + continue; + ERROR(log_) << "Key exchange message failed."; + return false; + } + DEBUG(log_) << "Using default handler for key exchange method message."; + } + else if (msg >= SSH::Message::UserAuthenticationGenericRangeBegin && + msg <= SSH::Message::UserAuthenticationGenericRangeEnd) + { + DEBUG(log_) << "Using default handler for generic user authentication message."; + } + else if (msg >= SSH::Message::UserAuthenticationMethodRangeBegin && + msg <= SSH::Message::UserAuthenticationMethodRangeEnd) + { + DEBUG(log_) << "Using default handler for user authentication method message."; + } + else if (msg >= SSH::Message::ConnectionProtocolGlobalRangeBegin && + msg <= SSH::Message::ConnectionProtocolGlobalRangeEnd) + { + DEBUG(log_) << "Using default handler for generic connection protocol message."; + } + else if (msg >= SSH::Message::ConnectionChannelRangeBegin && + msg <= SSH::Message::ConnectionChannelRangeEnd) + { + DEBUG(log_) << "Using default handler for connection channel message."; + } + else if (msg >= SSH::Message::ClientProtocolReservedRangeBegin && + msg <= SSH::Message::ClientProtocolReservedRangeEnd) + { + DEBUG(log_) << "Using default handler for client protocol message."; + } + else if (msg >= SSH::Message::LocalExtensionRangeBegin) + { + /* Because msg is a uint8_t, it will always be <= SSH::Message::LocalExtensionRangeEnd. */ + DEBUG(log_) << "Using default handler for local extension message."; + } + else + { + ASSERT(log_, msg == 0); + ERROR(log_) << "Message outside of protocol range received. Passing to default handler, but not expecting much."; + } + + /* + * If we're reading data that has been encoded, we need to untag it. + * Otherwise we need to frame it. + */ + if (encoded_) + { + if (packet.peek () != SSHStreamPacket || packet.length() == 1) + { + ERROR(log_) << "Got encoded packet with wrong message."; + return false; + } + packet.skip (1); + } + else + { + uint32_t length = packet.length (); + length = BigEndian::encode (length); + + Buffer b; + b.append (&length); + packet.moveout (&b); + packet = b; + } + + return produce (packet); + } + + return true; +} + +void SSH::DecryptFilter::flush (int flg) +{ + Buffer bfr; + if (! pending_.empty ()) + consume (bfr); + Filter::flush (flg); +} + diff --git a/ssh/ssh_filter.h b/ssh/ssh_filter.h new file mode 100644 index 0000000..aa4abd2 --- /dev/null +++ b/ssh/ssh_filter.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: ssh_filter.h // +// Description: SSH encryption/decryption inside a data filter pair // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef SSH_ENCRYPT_FILTER_H +#define SSH_ENCRYPT_FILTER_H + +#include +#include + +namespace SSH +{ + const int SOURCE_ENCODED = 0x01; + const int ALGORITHM_NEGOTIATED = 0x2C; + + class EncryptFilter : public BufferedFilter + { + private: + Session session_; + bool encoded_, negotiated_; + + public: + EncryptFilter (Role role, int flg = 0); + + virtual bool consume (Buffer& buf); + virtual bool produce (Buffer& buf); + virtual void flush (int flg); + + Session* current_session () { return &session_; } + }; + + class DecryptFilter : public LogisticFilter + { + private: + Buffer first_block_; + Session* session_; + bool encoded_, identified_; + + public: + DecryptFilter (int flg = 0); + + virtual bool consume (Buffer& buf); + virtual void flush (int flg); + + void set_encrypter (EncryptFilter* f) { session_ = (f ? f->current_session () : 0); set_upstream (f); } + }; +} + +#endif /* !SSH_ENCRYPT_FILTER_H */ diff --git a/ssh/ssh_key_exchange.cc b/ssh/ssh_key_exchange.cc new file mode 100644 index 0000000..1747c5d --- /dev/null +++ b/ssh/ssh_key_exchange.cc @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: ssh_key_exchange.cc // +// Description: SSH key check performed on filter session start // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#define DH_GROUP_MIN 1024 +#define DH_GROUP_MAX 8192 + +#define USE_TEST_GROUP + +namespace { +#ifdef USE_TEST_GROUP + static uint8_t test_prime_and_generator[] = { + 0x00, 0x00, 0x00, 0x81, 0x00, 0xe3, 0x1d, 0xfe, 0x85, 0x59, 0x9b, 0xcb, 0x5c, 0x2b, 0xbe, 0xcf, + 0x20, 0x1f, 0x5f, 0x49, 0xf1, 0xea, 0x31, 0x07, 0x7d, 0xa9, 0x26, 0xcb, 0x31, 0x03, 0x9d, 0x82, + 0x33, 0x2f, 0xed, 0x67, 0xa3, 0xa9, 0xb1, 0xc9, 0xe6, 0x34, 0x6c, 0xd7, 0xb5, 0x1a, 0x0a, 0x94, + 0x11, 0xa7, 0xd9, 0x26, 0xff, 0x0e, 0x8d, 0x72, 0xc1, 0x7b, 0x53, 0x9a, 0x13, 0x78, 0x7e, 0x16, + 0x38, 0x74, 0x7c, 0xb2, 0xdc, 0x60, 0x2c, 0x8c, 0xe8, 0x31, 0xf8, 0xd9, 0x7b, 0xac, 0xa6, 0x71, + 0xee, 0x61, 0x0c, 0x1a, 0xa4, 0x2f, 0x47, 0x2f, 0xe2, 0x22, 0xbd, 0x01, 0xe5, 0x25, 0xb6, 0x95, + 0xda, 0x3f, 0xf7, 0x03, 0xf4, 0x0e, 0xd6, 0x8c, 0xbb, 0x69, 0x1d, 0xcb, 0xd1, 0xe2, 0x60, 0xdb, + 0xf5, 0x0b, 0x85, 0x98, 0xe6, 0x17, 0xbe, 0x29, 0x4e, 0xa7, 0x90, 0x11, 0xac, 0xbc, 0xa5, 0x3e, + 0x05, 0xfe, 0xe9, 0x56, 0x93, 0x00, 0x00, 0x00, 0x01, 0x02 + }; +#endif + + static const uint8_t + DiffieHellmanGroupExchangeRequest = 34, + DiffieHellmanGroupExchangeGroup = 31, + DiffieHellmanGroupExchangeInitialize = 32, + DiffieHellmanGroupExchangeReply = 33; + + /* + * XXX + * Like a non-trivial amount of other code, this has been + * written a bit fast-and-loose. The usage of the dh_ and + * k_ in particularly are a bit dodgy and need to be freed + * in the destructor. + * + * Need to add assertions and frees. + */ + template + class DiffieHellmanGroupExchange : public SSH::KeyExchange { + LogHandle log_; + SSH::Session *session_; + DH *dh_; + Buffer key_exchange_; + BIGNUM *k_; + public: + DiffieHellmanGroupExchange(SSH::Session *session, const std::string& key_exchange_name) + : SSH::KeyExchange(key_exchange_name), + log_("/ssh/key_exchange/" + key_exchange_name), + session_(session), + dh_(NULL), + key_exchange_(), + k_() + { } + + ~DiffieHellmanGroupExchange() + { } + + KeyExchange *clone(void) const + { + return (new DiffieHellmanGroupExchange(session_, name_)); + } + + bool hash(Buffer *out, const Buffer *in) const + { + return (CryptoHash::hash(hash_algorithm, out, in)); + } + + bool input(Filter* sender, Buffer *in) + { + SSH::ServerHostKey *key; + uint32_t max, min, n; + BIGNUM *e, *f; + Buffer server_public_key; + Buffer signature; + Buffer packet; + Buffer group; + Buffer exchange_hash; + Buffer data; + Buffer initialize; + + switch (in->peek()) { + case DiffieHellmanGroupExchangeRequest: + if (session_->role_ != SSH::ServerRole) { + ERROR(log_) << "Received group exchange request as client."; + return (false); + } + in->skip(1); + key_exchange_ = *in; + if (!SSH::UInt32::decode(&min, in)) + return (false); + if (!SSH::UInt32::decode(&n, in)) + return (false); + if (!SSH::UInt32::decode(&max, in)) + return (false); + if (min < DH_GROUP_MIN) + min = DH_GROUP_MIN; + if (max > DH_GROUP_MAX) + max = DH_GROUP_MAX; + if (min > max) + return (false); + if (n < min) + n = min; + else if (n > max) + n = max; + +#ifdef USE_TEST_GROUP + group.append(test_prime_and_generator, sizeof test_prime_and_generator); + dh_ = DH_new(); + SSH::MPInt::decode(&dh_->p, &group); + SSH::MPInt::decode(&dh_->g, &group); + ASSERT(log_, group.empty()); +#else + DEBUG(log_) << "Doing DH_generate_parameters for " << n << " bits."; + ASSERT(log_, dh_ == NULL); + dh_ = DH_generate_parameters(n, 2, NULL, NULL); + if (dh_ == NULL) { + ERROR(log_) << "DH_generate_parameters failed."; + return (false); + } +#endif + + SSH::MPInt::encode(&group, dh_->p); + SSH::MPInt::encode(&group, dh_->g); + key_exchange_.append(group); + + packet.append(DiffieHellmanGroupExchangeGroup); + packet.append(group); + sender->produce(packet); + return (true); + case DiffieHellmanGroupExchangeGroup: + if (session_->role_ != SSH::ClientRole) { + ERROR(log_) << "Received DH group as server."; + return (false); + } + in->skip(1); + key_exchange_.append(in); + + dh_ = DH_new(); + if (dh_ == NULL) { + ERROR(log_) << "DH_new failed."; + return (false); + } + + if (!SSH::MPInt::decode(&dh_->p, in)) + return (false); + if (!SSH::MPInt::decode(&dh_->g, in)) + return (false); + + if (!DH_generate_key(dh_)) { + ERROR(log_) << "DH_generate_key failed."; + return (false); + } + e = dh_->pub_key; + + SSH::MPInt::encode(&initialize, e); + key_exchange_.append(initialize); + + packet.append(DiffieHellmanGroupExchangeInitialize); + packet.append(initialize); + sender->produce(packet); + return (true); + case DiffieHellmanGroupExchangeInitialize: + if (session_->role_ != SSH::ServerRole) { + ERROR(log_) << "Received group exchange initialization as client."; + return (false); + } + in->skip(1); + key_exchange_.append(in); + if (!SSH::MPInt::decode(&e, in)) + return (false); + + if (!DH_generate_key(dh_)) + return (false); + f = dh_->pub_key; + + SSH::MPInt::encode(&key_exchange_, f); + if (!exchange_finish(e)) { + ERROR(log_) << "Server key exchange finish failed."; + return (false); + } + + key = session_->chosen_algorithms_.server_host_key_; + if (!key->sign(&signature, &session_->exchange_hash_)) + return (false); + key->encode_public_key(&server_public_key); + + packet.append(DiffieHellmanGroupExchangeReply); + SSH::String::encode(&packet, server_public_key); + SSH::MPInt::encode(&packet, f); + SSH::String::encode(&packet, &signature); + sender->produce(packet); + + sender->flush(SSH::ALGORITHM_NEGOTIATED); + /* + * XXX + * Should send NEWKEYS. + */ + return (true); + case DiffieHellmanGroupExchangeReply: + if (session_->role_ != SSH::ClientRole) { + ERROR(log_) << "Received group exchange reply as client."; + return (false); + } + in->skip(1); + if (!SSH::String::decode(&server_public_key, in)) + return (false); + if (!SSH::MPInt::decode(&f, in)) + return (false); + if (!SSH::String::decode(&signature, in)) + return (false); + + key = session_->chosen_algorithms_.server_host_key_; + if (!key->decode_public_key(&server_public_key)) { + ERROR(log_) << "Could not decode server public key:" << std::endl << server_public_key.hexdump(); + return (false); + } + + SSH::MPInt::encode(&key_exchange_, f); + if (!exchange_finish(f)) { + ERROR(log_) << "Client key exchange finish failed."; + return (false); + } + + if (!key->verify(&signature, &session_->exchange_hash_)) { + ERROR(log_) << "Failed to verify exchange hash."; + return (false); + } + + sender->flush(SSH::ALGORITHM_NEGOTIATED); + /* + * XXX + * Should send NEWKEYS, but we're not ready for that yet. + * For now we just assume the peer will do it. How lazy, + * no? + */ + return (true); + default: + ERROR(log_) << "Not yet implemented."; + return (false); + } + } + + bool init(Buffer *out) + { + ASSERT(log_, out->empty()); + ASSERT(log_, session_->role_ == SSH::ClientRole); + + Buffer request; + SSH::UInt32::encode(&request, DH_GROUP_MIN); + SSH::UInt32::encode(&request, DH_GROUP_MIN); + SSH::UInt32::encode(&request, DH_GROUP_MAX); + + key_exchange_ = request; + + out->append(DiffieHellmanGroupExchangeRequest); + out->append(request); + + return (true); + } + + private: + bool exchange_finish(BIGNUM *remote_pubkey) + { + SSH::ServerHostKey *key; + Buffer server_public_key; + Buffer exchange_hash; + Buffer data; + + ASSERT(log_, dh_ != NULL); + + uint8_t secret[DH_size(dh_)]; + int secretlen = DH_compute_key(secret, remote_pubkey, dh_); + if (secretlen == -1) + return (false); + k_ = BN_bin2bn(secret, secretlen, NULL); + if (k_ == NULL) + return (false); + + key = session_->chosen_algorithms_.server_host_key_; + key->encode_public_key(&server_public_key); + + SSH::String::encode(&data, session_->client_version_); + SSH::String::encode(&data, session_->server_version_); + SSH::String::encode(&data, session_->client_kexinit_); + SSH::String::encode(&data, session_->server_kexinit_); + SSH::String::encode(&data, server_public_key); + data.append(key_exchange_); + SSH::MPInt::encode(&data, k_); + + if (!CryptoHash::hash(hash_algorithm, &exchange_hash, &data)) + return (false); + + session_->exchange_hash_ = exchange_hash; + SSH::MPInt::encode(&session_->shared_secret_, k_); + if (session_->session_id_.empty()) + session_->session_id_ = exchange_hash; + + return (true); + } + }; +} + +void +SSH::KeyExchange::add_algorithms(SSH::Session *session) +{ + session->algorithm_negotiation_->add_algorithm(new DiffieHellmanGroupExchange(session, "diffie-hellman-group-exchange-sha256")); + session->algorithm_negotiation_->add_algorithm(new DiffieHellmanGroupExchange(session, "diffie-hellman-group-exchange-sha1")); +} diff --git a/ssh/ssh_key_exchange.h b/ssh/ssh_key_exchange.h new file mode 100644 index 0000000..f9a0087 --- /dev/null +++ b/ssh/ssh_key_exchange.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_KEY_EXCHANGE_H +#define SSH_SSH_KEY_EXCHANGE_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: ssh_key_exchange.h // +// Description: SSH key check performed on filter session start // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class Buffer; +class Filter; + +namespace SSH { + struct Session; + + class KeyExchange { + protected: + const std::string name_; + + KeyExchange(const std::string& xname) + : name_(xname) + { } + + public: + virtual ~KeyExchange() + { } + + std::string name(void) const + { + return (name_); + } + + virtual KeyExchange *clone(void) const = 0; + + virtual bool hash(Buffer *, const Buffer *) const = 0; + + virtual bool input(Filter *, Buffer *) = 0; + + virtual bool init(Buffer *) = 0; + + static void add_algorithms(Session *); + }; +} + +#endif /* !SSH_SSH_KEY_EXCHANGE_H */ diff --git a/ssh/ssh_language.h b/ssh/ssh_language.h new file mode 100644 index 0000000..76397af --- /dev/null +++ b/ssh/ssh_language.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_LANGUAGE_H +#define SSH_SSH_LANGUAGE_H + +namespace SSH { + class Language { + std::string name_; + protected: + Language(const std::string& xname) + : name_(xname) + { } + + public: + virtual ~Language() + { } + + std::string name(void) const + { + return (name_); + } + + virtual Language *clone(void) const = 0; + + virtual bool input(Buffer *) = 0; + }; +} + +#endif /* !SSH_SSH_LANGUAGE_H */ diff --git a/ssh/ssh_mac.cc b/ssh/ssh_mac.cc new file mode 100644 index 0000000..25d78bb --- /dev/null +++ b/ssh/ssh_mac.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +namespace { + struct ssh_mac_algorithm { + const char *rfc4250_name_; + CryptoMAC::Algorithm crypto_algorithm_; + unsigned size_; + }; + + static const struct ssh_mac_algorithm ssh_mac_algorithms[] = { + { "hmac-sha1", CryptoMAC::SHA1, 0 }, + { "hmac-sha2-256", CryptoMAC::SHA256, 0 }, + { "hmac-sha2-512", CryptoMAC::SHA512, 0 }, + { "hmac-ripemd160", CryptoMAC::RIPEMD160, 0 }, + { "hmac-md5", CryptoMAC::MD5, 0 }, + { "hmac-sha1-96", CryptoMAC::SHA1, 12 }, + { "hmac-md5-96", CryptoMAC::MD5, 12 }, + { NULL, CryptoMAC::MD5, 0 } + }; + + class CryptoSSHMAC : public SSH::MAC { + LogHandle log_; + CryptoMAC::Instance *instance_; + public: + CryptoSSHMAC(const std::string& xname, CryptoMAC::Instance *instance, unsigned xsize) + : SSH::MAC(xname, xsize == 0 ? instance->size() : xsize, instance->size()), + log_("/ssh/mac/crypto/" + xname), + instance_(instance) + { } + + ~CryptoSSHMAC() + { } + + MAC *clone(void) const + { + return (new CryptoSSHMAC(name_, instance_->clone(), key_size_)); + } + + bool initialize(const Buffer *key) + { + return (instance_->initialize(key)); + } + + bool mac(Buffer *out, const Buffer *in) + { + return (instance_->mac(out, in)); + } + }; +} + +void +SSH::MAC::add_algorithms(Session *session) +{ + const struct ssh_mac_algorithm *alg; + + for (alg = ssh_mac_algorithms; alg->rfc4250_name_ != NULL; alg++) { + const CryptoMAC::Method *method = CryptoMAC::Method::method(alg->crypto_algorithm_); + if (method == NULL) { + DEBUG("/ssh/mac") << "Could not get method for algorithm: " << alg->crypto_algorithm_; + continue; + } + CryptoMAC::Instance *instance = method->instance(alg->crypto_algorithm_); + if (instance == NULL) { + DEBUG("/ssh/mac") << "Could not get instance for algorithm: " << alg->crypto_algorithm_; + continue; + } + session->algorithm_negotiation_->add_algorithm(new CryptoSSHMAC(alg->rfc4250_name_, instance, alg->size_)); + } +} + +SSH::MAC * +SSH::MAC::algorithm(CryptoMAC::Algorithm xalgorithm) +{ + const struct ssh_mac_algorithm *alg; + + for (alg = ssh_mac_algorithms; alg->rfc4250_name_ != NULL; alg++) { + if (xalgorithm != alg->crypto_algorithm_) + continue; + const CryptoMAC::Method *method = CryptoMAC::Method::method(xalgorithm); + if (method == NULL) { + ERROR("/ssh/mac") << "Could not get method for algorithm: " << xalgorithm; + return (NULL); + } + CryptoMAC::Instance *instance = method->instance(xalgorithm); + if (instance == NULL) { + ERROR("/ssh/mac") << "Could not get instance for algorithm: " << xalgorithm; + return (NULL); + } + return (new CryptoSSHMAC(alg->rfc4250_name_, instance, alg->size_)); + } + ERROR("/ssh/mac") << "No SSH MAC support is available for algorithm: " << xalgorithm; + return (NULL); +} diff --git a/ssh/ssh_mac.h b/ssh/ssh_mac.h new file mode 100644 index 0000000..5430288 --- /dev/null +++ b/ssh/ssh_mac.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_MAC_H +#define SSH_SSH_MAC_H + +#include + +namespace SSH { + struct Session; + + class MAC { + protected: + const std::string name_; + const unsigned size_; + const unsigned key_size_; + + MAC(const std::string& xname, unsigned xsize, unsigned xkey_size) + : name_(xname), + size_(xsize), + key_size_(xkey_size) + { } + + public: + virtual ~MAC() + { } + + std::string name(void) const + { + return (name_); + } + + unsigned size(void) const + { + return (size_); + } + + unsigned key_size(void) const + { + return (key_size_); + } + + virtual MAC *clone(void) const = 0; + + virtual bool initialize(const Buffer *) = 0; + virtual bool mac(Buffer *, const Buffer *) = 0; + + static void add_algorithms(Session *); + static MAC *algorithm(CryptoMAC::Algorithm); + }; +} + +#endif /* !SSH_SSH_MAC_H */ diff --git a/ssh/ssh_protocol.cc b/ssh/ssh_protocol.cc new file mode 100644 index 0000000..5fe2a28 --- /dev/null +++ b/ssh/ssh_protocol.cc @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +void +SSH::String::encode(Buffer *out, const Buffer& in) +{ + SSH::UInt32::encode(out, in.length()); + if (!in.empty()) + out->append(in); +} + +void +SSH::String::encode(Buffer *out, Buffer *in) +{ + SSH::String::encode(out, *in); + if (!in->empty()) + in->clear(); +} + +bool +SSH::String::decode(Buffer *out, Buffer *in) +{ + uint32_t len; + + if (in->length() < sizeof len) + return (false); + BigEndian::extract(&len, in); + if (len == 0) { + in->skip(sizeof len); + return (true); + } + + if (in->length() < sizeof len + len) + return (false); + + in->moveout(out, sizeof len, len); + return (true); +} + +void +SSH::UInt32::encode(Buffer *out, uint32_t in) +{ + BigEndian::append(out, in); +} + +bool +SSH::UInt32::decode(uint32_t *outp, Buffer *in) +{ + if (in->length() < sizeof *outp) + return (false); + BigEndian::extract(outp, in); + in->skip(sizeof *outp); + return (true); +} + +void +SSH::MPInt::encode(Buffer *out, const BIGNUM *in) +{ + if (BN_is_negative(in)) { + HALT("/ssh/mpint/encode") << "Negative numbers not yet implemented."; + } + uint8_t buf[BN_num_bytes(in)]; + BN_bn2bin(in, buf); + if ((buf[0] & 0x80) == 0x80) { + SSH::UInt32::encode(out, sizeof buf + 1); + out->append((uint8_t)0x00); + out->append(buf, sizeof buf); + } else { + SSH::UInt32::encode(out, sizeof buf); + out->append(buf, sizeof buf); + } +} + +bool +SSH::MPInt::decode(BIGNUM **outp, Buffer *in) +{ + uint32_t len; + BIGNUM *out; + + if (in->length() < sizeof len) + return (false); + BigEndian::extract(&len, in); + if (len == 0) { + out = BN_new(); + if (out == NULL) + return (false); + in->skip(sizeof len); + BN_zero(out); + *outp = out; + return (true); + } + + if (in->length() < sizeof len + len) + return (false); + + uint8_t buf[len]; + in->copyout(buf, sizeof len, len); + out = BN_bin2bn(buf, sizeof buf, NULL); + if (out == NULL) + return (false); + in->skip(sizeof len + len); + *outp = out; + return (true); +} + +void +SSH::NameList::encode(Buffer *out, const std::vector& in) +{ + Buffer merged; + + merged = Buffer::join(in, ","); + SSH::String::encode(out, &merged); +} + +bool +SSH::NameList::decode(std::vector& out, Buffer *in) +{ + Buffer merged; + + if (!SSH::String::decode(&merged, in)) + return (false); + + out = merged.split(','); + return (true); +} diff --git a/ssh/ssh_protocol.h b/ssh/ssh_protocol.h new file mode 100644 index 0000000..febf041 --- /dev/null +++ b/ssh/ssh_protocol.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_PROTOCOL_H +#define SSH_SSH_PROTOCOL_H + +#include + +/* XXX OpenSSL dependency. */ +typedef struct bignum_st BIGNUM; + +namespace SSH { + namespace Message { + static const uint8_t + TransportRangeBegin = 1, + TransportDisconnectMessage = 1, + TransportIgnoreMessage = 2, + TransportUnimplementedMessage = 3, + TransportDebugMessage = 4, + TransportServiceRequestMessage = 5, + TransportServiceAcceptMessage = 6, + TransportRangeEnd = 19, + + AlgorithmNegotiationRangeBegin = 20, + KeyExchangeInitializationMessage = 20, + NewKeysMessage = 21, + AlgorithmNegotiationRangeEnd = 29, + + KeyExchangeMethodRangeBegin = 30, + KeyExchangeMethodRangeEnd = 49, + + UserAuthenticationGenericRangeBegin = 50, + UserAuthenticationRequestMessage = 50, + UserAuthenticationFailureMessage = 51, + UserAuthenticationSuccessMessage = 52, + UserAuthenticationBannerMessage = 53, + UserAuthenticationGenericRangeEnd = 59, + + UserAuthenticationMethodRangeBegin = 60, + UserAuthenticationMethodRangeEnd = 79, + + ConnectionProtocolGlobalRangeBegin = 80, + ConnectionProtocolGlobalRequestMessage = 80, + ConnectionProtocolGlobalRequestSuccessMessage = 81, + ConnectionProtocolGlobalRequestFailureMessage = 82, + ConnectionProtocolGlobalRangeEnd = 89, + + ConnectionChannelRangeBegin = 90, + ConnectionChannelOpen = 90, + ConnectionChannelOpenConfirmation = 91, + ConnectionChannelOpenFailure = 92, + ConnectionChannelWindowAdjust = 93, + ConnectionChannelData = 94, + ConnectionChannelExtendedData = 95, + ConnectionChannelEndOfFile = 96, + ConnectionChannelClose = 97, + ConnectionChannelRequest = 98, + ConnectionChannelRequestSuccess = 99, + ConnectionChannelRequestFailure = 100, + ConnectionChannelRangeEnd = 127, + + ClientProtocolReservedRangeBegin = 128, + ClientProtocolReservedRangeEnd = 191, + + LocalExtensionRangeBegin = 192, + LocalExtensionRangeEnd = 255; + } + + namespace Boolean { + static const uint8_t + True = 1, + False = 0; + } + + namespace Disconnect { + static const uint8_t + HostNotallowedToConnect = 1, + ProtocolError = 2, + KeyExchangeFailed = 3, + Reserved = 4, + MACError = 5, + CompressionError = 6, + ServiceNotAvailable = 7, + ProtocolVersionNotSupported = 8, + HostKeyNotVerifiable = 9, + ConnectionLost = 10, + ByApplication = 11, + TooManyConnections = 12, + AuthenticationCancelledByUser = 13, + NoMoreAuthenticationMethodsAvailable = 14, + IllegalUserName = 15; + } + + namespace String { + void encode(Buffer *, const Buffer&); + void encode(Buffer *, Buffer *); + bool decode(Buffer *, Buffer *); + } + + namespace UInt32 { + void encode(Buffer *, uint32_t); + bool decode(uint32_t *, Buffer *); + } + + namespace MPInt { + void encode(Buffer *, const BIGNUM *); + bool decode(BIGNUM **, Buffer *); + } + + namespace NameList { + void encode(Buffer *, const std::vector&); + bool decode(std::vector&, Buffer *); + } +} + +#endif /* !SSH_SSH_PROTOCOL_H */ diff --git a/ssh/ssh_server_host_key.cc b/ssh/ssh_server_host_key.cc new file mode 100644 index 0000000..f3c8dca --- /dev/null +++ b/ssh/ssh_server_host_key.cc @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace { + class RSAServerHostKey : public SSH::ServerHostKey { + LogHandle log_; + SSH::Session *session_; + RSA *rsa_; + + public: + RSAServerHostKey(SSH::Session *session, RSA *rsa) + : SSH::ServerHostKey("ssh-rsa"), + log_("/ssh/serverhostkey/rsa"), + session_(session), + rsa_(rsa) + { + ASSERT(log_, rsa != NULL || session->role_ == SSH::ClientRole); + } + + ~RSAServerHostKey() + { } + + SSH::ServerHostKey *clone(void) const + { + if (session_->role_ == SSH::ClientRole) { + if (rsa_ != NULL) + return (new RSAServerHostKey(session_, RSAPublicKey_dup(rsa_))); + else + return (new RSAServerHostKey(session_, NULL)); + } else { + return (new RSAServerHostKey(session_, RSAPrivateKey_dup(rsa_))); + } + } + + bool decode_public_key(Buffer *in) + { + ASSERT(log_, session_->role_ == SSH::ClientRole); + ASSERT(log_, rsa_ == NULL); + Buffer tag; + if (!SSH::String::decode(&tag, in)) + return (false); + if (!tag.equal("ssh-rsa")) + return (false); + rsa_ = RSA_new(); + if (rsa_ == NULL) + return (false); + if (!SSH::MPInt::decode(&rsa_->e, in)) + return (false); + if (!SSH::MPInt::decode(&rsa_->n, in)) + return (false); + return (true); + } + + void encode_public_key(Buffer *out) const + { + SSH::String::encode(out, Buffer("ssh-rsa")); + SSH::MPInt::encode(out, rsa_->e); + SSH::MPInt::encode(out, rsa_->n); + } + + bool sign(Buffer *out, const Buffer *in) const + { + ASSERT(log_, session_->role_ == SSH::ServerRole); + + Buffer hash; + if (!CryptoHash::hash(CryptoHash::SHA1, &hash, in)) + return (false); + + uint8_t m[hash.length()]; + hash.moveout(m, sizeof m); + uint8_t signature[RSA_size(rsa_)]; + unsigned signature_length = sizeof signature; + int rv = RSA_sign(NID_sha1, m, sizeof m, signature, &signature_length, rsa_); + if (rv == 0) + return (false); + SSH::String::encode(out, Buffer("ssh-rsa")); + SSH::String::encode(out, Buffer(signature, signature_length)); + return (true); + } + + bool verify(const Buffer *signature, const Buffer *message) const + { + ASSERT(log_, session_->role_ == SSH::ClientRole); + + Buffer hash; + if (!CryptoHash::hash(CryptoHash::SHA1, &hash, message)) + return (false); + + Buffer in; + in.append(signature); + Buffer tag; + if (!SSH::String::decode(&tag, &in)) + return (false); + if (!tag.equal("ssh-rsa")) + return (false); + Buffer sig; + if (!SSH::String::decode(&sig, &in)) + return (false); + + uint8_t m[hash.length()]; + hash.moveout(m, sizeof m); + uint8_t sigbuf[sig.length()]; + sig.moveout(sigbuf, sig.length()); + int rv = RSA_verify(NID_sha1, m, sizeof m, sigbuf, sizeof sigbuf, rsa_); + if (rv == 0) + return (false); + return (true); + } + + static RSAServerHostKey *open(SSH::Session *session, FILE *file) + { + RSA *rsa; + + ASSERT("/ssh/serverhostkey/rsa/open", session->role_ == SSH::ServerRole); + + rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL); + if (rsa == NULL) + return (NULL); + + return (new RSAServerHostKey(session, rsa)); + } + }; +} + +void +SSH::ServerHostKey::add_client_algorithms(SSH::Session *session) +{ + ASSERT("/ssh/serverhostkey/client", session->role_ == SSH::ClientRole); + session->algorithm_negotiation_->add_algorithm(new RSAServerHostKey(session, NULL)); +} + +SSH::ServerHostKey * +SSH::ServerHostKey::server(SSH::Session *session, const std::string& keyfile) +{ + SSH::ServerHostKey *key; + FILE *file; + + ASSERT("/ssh/serverhostkey/server", session->role_ == SSH::ServerRole); + + file = fopen(keyfile.c_str(), "r"); + if (file == NULL) + return (NULL); + + key = RSAServerHostKey::open(session, file); + if (key == NULL) { + fclose(file); + return (NULL); + } + + fclose(file); + + return (key); +} diff --git a/ssh/ssh_server_host_key.h b/ssh/ssh_server_host_key.h new file mode 100644 index 0000000..85e3872 --- /dev/null +++ b/ssh/ssh_server_host_key.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_SERVER_HOST_KEY_H +#define SSH_SSH_SERVER_HOST_KEY_H + +class Buffer; + +namespace SSH { + struct Session; + + class ServerHostKey { + std::string name_; + protected: + ServerHostKey(const std::string& xname) + : name_(xname) + { } + + public: + virtual ~ServerHostKey() + { } + + std::string name(void) const + { + return (name_); + } + + virtual ServerHostKey *clone(void) const = 0; + + virtual bool decode_public_key(Buffer *) = 0; + virtual void encode_public_key(Buffer *) const = 0; + + virtual bool sign(Buffer *, const Buffer *) const = 0; + virtual bool verify(const Buffer *, const Buffer *) const = 0; + + static void add_client_algorithms(Session *); + static ServerHostKey *server(Session *, const std::string&); + }; +} + +#endif /* !SSH_SSH_SERVER_HOST_KEY_H */ diff --git a/ssh/ssh_session.cc b/ssh/ssh_session.cc new file mode 100644 index 0000000..acf7aac --- /dev/null +++ b/ssh/ssh_session.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +void +SSH::Session::activate_chosen(void) +{ + const Buffer *local_to_remote_iv_; + const Buffer *remote_to_local_iv_; + const Buffer *local_to_remote_key_; + const Buffer *remote_to_local_key_; + + /* + * XXX + * Need to free instances in active_algorithms_. + */ + + active_algorithms_ = chosen_algorithms_; + + if (active_algorithms_.client_to_server_.encryption_ != NULL) { + client_to_server_iv_ = generate_key("A", active_algorithms_.client_to_server_.encryption_->iv_size()); + client_to_server_key_ = generate_key("C", active_algorithms_.client_to_server_.encryption_->key_size()); + } + if (active_algorithms_.server_to_client_.encryption_ != NULL) { + server_to_client_iv_ = generate_key("B", active_algorithms_.server_to_client_.encryption_->iv_size()); + server_to_client_key_ = generate_key("D", active_algorithms_.server_to_client_.encryption_->key_size()); + } + if (active_algorithms_.client_to_server_.mac_ != NULL) { + client_to_server_integrity_key_ = generate_key("E", active_algorithms_.client_to_server_.mac_->key_size()); + if (!active_algorithms_.client_to_server_.mac_->initialize(&client_to_server_integrity_key_)) + HALT("/ssh/session") << "Failed to activate client-to-server MAC."; + } + if (active_algorithms_.server_to_client_.mac_ != NULL) { + server_to_client_integrity_key_ = generate_key("F", active_algorithms_.server_to_client_.mac_->key_size()); + if (!active_algorithms_.server_to_client_.mac_->initialize(&server_to_client_integrity_key_)) + HALT("/ssh/session") << "Failed to activate server-to-client MAC."; + } + + if (active_algorithms_.local_to_remote_->encryption_ != NULL) { + if (role_ == ClientRole) { + local_to_remote_iv_ = &client_to_server_iv_; + local_to_remote_key_ = &client_to_server_key_; + } else { + local_to_remote_iv_ = &server_to_client_iv_; + local_to_remote_key_ = &server_to_client_key_; + } + if (!active_algorithms_.local_to_remote_->encryption_->initialize(CryptoEncryption::Encrypt, local_to_remote_key_, local_to_remote_iv_)) + HALT("/ssh/session") << "Failed to initialize local-to-remote encryption."; + } + + if (active_algorithms_.remote_to_local_->encryption_ != NULL) { + if (role_ == ClientRole) { + remote_to_local_iv_ = &server_to_client_iv_; + remote_to_local_key_ = &server_to_client_key_; + } else { + remote_to_local_iv_ = &client_to_server_iv_; + remote_to_local_key_ = &client_to_server_key_; + } + if (!active_algorithms_.remote_to_local_->encryption_->initialize(CryptoEncryption::Decrypt, remote_to_local_key_, remote_to_local_iv_)) + HALT("/ssh/session") << "Failed to initialize local-to-remote encryption."; + } +} + +Buffer +SSH::Session::generate_key(const std::string& x, unsigned key_size) +{ + Buffer key; + while (key.length() < key_size) { + Buffer input; + input.append(shared_secret_); + input.append(exchange_hash_); + if (key.empty()) { + input.append(x); + input.append(session_id_); + } else { + input.append(key); + } + if (!active_algorithms_.key_exchange_->hash(&key, &input)) + HALT("/ssh/session") << "Hash failed in generating key."; + } + if (key.length() > key_size) + key.truncate(key_size); + return (key); +} diff --git a/ssh/ssh_session.h b/ssh/ssh_session.h new file mode 100644 index 0000000..ddebd21 --- /dev/null +++ b/ssh/ssh_session.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2012-2013 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef SSH_SSH_SESSION_H +#define SSH_SSH_SESSION_H + +namespace SSH { + class AlgorithmNegotiation; + class Compression; + class Encryption; + class KeyExchange; + class Language; + class MAC; + class ServerHostKey; + + enum Role { + ClientRole, + ServerRole, + }; + + struct UnidirectionalAlgorithms { + Encryption *encryption_; + MAC *mac_; + Compression *compression_; + Language *language_; + + UnidirectionalAlgorithms(void) + : encryption_(NULL), + mac_(NULL), + compression_(NULL), + language_(NULL) + { } + }; + + struct Algorithms { + KeyExchange *key_exchange_; + ServerHostKey *server_host_key_; + UnidirectionalAlgorithms client_to_server_; + UnidirectionalAlgorithms server_to_client_; + UnidirectionalAlgorithms *local_to_remote_; + UnidirectionalAlgorithms *remote_to_local_; + + Algorithms(Role role) + : key_exchange_(NULL), + server_host_key_(NULL), + client_to_server_(), + server_to_client_(), + local_to_remote_(role == ClientRole ? &client_to_server_ : &server_to_client_), + remote_to_local_(role == ClientRole ? &server_to_client_ : &client_to_server_) + { } + }; + + struct Session { + Role role_; + + AlgorithmNegotiation *algorithm_negotiation_; + Algorithms chosen_algorithms_; + Algorithms active_algorithms_; + Buffer client_version_; /* Client's version string. */ + Buffer server_version_; /* Server's version string. */ + Buffer client_kexinit_; /* Client's first key exchange packet. */ + Buffer server_kexinit_; /* Server's first key exchange packet. */ + Buffer shared_secret_; /* Shared secret from key exchange. */ + Buffer session_id_; /* First exchange hash. */ + Buffer exchange_hash_; /* Most recent exchange hash. */ + private: + Buffer client_to_server_iv_; /* Initial client-to-server IV. */ + Buffer server_to_client_iv_; /* Initial server-to-client IV. */ + Buffer client_to_server_key_; /* Client-to-server encryption key. */ + Buffer server_to_client_key_; /* Server-to-client encryption key. */ + Buffer client_to_server_integrity_key_; /* Client-to-server integrity key. */ + Buffer server_to_client_integrity_key_; /* Server-to-client integrity key. */ + public: + uint32_t local_sequence_number_; /* Our packet sequence number. */ + uint32_t remote_sequence_number_; /* Our peer's packet sequence number. */ + + Session(Role role) + : role_(role), + algorithm_negotiation_(NULL), + chosen_algorithms_(role), + active_algorithms_(role), + client_version_(), + server_version_(), + client_kexinit_(), + server_kexinit_(), + shared_secret_(), + session_id_(), + exchange_hash_(), + client_to_server_iv_(), + server_to_client_iv_(), + client_to_server_key_(), + server_to_client_key_(), + client_to_server_integrity_key_(), + server_to_client_integrity_key_(), + local_sequence_number_(0), + remote_sequence_number_(0) + { } + + void local_version(const Buffer& version) + { + if (role_ == ClientRole) + client_version_ = version; + else + server_version_ = version; + } + + void remote_version(const Buffer& version) + { + if (role_ == ClientRole) + server_version_ = version; + else + client_version_ = version; + } + + void local_kexinit(const Buffer& kexinit) + { + if (role_ == ClientRole) + client_kexinit_ = kexinit; + else + server_kexinit_ = kexinit; + } + + void remote_kexinit(const Buffer& kexinit) + { + if (role_ == ClientRole) + server_kexinit_ = kexinit; + else + client_kexinit_ = kexinit; + } + + void activate_chosen(void); + private: + Buffer generate_key(const std::string& x, unsigned key_size); + }; +} + +#endif /* !SSH_SSH_SESSION_H */ diff --git a/xcodec/FUTURE b/xcodec/FUTURE new file mode 100644 index 0000000..aaf1ba3 --- /dev/null +++ b/xcodec/FUTURE @@ -0,0 +1,79 @@ +o) We should really be looking up hashes both in our cache and in our cache + of hashes from the peer. If it's only in the peer's cache, we can add it + to ours and send it as if it were a new piece of data. This could either + be a big speedup or a big slowdown for the encoding process. It depends + on how often we see data from the peer then be data we need to send back + to the peer. + +XCodec 0.9.0 goals: +o) Stop using hashes like names and use actual names. This abstraction will + allow us to minimize the cost of collisions, speed lookup, etc. It also + means that different systems will be able to use different encode/hash + algorithms for lookup based on their requirements. +o) Expand the protocol to have different ops for referening hashes in our + namespace vs. those of our peer... +o) Exchange not just our UUIDs but a list of all of the UUIDs of other systems + we're talking to, allowing us to also reference hashes in other namespaces + that we share access to. +o) Also exchange other parameters, like size of the backref window, using the + minimum between the two peers. + +Past ideas: +o) Create a new XCodecTag that incorporates a hash and a counter and + perhaps other things... +o) The counter will increment for each collision (or perhaps just be a + random number after the first collision and bail out if there's a + collision on the random number) and add new variants of extract, etc., + that give a counter to append to the hash to get the Tag. +o) Allow either powers of two above 128 or multiples of 128 to be usable + chunk sizes instead of just 128. Include any time we define a tag a + bitmap of 128-byte blocks (or blocks of each size down to 128) within + the chunk that are to be learned, too, so that we still deal well with + changes. Eventually allow defining new e.g. 4K blocks based on old 4K + blocks with a single 128-byte block difference? +o) Add a pass number to the Tag so we can do recursive encoding. Use a + limited number of bits and put this above the opcode so that we can have + separate back-reference windows, etc., for each pass and so that we can + avoid escaping for subsequent passes, perhaps? +o) Deflate after recursive encoding. +o) A new encoder that can exploit all of those features, possibly keeping + the old encoder around for applications that need low latency and high + throughput. + +To-do: + +o) Add a 'count' field to the hash and allow incrementing it to do collision + overflow. For this it'd be nice to have an interface that would return a + range of matches in the dictionary. Put the count at the end to make this + possible. Would need to change the encoding logic to use a different OP + for these that took, say, a count or even just the full hash/identifier. +o) Only have N bytes outstanding at any given time (say 128k?) and add some + type of ACK, perhaps? This is necessary to: +o) Write a garbage-collector for the dictionary. LRU? + +Possibly-bad future ideas: + +o) Incorporate run-length encoding. +o) Incorporate occasional (figure out frequency) CRCs or such of the next N + bytes of decoded data to make it possible to detect any hash mismatches, + using a different hash function to any that go into the hash. +o) If the encoded version of a stream is larger than the source would be + escaped, it'd be nice to just transmit it escaped and to have some way to + tell the remote side how to pick out chunks to be taken as known to both + parties in the future. One approach would be to send a list of offsets + at which hashes were declared. + +%%% + +Hash-set deduplication: + +For a given number of hashes (say 64), put an unordered list (hash?) of each +64 hashes that are encountered into a database. + +When data is encoded, check whether its 64 hashes have appeared previously. If +they have, then use a compact encoding to list the order in which they appear +and the offsets within the list at which escaped or new data is to be inserted. + +Eventually extend with one of the Computational Biology algorithms for finding +sequences missing an element or with one element changed so that we can do work +with deltas and offset sequences/sets more reliably. diff --git a/xcodec/Makefile b/xcodec/Makefile new file mode 100644 index 0000000..19d04f6 --- /dev/null +++ b/xcodec/Makefile @@ -0,0 +1,5 @@ +SUBDIR+=example +SUBDIR+=test +SUBDIR+=cache/coss + +include ../common/subdir.mk diff --git a/xcodec/TODO b/xcodec/TODO new file mode 100644 index 0000000..554308e --- /dev/null +++ b/xcodec/TODO @@ -0,0 +1,19 @@ +o) Go back to a lookahead decoder so we don't have to do an ASK/LEARN at a time, + that will be really painful on long, slow links. +o) Add a PAUSE/RESUME mechanism so that we don't have, say, more than 1MB of data + queued up during an ASK/LEARN session? PAUSE when we send an ASK with more + than 1MB or data or get more than 1MB of data with an ASK outstanding, and then + send a RESUME once we get <1MB of data outstanding? +o) Use a 16-bit window counter rather than an 8-bit one so we have an 8MB window + rather than a 32KB one. + XXX Preliminary tests show this to be a big throughput hit. Need to check + whether the gains are worth it. +o) Don't let a peer claim to have our UUID? +o) Permanent storage. +o) Decide whether to keep a std::set (or something fancier) of hashes associated + with each UUID (i.e. ones we have sent to them). We could even make it a + set of so that we can distribute updates like routing + tables. +o) Do lookups in the peer's dictionary and ours at the same time. +o) Put a generation number in the hashes so that if the remote side recycles a + hash, we can do something about it. diff --git a/xcodec/cache/coss/Makefile b/xcodec/cache/coss/Makefile new file mode 100644 index 0000000..88478a9 --- /dev/null +++ b/xcodec/cache/coss/Makefile @@ -0,0 +1,4 @@ +SUBDIR+=test + +include ../../../common/subdir.mk + diff --git a/xcodec/cache/coss/lib.mk b/xcodec/cache/coss/lib.mk new file mode 100644 index 0000000..01e6170 --- /dev/null +++ b/xcodec/cache/coss/lib.mk @@ -0,0 +1,5 @@ + +VPATH+= ${TOPDIR}/xcodec/cache/coss + +SRCS+= xcodec_cache_coss.cc + diff --git a/xcodec/cache/coss/test/Makefile b/xcodec/cache/coss/test/Makefile new file mode 100644 index 0000000..8169307 --- /dev/null +++ b/xcodec/cache/coss/test/Makefile @@ -0,0 +1,3 @@ +SUBDIR+=xcodec-coss1 + +include ../../../../common/subdir.mk diff --git a/xcodec/cache/coss/test/xcodec-coss1/Makefile b/xcodec/cache/coss/test/xcodec-coss1/Makefile new file mode 100644 index 0000000..1907824 --- /dev/null +++ b/xcodec/cache/coss/test/xcodec-coss1/Makefile @@ -0,0 +1,7 @@ +TEST=xcodec-coss1 + +TOPDIR=../../../../.. +USE_LIBS=common common/uuid xcodec xcodec/cache/coss +include ${TOPDIR}/common/program.mk +LDADD+=-lboost_filesystem -lboost_system + diff --git a/xcodec/cache/coss/test/xcodec-coss1/xcodec-coss1.cc b/xcodec/cache/coss/test/xcodec-coss1/xcodec-coss1.cc new file mode 100644 index 0000000..b1a0c77 --- /dev/null +++ b/xcodec/cache/coss/test/xcodec-coss1/xcodec-coss1.cc @@ -0,0 +1,96 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +using namespace boost::filesystem; + +int +main(void) +{ + + char tmp_template[] = "/tmp/cache-coss-XXXXXX"; + path cache_path = mkdtemp(tmp_template); + create_directory(cache_path); + + typedef pair segment_list_element_t; + typedef deque segment_list_t; + segment_list_t segment_list; + + { + TestGroup g("/test/xcodec/encode-decode-coss/2/char_kat", + "XCodecEncoder::encode / XCodecDecoder::decode #2"); + + UUID uuid; + std::string cache_path_str = cache_path.string(); + unsigned i, j; + + uuid.generate(); + + for (j = 0; j < 4; j++) { + XCodecCache *cache = new XCodecCacheCOSS(uuid, cache_path_str, + 10, 10, 10); + + for (i = 0; i < 10000; i++) { + uint8_t random[XCODEC_SEGMENT_LENGTH]; + + ifstream rand_fd("/dev/urandom"); + rand_fd.read(random, sizeof(random)); + ASSERT("xcodec-coss1", rand_fd.good()); + + uint64_t hash = XCodecHash::hash(random); + const uint8_t* data = cache->lookup(hash); + if (data) + continue; + segment_list.push_front(make_pair(hash, data)); + Buffer buf (data, XCODEC_SEGMENT_LENGTH); + cache->enter(hash, buf, 0); + } + + delete cache; + cache = new XCodecCacheCOSS(uuid, cache_path_str, + 10, 10, 10); + + segment_list_element_t el; + const uint8_t *seg1, *seg2; + + uint64_t hash; + while (!segment_list.empty()){ + el = segment_list.back(); + segment_list.pop_back(); + seg1 = el.second; + seg2 = cache->lookup(el.first); + hash = el.first; + if (!seg2) + cout << "Segment not found: " << hash << endl;; + if (seg2) { + if (memcmp (seg1, seg2, XCODEC_SEGMENT_LENGTH)) + cout << "Segments are not equal: " << hash << + endl; + Test _(g, "Segment are not equal.", + seg1->equal(seg2)); + } + } + + delete cache; + } + + } + + remove_all(cache_path); + + return (0); +} + diff --git a/xcodec/cache/coss/xcodec_cache_coss.cc b/xcodec/cache/coss/xcodec_cache_coss.cc new file mode 100644 index 0000000..f62a3fa --- /dev/null +++ b/xcodec/cache/coss/xcodec_cache_coss.cc @@ -0,0 +1,371 @@ +/* + * + * XCodec COSS Cache + * + * COSS = Cyclic Object storage system + * + * Idea taken from Squid COSS cache. + * + * Diego Woitasen + * XTECH + * + */ + +#include +#include +#include + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_cache_coss.cc // +// Description: persistent cache on disk for xcodec protocol streams // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + + +XCodecCacheCOSS::XCodecCacheCOSS (const UUID& uuid, const std::string& cache_dir, size_t cache_size) + : XCodecCache(uuid, cache_size), + log_("xcodec/cache/coss") +{ + uint8_t str[UUID_STRING_SIZE + 1]; + uuid.to_string (str); + file_path_ = cache_dir; + if (file_path_.size() > 0 && file_path_[file_path_.size() - 1] != '/') + file_path_.append ("/"); + file_path_.append ((const char*) str, UUID_STRING_SIZE); + file_path_.append (".wpc"); + + struct stat st; + if (::stat (file_path_.c_str(), &st) == 0 && (st.st_mode & S_IFREG)) + file_size_ = st.st_size; + else + { + ofstream tmp (file_path_.c_str()); + file_size_ = 0; + } + + serial_number_ = 0; + stripe_range_ = 0; + if (! cache_size) + cache_size = CACHE_BASIC_SIZE; + uint64_t size = ROUND_UP((uint64_t) cache_size * 1048576, sizeof (COSSStripe)); + stripe_limit_ = size / sizeof (COSSStripe); + freshness_level_ = 0; + active_ = 0; + + directory_ = new COSSMetadata[stripe_limit_]; + memset (directory_, 0, sizeof (COSSMetadata) * stripe_limit_); + + stream_.open (file_path_.c_str(), fstream::in | fstream::out | fstream::binary); + if (! read_file ()) + { + stream_.close (); + stream_.open (file_path_.c_str(), fstream::in | fstream::out | fstream::trunc | fstream::binary); + file_size_ = 0; + initialize_stripe (stripe_range_, active_); + } + + DEBUG(log_) << "Cache file: " << file_path_; + DEBUG(log_) << "Max size: " << size; + DEBUG(log_) << "Stripe size: " << sizeof (COSSStripe); + DEBUG(log_) << "Stripe header size: " << sizeof (COSSStripeHeader); + DEBUG(log_) << "Serial: " << serial_number_; + DEBUG(log_) << "Stripe number: " << stripe_range_; +} + +XCodecCacheCOSS::~XCodecCacheCOSS() +{ + for (int i = 0; i < LOADED_STRIPE_COUNT; ++i) + if (stripe_[i].header.metadata.state == 1) + store_stripe (i, (i == active_ ? sizeof (COSSStripe) : sizeof (COSSStripeHeader))); + + stream_.close(); + + delete[] directory_; + + /* + INFO(log_) << "Stats: "; + INFO(log_) << "\tLookups=" << stats_.lookups; + INFO(log_) << "\tHits=" << (stats_.found_1 + stats_.found_2) << " (" << stats_.found_1 << " + " << stats_.found_2 << ")"; + if (stats_.lookups > 0) + INFO(log_) << "\tHit ratio=" << ((stats_.found_1 + stats_.found_2) * 100) / stats_.lookups << "%"; + */ + + DEBUG(log_) << "Closing coss file: " << file_path_; + DEBUG(log_) << "Serial: " << serial_number_; + DEBUG(log_) << "Stripe number: " << stripe_range_; + DEBUG(log_) << "Index size: " << cache_index_.size(); +} + +bool XCodecCacheCOSS::read_file () +{ + COSSStripeHeader header; + COSSIndexEntry entry; + uint64_t serial, range, limit, level; + uint64_t hash; + + serial = range = limit = level = 0; + limit = file_size_ / sizeof (COSSStripe); + if (limit * sizeof (COSSStripe) != file_size_) + return false; + if (limit > stripe_limit_) + limit = stripe_limit_; + stream_.seekg (0); + + for (uint64_t n = 0; n < limit; ++n) + { + stream_.read ((char*) &header, sizeof header); + if (! stream_.good() || stream_.gcount () != sizeof header) + return false; + if (header.metadata.signature != CACHE_SIGNATURE) + return false; + if (header.metadata.segment_count > STRIPE_SEGMENT_COUNT) + return false; + stream_.seekg (sizeof (COSSStripe) - sizeof header, ios::cur); + + if (header.metadata.serial_number > serial) + serial = header.metadata.serial_number, range = n; + if (header.metadata.freshness > level) + level = header.metadata.freshness; + + directory_[n] = header.metadata; + directory_[n].state = 0; + + for (int i = 0; i < STRIPE_SEGMENT_COUNT; ++i) + { + if ((hash = header.hash_array[i])) + { + entry.stripe_range = n; + entry.position = i; + cache_index_.insert (hash, entry); + } + } + } + + if (serial > 0) + { + serial_number_ = serial; + stripe_range_ = range; + freshness_level_ = level; + load_stripe (stripe_range_, active_); + } + else + { + initialize_stripe (stripe_range_, active_); + } + + return true; +} + +void XCodecCacheCOSS::enter (const uint64_t& hash, const Buffer& buf, unsigned off) +{ + COSSIndexEntry entry; + + while (stripe_[active_].header.metadata.segment_index >= STRIPE_SEGMENT_COUNT) + new_active (); + + COSSStripe& act = stripe_[active_]; + act.header.hash_array[act.header.metadata.segment_index] = hash; + buf.copyout (act.segment_array[act.header.metadata.segment_index].bytes, off, XCODEC_SEGMENT_LENGTH); + entry.stripe_range = act.header.metadata.stripe_range; + entry.position = act.header.metadata.segment_index; + + act.header.metadata.segment_index++; + while (act.header.metadata.segment_index < STRIPE_SEGMENT_COUNT && + act.header.hash_array[act.header.metadata.segment_index]) + act.header.metadata.segment_index++; + act.header.metadata.segment_count++; + act.header.metadata.freshness = ++freshness_level_; + + cache_index_.insert (hash, entry); +} + +bool XCodecCacheCOSS::lookup (const uint64_t& hash, Buffer& buf) +{ + const COSSIndexEntry* entry; + const uint8_t* data; + int slot; + + stats_.lookups++; + + if ((data = find_recent (hash))) + { + buf.append (data, XCODEC_SEGMENT_LENGTH); + stats_.found_1++; + return true; + } + + if (! (entry = cache_index_.lookup (hash))) + return false; + + for (slot = 0; slot < LOADED_STRIPE_COUNT; ++slot) + if (stripe_[slot].header.metadata.stripe_range == entry->stripe_range) + break; + + if (slot >= LOADED_STRIPE_COUNT) + { + slot = best_unloadable_slot (); + detach_stripe (slot); + load_stripe (entry->stripe_range, slot); + } + + if (stripe_[slot].header.hash_array[entry->position] != hash) + return false; + + stripe_[slot].header.metadata.freshness = ++freshness_level_; + stripe_[slot].header.metadata.uses++; + stripe_[slot].header.metadata.credits++; + stripe_[slot].header.metadata.load_uses++; + stripe_[slot].header.flags[entry->position] |= 3; + + data = stripe_[slot].segment_array[entry->position].bytes; + remember (hash, data); + buf.append (data, XCODEC_SEGMENT_LENGTH); + stats_.found_2++; + return true; +} + +void XCodecCacheCOSS::initialize_stripe (uint64_t range, int slot) +{ + memset (&stripe_[slot].header, 0, sizeof (COSSStripeHeader)); + stripe_[slot].header.metadata.signature = CACHE_SIGNATURE; + stripe_[slot].header.metadata.version = CACHE_VERSION; + stripe_[slot].header.metadata.serial_number = ++serial_number_; + stripe_[slot].header.metadata.stripe_range = range; + stripe_[slot].header.metadata.state = 1; + directory_[range] = stripe_[slot].header.metadata; +} + +bool XCodecCacheCOSS::load_stripe (uint64_t range, int slot) +{ + uint64_t pos = range * sizeof (COSSStripe); + if (pos < file_size_) + { + stream_.seekg (pos); + stream_.read ((char*) &stripe_[slot], sizeof (COSSStripe)); + if (stream_.gcount () == sizeof (COSSStripe)) + { + stripe_[slot].header.metadata.stripe_range = range; + stripe_[slot].header.metadata.load_uses = 0; + stripe_[slot].header.metadata.state = 1; + directory_[range].state = 1; + return true; + } + } + + stream_.clear (); + return false; +} + +void XCodecCacheCOSS::store_stripe (int slot, size_t size) +{ + uint64_t pos = stripe_[slot].header.metadata.stripe_range * sizeof (COSSStripe); + if (pos != (uint64_t) stream_.tellp ()) + stream_.seekp (pos); + stream_.write ((char*) &stripe_[slot], size); + if (stream_.good () && pos + sizeof (COSSStripe) > file_size_) + file_size_ = pos + sizeof (COSSStripe); + stream_.clear (); +} + +void XCodecCacheCOSS::new_active () +{ + store_stripe (active_, sizeof (COSSStripe)); + active_ = best_unloadable_slot (); + detach_stripe (active_); + stripe_range_ = best_erasable_stripe (); + if (load_stripe (stripe_range_, active_)) + purge_stripe (active_); + else + initialize_stripe (stripe_range_, active_); +} + +int XCodecCacheCOSS::best_unloadable_slot () +{ + uint64_t v, n = 0xFFFFFFFFFFFFFFFFull; + int j = 0; + + for (int i = 0; i < LOADED_STRIPE_COUNT; ++i) + { + if (i == active_) + continue; + if (stripe_[i].header.metadata.signature == 0) + return i; + if ((v = stripe_[i].header.metadata.freshness + stripe_[i].header.metadata.load_uses) < n) + j = i, n = v; + } + + return j; +} + +uint64_t XCodecCacheCOSS::best_erasable_stripe () +{ + COSSMetadata* m; + uint64_t v, n = 0xFFFFFFFFFFFFFFFFull; + uint64_t i, j = 0; + + for (m = directory_, i = 0; i < stripe_limit_; ++i, ++m) + { + if (m->state == 1) + continue; + if (m->signature == 0) + return i; + if ((v = m->freshness + m->uses) < n) + j = i, n = v; + } + + return j; +} + +void XCodecCacheCOSS::detach_stripe (int slot) +{ + if (stripe_[slot].header.metadata.state == 1) + { + uint64_t range = stripe_[slot].header.metadata.stripe_range; + directory_[range] = stripe_[slot].header.metadata; + directory_[range].state = 2; + + for (int i = 0; i < STRIPE_SEGMENT_COUNT; ++i) + { + if (stripe_[slot].header.flags[i] & 1) + { + forget (stripe_[slot].header.hash_array[i]); + stripe_[slot].header.flags[i] &= ~1; + } + } + + stripe_[slot].header.metadata.state = 0; + store_stripe (slot, sizeof (COSSStripeHeader)); + } +} + +void XCodecCacheCOSS::purge_stripe (int slot) +{ + for (int i = STRIPE_SEGMENT_COUNT - 1; i >= 0; --i) + { + uint64_t hash = stripe_[slot].header.hash_array[i]; + if (hash && ! (stripe_[slot].header.flags[i] & 2)) + { + cache_index_.erase (hash); + stripe_[slot].header.hash_array[i] = 0; + stripe_[slot].header.flags[i] = 0; + stripe_[slot].header.metadata.segment_count--; + } + + stripe_[slot].header.flags[i] &= ~2; + if (! stripe_[slot].header.hash_array[i]) + stripe_[slot].header.metadata.segment_index = i; + } + + stripe_[slot].header.metadata.serial_number = ++serial_number_; + stripe_[slot].header.metadata.uses = stripe_[slot].header.metadata.credits; + stripe_[slot].header.metadata.credits = 0; + + if (stripe_[slot].header.metadata.segment_count >= STRIPE_SEGMENT_COUNT) + INFO(log_) << "No more space available in cache"; +} diff --git a/xcodec/cache/coss/xcodec_cache_coss.h b/xcodec/cache/coss/xcodec_cache_coss.h new file mode 100644 index 0000000..6916085 --- /dev/null +++ b/xcodec/cache/coss/xcodec_cache_coss.h @@ -0,0 +1,223 @@ +/* + * + * XCodec COSS Cache + * + * COSS = Cyclic Object Storage System + * + * Idea taken from Squid COSS. + * + * Diego Woitasen + * XTECH + * + */ + +#ifndef XCODEC_XCODEC_CACHE_COSS_H +#define XCODEC_XCODEC_CACHE_COSS_H + +#include +#include +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_cache_coss.h // +// Description: persistent cache on disk for xcodec protocol streams // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +using namespace std; + +/* + * - In COSS, we have one file per cache (UUID). The file is divided in + * stripes. + * + * - Each stripe is composed by: + * metadata + hash array + segment size array + segment array. + * + * - The arrays elements are the same order: + * hash1, hash2, ..., hashN - size1, size2, ..., sizeN - seg1, seg2, ..., segN. + * + * - The segments are indexed in memory. This index is loaded when the cache is + * openned, reading the hash array of each stripe. Takes a few millisecons in + * a 10 GB cache. + * + * - We have one active stripe in memory at a time. New segments are written + * in the current stripe in order of appearance. + * + * - When a cached segment is requested and it's out of the active stripe, + * is copied to it. + * + * - When the current stripe is full, we move to the next one. + * + * - When we reach the EOF, first stripe is zeroed and becomes active. + * + */ + +// Changes introduced in version 2: +// +// - segment size is made independent of BUFFER_SEGMENT_SIZE and not stored explicitly +// since it is always XCODEC_SEGMENT_LENGTH +// - several stripes can be help simultaneously in memory, and when a segment is requested +// which lies outside the active stripe it is read into an alternate slot together +// with the whole stripe to be ready to satisfy requests for neighbour segments +// - an array of bits keeps track of the state of each segment within a stripe signaling +// if it has been recently used +// - when no more place is available, the LRU stripe is purged and any segments +// no used during the last period are erased + +/* + * This values should be page aligned. + */ + +#define CACHE_SIGNATURE 0xF150E964 +#define CACHE_VERSION 2 +#define STRIPE_SEGMENT_COUNT 512 // segments of XCODEC_SEGMENT_LENGTH per stripe (must fit into 16 bits) +#define LOADED_STRIPE_COUNT 4 // number of stripes held in memory (must be greater than 1) +#define CACHE_BASIC_SIZE 1024 // MB + +#define CACHE_ALIGNEMENT 4096 +#define HEADER_ARRAY_SIZE (STRIPE_SEGMENT_COUNT * (sizeof (uint64_t) + sizeof (uint32_t))) +#define METADATA_SIZE (sizeof (COSSMetadata)) +#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +#define HEADER_ALIGNED_SIZE ROUND_UP(HEADER_ARRAY_SIZE + METADATA_SIZE, CACHE_ALIGNEMENT) +#define METADATA_PADDING (HEADER_ALIGNED_SIZE - HEADER_ARRAY_SIZE - METADATA_SIZE) + +struct COSSIndexEntry +{ + uint64_t stripe_range : 48; + uint64_t position : 16; +}; + +class COSSIndex +{ + typedef __gnu_cxx::hash_map index_t; + index_t index; + +public: + void insert (const uint64_t& hash, const COSSIndexEntry& entry) + { + index[hash] = entry; + } + + const COSSIndexEntry* lookup (const uint64_t& hash) + { + index_t::iterator it = index.find (hash); + return (it != index.end () ? &it->second : 0); + } + + void erase (const uint64_t& hash) + { + index.erase (hash); + } + + size_t size() + { + return index.size(); + } +}; + +struct COSSOnDiskSegment +{ + uint8_t bytes[XCODEC_SEGMENT_LENGTH]; + string hexdump() { + string dump; + char buf[8]; + int i; + for (i = 0; i < 70; i++) { + snprintf(buf, 8, "%02x", bytes[i]); + dump += buf; + } + return dump; + } +}; + +struct COSSMetadata +{ + uint32_t signature; + uint32_t version; + uint64_t serial_number; + uint64_t stripe_range; + uint32_t segment_index; + uint32_t segment_count; + uint64_t freshness; + uint64_t uses; + uint64_t credits; + uint32_t load_uses; + uint32_t state; +}; + +struct COSSStripeHeader +{ + COSSMetadata metadata; + char padding[METADATA_PADDING]; + uint32_t flags[STRIPE_SEGMENT_COUNT]; + uint64_t hash_array[STRIPE_SEGMENT_COUNT]; +}; + +struct COSSStripe +{ + COSSStripeHeader header; + COSSOnDiskSegment segment_array[STRIPE_SEGMENT_COUNT]; + +public: + COSSStripe() { memset (&header, 0, sizeof header); } +}; + +struct COSSStats +{ + uint64_t lookups; + uint64_t found_1; + uint64_t found_2; + +public: + COSSStats() { lookups = found_1 = found_2 = 0; } +}; + + +class XCodecCacheCOSS : public XCodecCache +{ + std::string file_path_; + uint64_t file_size_; + fstream stream_; + + uint64_t serial_number_; + uint64_t stripe_range_; + uint64_t stripe_limit_; + uint64_t freshness_level_; + + COSSStripe stripe_[LOADED_STRIPE_COUNT]; + int active_; + + COSSMetadata* directory_; + COSSIndex cache_index_; + COSSStats stats_; + LogHandle log_; + +public: + XCodecCacheCOSS (const UUID& uuid, const std::string& cache_dir, size_t cache_size); + ~XCodecCacheCOSS(); + + virtual void enter (const uint64_t& hash, const Buffer& buf, unsigned off); + virtual bool lookup (const uint64_t& hash, Buffer& buf); + +private: + bool read_file (); + void initialize_stripe (uint64_t range, int slot); + bool load_stripe (uint64_t range, int slot); + void store_stripe (int slot, size_t size); + void new_active (); + int best_unloadable_slot (); + uint64_t best_erasable_stripe (); + void detach_stripe (int slot); + void purge_stripe (int slot); +}; + +#endif /* !XCODEC_XCODEC_CACHE_COSS_H */ + diff --git a/xcodec/example/Makefile b/xcodec/example/Makefile new file mode 100644 index 0000000..036bd5a --- /dev/null +++ b/xcodec/example/Makefile @@ -0,0 +1,3 @@ +SUBDIR+=xcodec-hash-roll1 + +include ../../common/subdir.mk diff --git a/xcodec/example/xcodec-hash-roll1/Makefile b/xcodec/example/xcodec-hash-roll1/Makefile new file mode 100644 index 0000000..1587fe7 --- /dev/null +++ b/xcodec/example/xcodec-hash-roll1/Makefile @@ -0,0 +1,7 @@ +PROGRAM=xcodec-hash-roll1 + +SRCS+= xcodec-hash-roll1.cc + +TOPDIR=../../.. +USE_LIBS=common common/thread common/time common/uuid event io xcodec +include ${TOPDIR}/common/program.mk diff --git a/xcodec/example/xcodec-hash-roll1/xcodec-hash-roll1.cc b/xcodec/example/xcodec-hash-roll1/xcodec-hash-roll1.cc new file mode 100644 index 0000000..0d0981e --- /dev/null +++ b/xcodec/example/xcodec-hash-roll1/xcodec-hash-roll1.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#include +#include + +class Sink { + LogHandle log_; + + StreamHandle fd_; + XCodecHash hash_; + unsigned length_; + Action *action_; +public: + Sink(int fd) + : log_("/sink"), + fd_(fd), + hash_(), + length_(0), + action_(NULL) + { + EventCallback *cb = callback(this, &Sink::read_complete); + action_ = fd_.read(0, cb); + } + + ~Sink() + { + ASSERT(log_, action_ == NULL); + } + + void read_complete(Event e) + { + action_->cancel(); + action_ = NULL; + + switch (e.type_) { + case Event::Done: + case Event::EOS: + break; + default: + HALT(log_) << "Unexpected event: " << e; + return; + } + + while (!e.buffer_.empty()) { + BufferSegment *seg; + e.buffer_.moveout(&seg); + + const uint8_t *p, *q = seg->end(); + if (length_ == XCODEC_SEGMENT_LENGTH) { + for (p = seg->data(); p < q; p++) { + hash_.roll(*p); + } + } else { + for (p = seg->data(); p < q; p++) { + if (length_ == XCODEC_SEGMENT_LENGTH) { + hash_.roll(*p); + } else { + hash_.add(*p); + length_++; + } + } + } + seg->unref(); + } + + if (e.type_ == Event::EOS) { + fd_.close(); + return; + } + + EventCallback *cb = callback(this, &Sink::read_complete); + action_ = fd_.read(0, cb); + } +}; + +int +main(void) +{ + Sink sink(STDIN_FILENO); + + event_system.run(); +} diff --git a/xcodec/lib.mk b/xcodec/lib.mk new file mode 100644 index 0000000..02477a0 --- /dev/null +++ b/xcodec/lib.mk @@ -0,0 +1,5 @@ +VPATH+= ${TOPDIR}/xcodec + +SRCS+= xcodec_encoder.cc +SRCS+= xcodec_decoder.cc +SRCS+= xcodec_filter.cc diff --git a/xcodec/test/Makefile b/xcodec/test/Makefile new file mode 100644 index 0000000..6f1271c --- /dev/null +++ b/xcodec/test/Makefile @@ -0,0 +1,4 @@ +SUBDIR+=xcodec-encode-decode1 +SUBDIR+=xcodec-hash1 + +include ../../common/subdir.mk diff --git a/xcodec/test/xcodec-encode-decode1/Makefile b/xcodec/test/xcodec-encode-decode1/Makefile new file mode 100644 index 0000000..64a10ab --- /dev/null +++ b/xcodec/test/xcodec-encode-decode1/Makefile @@ -0,0 +1,5 @@ +TEST=xcodec-encode-decode1 + +TOPDIR=../../.. +USE_LIBS=common common/uuid xcodec +include ${TOPDIR}/common/program.mk diff --git a/xcodec/test/xcodec-encode-decode1/xcodec-encode-decode1.cc b/xcodec/test/xcodec-encode-decode1/xcodec-encode-decode1.cc new file mode 100644 index 0000000..d36e228 --- /dev/null +++ b/xcodec/test/xcodec-encode-decode1/xcodec-encode-decode1.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +int +main(void) +{ + { + TestGroup g("/test/xcodec/encode-decode/1/char_kat", "XCodecEncoder::encode / XCodecDecoder::decode #1"); + + unsigned i; + for (i = 0; i < 256; i++) { + Buffer in; + unsigned j; + for (j = 0; j < XCODEC_SEGMENT_LENGTH; j++) + in.append((uint8_t)i); + + for (j = 0; j < 8; j++) { + Buffer tmp(in); + tmp.append(in); + in = tmp; + } + + Buffer original(in); + + UUID uuid; + uuid.generate(); + + XCodecCache *cache = new XCodecMemoryCache(uuid); + XCodecEncoder encoder(cache); + + Buffer out; + encoder.encode(&out, &in); + + { + Test _(g, "Empty input buffer after encode.", in.empty()); + } + + { + Test _(g, "Non-empty output buffer after encode.", !out.empty()); + } + + { + Test _(g, "Reduction in size.", out.length() < original.length()); + } + + out.moveout(&in); + + XCodecDecoder decoder(cache); + std::set unknown_hashes; + + bool ok = decoder.decode(&out, &in, unknown_hashes); + { + Test _(g, "Decoder success.", ok); + } + + { + Test _(g, "No unknown hashes.", unknown_hashes.empty()); + } + + { + Test _(g, "Empty input buffer after decode.", in.empty()); + } + + { + Test _(g, "Non-empty output buffer after decode.", !out.empty()); + } + + { + Test _(g, "Expected data.", out.equal(&original)); + } + + delete cache; + } + } + + return (0); +} diff --git a/xcodec/test/xcodec-hash1/Makefile b/xcodec/test/xcodec-hash1/Makefile new file mode 100644 index 0000000..f587381 --- /dev/null +++ b/xcodec/test/xcodec-hash1/Makefile @@ -0,0 +1,5 @@ +TEST=xcodec-hash1 + +TOPDIR=../../.. +USE_LIBS=common xcodec +include ${TOPDIR}/common/program.mk diff --git a/xcodec/test/xcodec-hash1/xcodec-hash1.cc b/xcodec/test/xcodec-hash1/xcodec-hash1.cc new file mode 100644 index 0000000..fbb1e85 --- /dev/null +++ b/xcodec/test/xcodec-hash1/xcodec-hash1.cc @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +/* + * Single-character run known-answer tests. + */ +static uint64_t char_kats[] = { + 0x0000000080200400ull, + 0x8200400000400800ull, + 0x0400800080600c00ull, + 0x8200400000801000ull, + 0x8600c00080a01400ull, + 0x8200400000c01800ull, + 0x0400800080e01c00ull, + 0x8200400001002000ull, + 0x0801000081202400ull, + 0x8200400001402800ull, + 0x0400800081602c00ull, + 0x8200400001803000ull, + 0x8600c00081a03400ull, + 0x8200400001c03800ull, + 0x0400800081e03c00ull, + 0x8200400002004000ull, + 0x8a01400082204400ull, + 0x8200400002404800ull, + 0x0400800082604c00ull, + 0x8200400002805000ull, + 0x8600c00082a05400ull, + 0x8200400002c05800ull, + 0x0400800082e05c00ull, + 0x8200400003006000ull, + 0x0801000083206400ull, + 0x8200400003406800ull, + 0x0400800083606c00ull, + 0x8200400003807000ull, + 0x8600c00083a07400ull, + 0x8200400003c07800ull, + 0x0400800083e07c00ull, + 0x8200400004008000ull, + 0x0c01800084208400ull, + 0x8200400004408800ull, + 0x0400800084608c00ull, + 0x8200400004809000ull, + 0x8600c00084a09400ull, + 0x8200400004c09800ull, + 0x0400800084e09c00ull, + 0x820040000500a000ull, + 0x080100008520a400ull, + 0x820040000540a800ull, + 0x040080008560ac00ull, + 0x820040000580b000ull, + 0x8600c00085a0b400ull, + 0x8200400005c0b800ull, + 0x0400800085e0bc00ull, + 0x820040000600c000ull, + 0x8a0140008620c400ull, + 0x820040000640c800ull, + 0x040080008660cc00ull, + 0x820040000680d000ull, + 0x8600c00086a0d400ull, + 0x8200400006c0d800ull, + 0x0400800086e0dc00ull, + 0x820040000700e000ull, + 0x080100008720e400ull, + 0x820040000740e800ull, + 0x040080008760ec00ull, + 0x820040000780f000ull, + 0x8600c00087a0f400ull, + 0x8200400007c0f800ull, + 0x0400800087e0fc00ull, + 0x8200400008010000ull, + 0x8e01c00088210400ull, + 0x8200400008410800ull, + 0x0400800088610c00ull, + 0x8200400008811000ull, + 0x8600c00088a11400ull, + 0x8200400008c11800ull, + 0x0400800088e11c00ull, + 0x8200400009012000ull, + 0x0801000089212400ull, + 0x8200400009412800ull, + 0x0400800089612c00ull, + 0x8200400009813000ull, + 0x8600c00089a13400ull, + 0x8200400009c13800ull, + 0x0400800089e13c00ull, + 0x820040000a014000ull, + 0x8a0140008a214400ull, + 0x820040000a414800ull, + 0x040080008a614c00ull, + 0x820040000a815000ull, + 0x8600c0008aa15400ull, + 0x820040000ac15800ull, + 0x040080008ae15c00ull, + 0x820040000b016000ull, + 0x080100008b216400ull, + 0x820040000b416800ull, + 0x040080008b616c00ull, + 0x820040000b817000ull, + 0x8600c0008ba17400ull, + 0x820040000bc17800ull, + 0x040080008be17c00ull, + 0x820040000c018000ull, + 0x0c0180008c218400ull, + 0x820040000c418800ull, + 0x040080008c618c00ull, + 0x820040000c819000ull, + 0x8600c0008ca19400ull, + 0x820040000cc19800ull, + 0x040080008ce19c00ull, + 0x820040000d01a000ull, + 0x080100008d21a400ull, + 0x820040000d41a800ull, + 0x040080008d61ac00ull, + 0x820040000d81b000ull, + 0x8600c0008da1b400ull, + 0x820040000dc1b800ull, + 0x040080008de1bc00ull, + 0x820040000e01c000ull, + 0x8a0140008e21c400ull, + 0x820040000e41c800ull, + 0x040080008e61cc00ull, + 0x820040000e81d000ull, + 0x8600c0008ea1d400ull, + 0x820040000ec1d800ull, + 0x040080008ee1dc00ull, + 0x820040000f01e000ull, + 0x080100008f21e400ull, + 0x820040000f41e800ull, + 0x040080008f61ec00ull, + 0x820040000f81f000ull, + 0x8600c0008fa1f400ull, + 0x820040000fc1f800ull, + 0x040080008fe1fc00ull, + 0x8200400010020000ull, + 0x1002000090220400ull, + 0x8200400010420800ull, + 0x0400800090620c00ull, + 0x8200400010821000ull, + 0x8600c00090a21400ull, + 0x8200400010c21800ull, + 0x0400800090e21c00ull, + 0x8200400011022000ull, + 0x0801000091222400ull, + 0x8200400011422800ull, + 0x0400800091622c00ull, + 0x8200400011823000ull, + 0x8600c00091a23400ull, + 0x8200400011c23800ull, + 0x0400800091e23c00ull, + 0x8200400012024000ull, + 0x8a01400092224400ull, + 0x8200400012424800ull, + 0x0400800092624c00ull, + 0x8200400012825000ull, + 0x8600c00092a25400ull, + 0x8200400012c25800ull, + 0x0400800092e25c00ull, + 0x8200400013026000ull, + 0x0801000093226400ull, + 0x8200400013426800ull, + 0x0400800093626c00ull, + 0x8200400013827000ull, + 0x8600c00093a27400ull, + 0x8200400013c27800ull, + 0x0400800093e27c00ull, + 0x8200400014028000ull, + 0x0c01800094228400ull, + 0x8200400014428800ull, + 0x0400800094628c00ull, + 0x8200400014829000ull, + 0x8600c00094a29400ull, + 0x8200400014c29800ull, + 0x0400800094e29c00ull, + 0x820040001502a000ull, + 0x080100009522a400ull, + 0x820040001542a800ull, + 0x040080009562ac00ull, + 0x820040001582b000ull, + 0x8600c00095a2b400ull, + 0x8200400015c2b800ull, + 0x0400800095e2bc00ull, + 0x820040001602c000ull, + 0x8a0140009622c400ull, + 0x820040001642c800ull, + 0x040080009662cc00ull, + 0x820040001682d000ull, + 0x8600c00096a2d400ull, + 0x8200400016c2d800ull, + 0x0400800096e2dc00ull, + 0x820040001702e000ull, + 0x080100009722e400ull, + 0x820040001742e800ull, + 0x040080009762ec00ull, + 0x820040001782f000ull, + 0x8600c00097a2f400ull, + 0x8200400017c2f800ull, + 0x0400800097e2fc00ull, + 0x8200400018030000ull, + 0x8e01c00098230400ull, + 0x8200400018430800ull, + 0x0400800098630c00ull, + 0x8200400018831000ull, + 0x8600c00098a31400ull, + 0x8200400018c31800ull, + 0x0400800098e31c00ull, + 0x8200400019032000ull, + 0x0801000099232400ull, + 0x8200400019432800ull, + 0x0400800099632c00ull, + 0x8200400019833000ull, + 0x8600c00099a33400ull, + 0x8200400019c33800ull, + 0x0400800099e33c00ull, + 0x820040001a034000ull, + 0x8a0140009a234400ull, + 0x820040001a434800ull, + 0x040080009a634c00ull, + 0x820040001a835000ull, + 0x8600c0009aa35400ull, + 0x820040001ac35800ull, + 0x040080009ae35c00ull, + 0x820040001b036000ull, + 0x080100009b236400ull, + 0x820040001b436800ull, + 0x040080009b636c00ull, + 0x820040001b837000ull, + 0x8600c0009ba37400ull, + 0x820040001bc37800ull, + 0x040080009be37c00ull, + 0x820040001c038000ull, + 0x0c0180009c238400ull, + 0x820040001c438800ull, + 0x040080009c638c00ull, + 0x820040001c839000ull, + 0x8600c0009ca39400ull, + 0x820040001cc39800ull, + 0x040080009ce39c00ull, + 0x820040001d03a000ull, + 0x080100009d23a400ull, + 0x820040001d43a800ull, + 0x040080009d63ac00ull, + 0x820040001d83b000ull, + 0x8600c0009da3b400ull, + 0x820040001dc3b800ull, + 0x040080009de3bc00ull, + 0x820040001e03c000ull, + 0x8a0140009e23c400ull, + 0x820040001e43c800ull, + 0x040080009e63cc00ull, + 0x820040001e83d000ull, + 0x8600c0009ea3d400ull, + 0x820040001ec3d800ull, + 0x040080009ee3dc00ull, + 0x820040001f03e000ull, + 0x080100009f23e400ull, + 0x820040001f43e800ull, + 0x040080009f63ec00ull, + 0x820040001f83f000ull, + 0x8600c0009fa3f400ull, + 0x820040001fc3f800ull, + 0x040080009fe3fc00ull, + 0x8200400020040000ull +}; + +int +main(void) +{ + { + TestGroup g("/test/xcodec/hash1/char_kat", "XCodecHash #1 / Single-character KATs"); + + unsigned i; + for (i = 0; i < sizeof char_kats / sizeof char_kats[0]; i++) { + XCodecHash hash; + unsigned j; + for (j = 0; j < XCODEC_SEGMENT_LENGTH; j++) + hash.add((uint8_t)i); + + std::ostringstream os; + os << "KAT #" << i; + + Test _(g, os.str(), char_kats[i] == hash.mix()); + } + } + + return (0); +} diff --git a/xcodec/xcodec.h b/xcodec/xcodec.h new file mode 100644 index 0000000..da3d708 --- /dev/null +++ b/xcodec/xcodec.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef XCODEC_XCODEC_H +#define XCODEC_XCODEC_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec.h // +// Description: symbolic constants for the basic xcodec protocol // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#define XCODEC_MAGIC ((uint8_t)0xf1) /* Magic! */ + +/* + * Usage: + * + * + * Effects: + * A literal XCODEC_MAGIC appears in the output stream. + * + */ +#define XCODEC_OP_ESCAPE ((uint8_t)0x00) + +/* + * Usage: + * data[uint8_t x XCODEC_SEGMENT_LENGTH] + * + * Effects: + * The `data' is hashed, the hash is associated with the data if possible + * and the data is inserted into the output stream. + * + * If other data is already known by the hash of `data', error will be + * indicated from the decoder. + * + */ +#define XCODEC_OP_EXTRACT ((uint8_t)0x01) + +/* + * Usage: + * hash[uint64_t] + * + * Effects: + * The data associated with the hash `hash' is looked up and inserted into + * the output stream if possible. + * + * If the `hash' is not known, an OP_ASK will be sent in response. + * + */ +#define XCODEC_OP_REF ((uint8_t)0x02) + +#define XCODEC_SEGMENT_LENGTH (2048) + +#endif /* !XCODEC_XCODEC_H */ diff --git a/xcodec/xcodec_cache.h b/xcodec/xcodec_cache.h new file mode 100644 index 0000000..d661f11 --- /dev/null +++ b/xcodec/xcodec_cache.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef XCODEC_XCODEC_CACHE_H +#define XCODEC_XCODEC_CACHE_H + +#include +#include + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_cache.h // +// Description: base cache class and in-memory cache implementation // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#define XCODEC_WINDOW_COUNT 64 // must be binary + +/* + * XXX + * GCC supports hash but not hash. On some + * of our platforms, the former is 64-bits, on some the latter. As a result, + * we need this wrapper structure to throw our hashes into so that GCC's hash + * function can be reliably defined to use them. + */ +struct Hash64 +{ + uint64_t hash_; + + Hash64(const uint64_t& hash) + : hash_(hash) + { } + + bool operator== (const Hash64& hash) const + { + return (hash_ == hash.hash_); + } + + bool operator< (const Hash64& hash) const + { + return (hash_ < hash.hash_); + } +}; + +namespace __gnu_cxx +{ + template<> + struct hash + { + size_t operator() (const Hash64& x) const + { + return (x.hash_); + } + }; +} + + +class XCodecCache +{ +private: + UUID uuid_; + size_t size_; + struct WindowItem {uint64_t hash; const uint8_t* data;}; + WindowItem window_[XCODEC_WINDOW_COUNT]; + unsigned cursor_; + +protected: + XCodecCache (const UUID& uuid, size_t size) + : uuid_(uuid), + size_(size) + { + memset (window_, 0, sizeof window_); + cursor_ = 0; + } + +public: + virtual ~XCodecCache() + { } + + const UUID& identifier () + { + return uuid_; + } + + size_t nominal_size () + { + return size_; + } + + virtual void enter (const uint64_t& hash, const Buffer& buf, unsigned off) = 0; + virtual bool lookup (const uint64_t& hash, Buffer& buf) = 0; + +protected: + void remember (const uint64_t& hash, const uint8_t* data) + { + window_[cursor_].hash = hash; + window_[cursor_].data = data; + cursor_ = (cursor_ + 1) & (XCODEC_WINDOW_COUNT - 1); + } + + const uint8_t* find_recent (const uint64_t& hash) + { + WindowItem* w; + int n; + + for (w = window_, n = XCODEC_WINDOW_COUNT; n > 0; --n, ++w) + if (w->hash == hash) + return w->data; + + return 0; + } + + void forget (const uint64_t& hash) + { + WindowItem* w; + int n; + + for (w = window_, n = XCODEC_WINDOW_COUNT; n > 0; --n, ++w) + if (w->hash == hash) + w->hash = 0; + } +}; + + +class XCodecMemoryCache : public XCodecCache +{ + typedef __gnu_cxx::hash_map segment_hash_map_t; + segment_hash_map_t segment_hash_map_; + LogHandle log_; + +public: + XCodecMemoryCache (const UUID& uuid, size_t size) + : XCodecCache(uuid, size), + log_("/xcodec/cache/memory") + { } + + ~XCodecMemoryCache() + { + segment_hash_map_t::const_iterator it; + for (it = segment_hash_map_.begin(); it != segment_hash_map_.end(); ++it) + delete[] it->second; + segment_hash_map_.clear(); + } + + void enter (const uint64_t& hash, const Buffer& buf, unsigned off) + { + ASSERT(log_, segment_hash_map_.find(hash) == segment_hash_map_.end()); + uint8_t* data = new uint8_t[XCODEC_SEGMENT_LENGTH]; + buf.copyout (data, off, XCODEC_SEGMENT_LENGTH); + segment_hash_map_[hash] = data; + } + + bool lookup (const uint64_t& hash, Buffer& buf) + { + const uint8_t* data; + if ((data = find_recent (hash))) + { + buf.append (data, XCODEC_SEGMENT_LENGTH); + return true; + } + segment_hash_map_t::const_iterator it = segment_hash_map_.find (hash); + if (it != segment_hash_map_.end ()) + { + buf.append (it->second, XCODEC_SEGMENT_LENGTH); + remember (hash, it->second); + return true; + } + return false; + } +}; + +#endif /* !XCODEC_XCODEC_CACHE_H */ diff --git a/xcodec/xcodec_decoder.cc b/xcodec/xcodec_decoder.cc new file mode 100644 index 0000000..090b3a5 --- /dev/null +++ b/xcodec/xcodec_decoder.cc @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_decoder.cc // +// Description: decoding routines for the xcodex protocol // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +XCodecDecoder::XCodecDecoder(XCodecCache* cache) +: log_("/xcodec/decoder"), + cache_(cache) +{ } + +XCodecDecoder::~XCodecDecoder() +{ } + +/* + * XXX These comments are out-of-date. + * + * Decode an XCodec-encoded stream. Returns false if there was an + * inconsistency, error or unrecoverable condition in the stream. + * Returns true if we were able to process the stream entirely or + * expect to be able to finish processing it once more data arrives. + * The input buffer is cleared of anything we can parse right now. + * + * Since some events later in the stream (i.e. ASK or LEARN) may need + * to be processed before some earlier in the stream (i.e. REF), we + * parse the stream into a list of actions to take, performing them + * as we go if possible, otherwise queueing them to occur until the + * action that is blocking the stream has been satisfied or the stream + * has been closed. + * + * XXX For now we will ASK in every stream where an unknown hash has + * occurred and expect a LEARN in all of them. In the future, it is + * desirable to optimize this. Especially once we start putting an + * instance UUID in the HELLO message and can tell which streams + * share an originator. + */ +bool +XCodecDecoder::decode (Buffer& output, Buffer& input, std::set& unknown_hashes) +{ + uint8_t data[XCODEC_SEGMENT_LENGTH]; + Buffer old; + uint64_t behash; + uint64_t hash; + unsigned off; + uint8_t op; + + while (! input.empty()) + { + if (! input.find (XCODEC_MAGIC, &off)) + { + input.moveout (&output); + break; + } + + if (off > 0) + { + output.append (input, off); + input.skip (off); + } + ASSERT(log_, !input.empty()); + + /* + * Need the following byte at least. + */ + if (input.length() == 1) + break; + + input.extract (&op, sizeof XCODEC_MAGIC); + + switch (op) + { + case XCODEC_OP_ESCAPE: + output.append (XCODEC_MAGIC); + input.skip (sizeof XCODEC_MAGIC + sizeof op); + break; + + case XCODEC_OP_EXTRACT: + if (input.length() < sizeof XCODEC_MAGIC + sizeof op + XCODEC_SEGMENT_LENGTH) + return (true); + + input.skip (sizeof XCODEC_MAGIC + sizeof op); + input.copyout (data, XCODEC_SEGMENT_LENGTH); + hash = XCodecHash::hash (data); + + if (cache_->lookup (hash, old)) + { + if (old.equal (data, sizeof data)) + { + DEBUG(log_) << "Declaring segment already in cache."; + } + else + { + ERROR(log_) << "Collision in ."; + return (false); + } + old.clear (); + } + else + cache_->enter (hash, input, 0); + + output.append (input, XCODEC_SEGMENT_LENGTH); + input.skip (XCODEC_SEGMENT_LENGTH); + break; + + case XCODEC_OP_REF: + if (input.length() < sizeof XCODEC_MAGIC + sizeof op + sizeof behash) + return (true); + + input.extract (&behash, sizeof XCODEC_MAGIC + sizeof op); + hash = BigEndian::decode (behash); + + if (cache_->lookup (hash, output)) + { + input.skip (sizeof XCODEC_MAGIC + sizeof op + sizeof behash); + } + else + { + if (unknown_hashes.find (hash) == unknown_hashes.end()) + { + DEBUG(log_) << "Sending , waiting for ."; + unknown_hashes.insert (hash); + } + else + { + DEBUG(log_) << "Already sent , waiting for ."; + } + return (true); + } + break; + + default: + ERROR(log_) << "Unsupported XCodec opcode " << (unsigned)op << "."; + return (false); + } + } + + return (true); +} diff --git a/xcodec/xcodec_decoder.h b/xcodec/xcodec_decoder.h new file mode 100644 index 0000000..539f6aa --- /dev/null +++ b/xcodec/xcodec_decoder.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef XCODEC_XCODEC_DECODER_H +#define XCODEC_XCODEC_DECODER_H + +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_decoder.h // +// Description: decoding routines for the xcodex protocol // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class XCodecCache; + +class XCodecDecoder { + LogHandle log_; + XCodecCache* cache_; + +public: + XCodecDecoder(XCodecCache*); + ~XCodecDecoder(); + + bool decode (Buffer&, Buffer&, std::set&); +}; + +#endif /* !XCODEC_XCODEC_DECODER_H */ diff --git a/xcodec/xcodec_encoder.cc b/xcodec/xcodec_encoder.cc new file mode 100644 index 0000000..0502b86 --- /dev/null +++ b/xcodec/xcodec_encoder.cc @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2009-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_encoder.cc // +// Description: encoding routines for the xcodex protocol // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +struct candidate_symbol +{ + bool set_; + unsigned offset_; + uint64_t symbol_; +}; + +XCodecEncoder::XCodecEncoder(XCodecCache *cache) +: log_("/xcodec/encoder"), + cache_(cache) +{ } + +XCodecEncoder::~XCodecEncoder() +{ } + +/* + * This takes a view of a data stream and turns it into a series of references + * to other data, declarations of data to be referenced, and data that needs + * escaped. + */ +void +XCodecEncoder::encode (Buffer& output, Buffer& input) +{ + XCodecHash xcodec_hash; + candidate_symbol candidate = {0, 0, 0}; + unsigned offset = 0; + unsigned o = 0; + Buffer old; + + for (Buffer::SegmentIterator it = input.segments (); ! it.end (); it.next ()) + { + const BufferSegment* seg = *it; + const uint8_t *p, *q = seg->end (); + + for (p = seg->data (); p < q; ++p) + { + /* + * Add bytes to the hash until we have a complete hash. + */ + if (++o < XCODEC_SEGMENT_LENGTH) + xcodec_hash.add (*p); + else + { + if (o == XCODEC_SEGMENT_LENGTH) + xcodec_hash.add (*p); + else + xcodec_hash.roll (*p); + + /* + * And then mix the hash's internal state into a + * uint64_t that we can use to refer to that data + * and to look up possible past occurances of that + * data in the XCodecCache. + */ + uint64_t hash = xcodec_hash.mix (); + + /* + * If there is a pending candidate hash that wouldn't + * overlap with the data that the rolling hash presently + * covers, declare it now. + */ + if (candidate.set_ && candidate.offset_ + (XCODEC_SEGMENT_LENGTH * 2) <= offset + o) + { + encode_declaration (output, input, offset, candidate.offset_, candidate.symbol_); + o -= (candidate.offset_ + XCODEC_SEGMENT_LENGTH - offset); + offset = (candidate.offset_ + XCODEC_SEGMENT_LENGTH); + candidate.set_ = false; + } + + /* + * Now attempt to encode this hash as a reference if it + * has been defined before. + */ + + if (cache_->lookup (hash, old)) + { + /* + * This segment already exists. If it's + * identical to this chunk of data, then that's + * positively fantastic. + */ + if (encode_reference (output, input, offset, offset + o - XCODEC_SEGMENT_LENGTH, hash, old)) + { + /* + * We have output any data before this hash + * in escaped form, so any candidate hash + * before it is invalid now. + */ + offset += o; + o = 0; + xcodec_hash.reset(); + candidate.set_ = false; + } + else + { + /* + * This hash isn't usable because it collides + * with another, so keep looking for something + * viable. + */ + DEBUG(log_) << "Collision in first pass."; + } + + old.clear (); + } + else + { + /* + * Not defined before, it's a candidate for declaration + * if we don't already have one. + */ + if (candidate.set_) + { + /* + * We already have a hash that occurs earlier, + * isn't a collision and includes data that's + * covered by this hash, so don't remember it + * and keep going. + */ + ASSERT(log_, candidate.offset_ + (XCODEC_SEGMENT_LENGTH * 2) > offset + o); + } + else + { + /* + * The hash at this offset doesn't collide with any + * other and is the first viable hash we've seen so far + * in the stream, so remember it so that if we don't + * find something to reference we can declare this one + * for future use. + */ + candidate.offset_ = offset + o - XCODEC_SEGMENT_LENGTH; + candidate.symbol_ = hash; + candidate.set_ = true; + } + } + } + } + } + + /* + * There's a hash we can declare, do it. + */ + if (candidate.set_) + { + encode_declaration (output, input, offset, candidate.offset_, candidate.symbol_); + o -= (candidate.offset_ + XCODEC_SEGMENT_LENGTH - offset); + offset = (candidate.offset_ + XCODEC_SEGMENT_LENGTH); + candidate.set_ = false; + } + + /* + * There's data after that hash or no candidate hash, so + * just escape it. + */ + if (offset < input.length ()) + encode_escape (output, input, offset, input.length ()); +} + +void +XCodecEncoder::encode_declaration (Buffer& output, Buffer& input, unsigned offset, unsigned start, uint64_t hash) +{ + if (offset < start) + encode_escape (output, input, offset, start); + + cache_->enter (hash, input, start); + + output.append (XCODEC_MAGIC); + output.append (XCODEC_OP_EXTRACT); + output.append (input, start, XCODEC_SEGMENT_LENGTH); +} + +void +XCodecEncoder::encode_escape (Buffer& output, Buffer& input, unsigned offset, unsigned limit) +{ + unsigned pos; + + while (offset < limit && input.find (XCODEC_MAGIC, offset, limit - offset, &pos)) + { + if (offset < pos) + output.append (input, offset, pos - offset); + output.append (XCODEC_MAGIC); + output.append (XCODEC_OP_ESCAPE); + offset = pos + 1; + } + + if (offset < limit) + output.append (input, offset, limit - offset); +} + +bool +XCodecEncoder::encode_reference (Buffer& output, Buffer& input, unsigned offset, unsigned start, uint64_t hash, Buffer& old) +{ + uint8_t data[XCODEC_SEGMENT_LENGTH]; + input.copyout (data, start, XCODEC_SEGMENT_LENGTH); + + if (old.equal (data, sizeof data)) + { + if (offset < start) + encode_escape (output, input, offset, start); + + output.append (XCODEC_MAGIC); + output.append (XCODEC_OP_REF); + uint64_t behash = BigEndian::encode (hash); + output.append (&behash); + return true; + } + + return false; +} diff --git a/xcodec/xcodec_encoder.h b/xcodec/xcodec_encoder.h new file mode 100644 index 0000000..1aea77f --- /dev/null +++ b/xcodec/xcodec_encoder.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008-2011 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef XCODEC_XCODEC_ENCODER_H +#define XCODEC_XCODEC_ENCODER_H + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_encoder.h // +// Description: encoding routines for the xcodex protocol // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +class XCodecCache; + +class XCodecEncoder +{ + LogHandle log_; + XCodecCache* cache_; + +public: + XCodecEncoder(XCodecCache*); + ~XCodecEncoder(); + + void encode (Buffer&, Buffer&); +private: + void encode_declaration (Buffer&, Buffer&, unsigned, unsigned, uint64_t); + void encode_escape (Buffer&, Buffer&, unsigned, unsigned); + bool encode_reference (Buffer&, Buffer&, unsigned, unsigned, uint64_t, Buffer&); +}; + +#endif /* !XCODEC_XCODEC_ENCODER_H */ diff --git a/xcodec/xcodec_filter.cc b/xcodec/xcodec_filter.cc new file mode 100644 index 0000000..089beb7 --- /dev/null +++ b/xcodec/xcodec_filter.cc @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2011-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include "xcodec_filter.h" + +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_filter.cc // +// Description: instantiation of encoder/decoder in a data filter pair // +// Project: WANProxy XTech // +// Adapted by: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +/* + * Usage: + * length[uint8_t] data[uint8_t x length] + * + * Effects: + * Must appear at the start of and only at the start of an encoded stream. + * + * Sife-effects: + * Possibly many. + */ +#define XCODEC_PIPE_OP_HELLO ((uint8_t)0xff) + +/* + * Usage: + * data[uint8_t x XCODEC_PIPE_SEGMENT_LENGTH] + * + * Effects: + * The `data' is hashed, the hash is associated with the data if possible. + * + * Side-effects: + * None. + */ +#define XCODEC_PIPE_OP_LEARN ((uint8_t)0xfe) + +/* + * Usage: + * hash[uint64_t] + * + * Effects: + * An OP_LEARN will be sent in response with the data corresponding to the + * hash. + * + * If the hash is unknown, error will be indicated. + * + * Side-effects: + * None. + */ +#define XCODEC_PIPE_OP_ASK ((uint8_t)0xfd) + +/* + * Usage: + * + * + * Effects: + * Alert the other party that we have no intention of sending more data. + * + * Side-effects: + * The other party will send when it has processed all of + * the data we have sent. + */ +#define XCODEC_PIPE_OP_EOS ((uint8_t)0xfc) + +/* + * Usage: + * + * + * Effects: + * Alert the other party that we have no intention of reading more data. + * + * Side-effects: + * The connection will be torn down. + */ +#define XCODEC_PIPE_OP_EOS_ACK ((uint8_t)0xfb) + +/* + * Usage: + * length[uint16_t] data[uint8_t x length] + * + * Effects: + * Frames an encoded chunk. + * + * Side-effects: + * None. + */ +#define XCODEC_PIPE_OP_FRAME ((uint8_t)0x00) + +#define XCODEC_PIPE_MAX_FRAME (32768) + +// Encoding + +bool EncodeFilter::consume (Buffer& buf) +{ + Buffer output; + Buffer enc; + + ASSERT(log_, ! flushing_); + + if (! encoder_) + { + if (! cache_ || ! cache_->identifier().is_valid ()) + { + ERROR(log_) << "Could not encode UUID for ."; + return false; + } + + output.append (XCODEC_PIPE_OP_HELLO); + uint64_t mb = cache_->nominal_size (); + output.append ((uint8_t) (UUID_STRING_SIZE + sizeof mb)); + cache_->identifier().encode (output); + output.append (&mb); + + if (! (encoder_ = new XCodecEncoder (cache_))) + return false; + } + + encoder_->encode (enc, buf); + + while (! enc.empty ()) + { + int n = enc.length (); + if (n > XCODEC_PIPE_MAX_FRAME) + n = XCODEC_PIPE_MAX_FRAME; + + Buffer frame; + enc.moveout (&frame, n); + + uint16_t len = n; + len = BigEndian::encode (len); + + output.append (XCODEC_PIPE_OP_FRAME); + output.append (&len); + output.append (frame); + } + + return produce (output); +} + +void EncodeFilter::flush (int flg) +{ + if (flg == XCODEC_PIPE_OP_EOS_ACK) + eos_ack_ = true; + else + { + flushing_ = true; + flush_flags_ |= flg; + if (! sent_eos_) + { + Buffer output; + output.append (XCODEC_PIPE_OP_EOS); + sent_eos_ = produce (output); + } + } + if (flushing_ && eos_ack_) + Filter::flush (flush_flags_); +} + +// Decoding + +bool DecodeFilter::consume (Buffer& buf) +{ + if (! upstream_) + { + ERROR(log_) << "Decoder not configured"; + return false; + } + + pending_.append (buf); + + while (! pending_.empty ()) + { + uint8_t op = pending_.peek (); + switch (op) + { + case XCODEC_PIPE_OP_HELLO: + if (decoder_cache_) + { + ERROR(log_) << "Got twice."; + return false; + } + else + { + uint8_t len; + if (pending_.length() < sizeof op + sizeof len) + return true; + pending_.extract (&len, sizeof op); + if (pending_.length() < sizeof op + sizeof len + len) + return true; + + uint64_t mb; + if (len != UUID_STRING_SIZE + sizeof mb) + { + ERROR(log_) << "Unsupported length: " << (unsigned)len; + return false; + } + + UUID uuid; + pending_.skip (sizeof op + sizeof len); + if (! uuid.decode (pending_)) + { + ERROR(log_) << "Invalid UUID in ."; + return false; + } + pending_.extract (&mb); + pending_.skip (sizeof mb); + + if (! (decoder_cache_ = wanproxy.find_cache (uuid))) + decoder_cache_ = wanproxy.add_cache (uuid, mb); + + ASSERT(log_, decoder_ == NULL); + if (decoder_cache_) + decoder_ = new XCodecDecoder (decoder_cache_); + + DEBUG(log_) << "Peer connected with UUID: " << uuid; + } + break; + + case XCODEC_PIPE_OP_ASK: + if (! encoder_cache_) + { + ERROR(log_) << "Decoder not configured"; + return false; + } + else + { + uint64_t hash; + if (pending_.length() < sizeof op + sizeof hash) + return true; + + pending_.skip (sizeof op); + pending_.moveout (&hash); + hash = BigEndian::decode (hash); + + Buffer learn; + learn.append (XCODEC_PIPE_OP_LEARN); + if (encoder_cache_->lookup (hash, learn)) + { + DEBUG(log_) << "Responding to with ."; + if (! upstream_->produce (learn)) + return false; + } + else + { + ERROR(log_) << "Unknown hash in : " << hash; + return false; + } + } + break; + + case XCODEC_PIPE_OP_LEARN: + if (! decoder_cache_) + { + ERROR(log_) << "Got before ."; + return false; + } + else + { + if (pending_.length() < sizeof op + XCODEC_SEGMENT_LENGTH) + return true; + + pending_.skip (sizeof op); + uint8_t data[XCODEC_SEGMENT_LENGTH]; + pending_.copyout (data, XCODEC_SEGMENT_LENGTH); + uint64_t hash = XCodecHash::hash (data); + if (unknown_hashes_.find (hash) == unknown_hashes_.end ()) + INFO(log_) << "Gratuitous without ."; + else + unknown_hashes_.erase (hash); + + Buffer old; + if (decoder_cache_->lookup (hash, old)) + { + if (old.equal (data, sizeof data)) + { + DEBUG(log_) << "Redundant ."; + } + else + { + ERROR(log_) << "Collision in ."; + return false; + } + old.clear (); + } + else + { + DEBUG(log_) << "Successful ."; + decoder_cache_->enter (hash, pending_, 0); + } + pending_.skip (XCODEC_SEGMENT_LENGTH); + } + break; + + case XCODEC_PIPE_OP_EOS: + if (received_eos_) + { + ERROR(log_) << "Duplicate ."; + return false; + } + pending_.skip (sizeof op); + received_eos_ = true; + break; + + case XCODEC_PIPE_OP_EOS_ACK: + if (received_eos_ack_) + { + ERROR(log_) << "Duplicate ."; + return false; + } + pending_.skip (sizeof op); + received_eos_ack_ = true; + break; + + case XCODEC_PIPE_OP_FRAME: + if (! decoder_) + { + ERROR(log_) << "Got frame data before decoder initialized."; + return false; + } + else + { + uint16_t len; + if (pending_.length() < sizeof op + sizeof len) + return true; + + pending_.extract (&len, sizeof op); + len = BigEndian::decode (len); + if (len == 0 || len > XCODEC_PIPE_MAX_FRAME) + { + ERROR(log_) << "Invalid framed data length."; + return false; + } + if (pending_.length() < sizeof op + sizeof len + len) + return true; + + pending_.moveout (&frame_buffer_, sizeof op + sizeof len, len); + } + break; + + default: + ERROR(log_) << "Unsupported operation in pipe stream."; + return false; + } + + if (frame_buffer_.empty ()) + continue; + + if (! unknown_hashes_.empty ()) + { + DEBUG(log_) << "Waiting for unknown hashes to continue processing data."; + continue; + } + + Buffer output; + if (! decoder_->decode (output, frame_buffer_, unknown_hashes_)) + { + ERROR(log_) << "Decoder exiting with error."; + return false; + } + + if (! output.empty ()) + { + ASSERT(log_, ! flushing_); + if (! produce (output)) + return false; + } + else + { + /* + * We should only get no output from the decoder if + * we're waiting on the next frame or we need an + * unknown hash. It would be nice to make the + * encoder framing aware so that it would not end + * up with encoded data that straddles a frame + * boundary. (Fixing that would also allow us to + * simplify length checking within the decoder + * considerably.) + */ + ASSERT(log_, !frame_buffer_.empty() || !unknown_hashes_.empty()); + } + + Buffer ask; + std::set::const_iterator it; + for (it = unknown_hashes_.begin(); it != unknown_hashes_.end(); ++it) + { + uint64_t hash = *it; + hash = BigEndian::encode (hash); + ask.append (XCODEC_PIPE_OP_ASK); + ask.append (&hash); + } + if (! ask.empty ()) + { + DEBUG(log_) << "Sending s."; + if (! upstream_->produce (ask)) + return false; + } + } + + if (received_eos_ && ! sent_eos_ack_ && frame_buffer_.empty ()) + { + DEBUG(log_) << "Decoder received , sending ."; + + Buffer eos_ack; + eos_ack.append (XCODEC_PIPE_OP_EOS_ACK); + sent_eos_ack_ = true; + if (! upstream_->produce (eos_ack)) + return false; + } + + /* + * If we have received EOS and not yet sent it, we can send it now. + * The only caveat is that if we have outstanding s, i.e. we have + * not yet emptied decoder_unknown_hashes_, then we can't send EOS yet. + */ + if (received_eos_ && ! flushing_) + { + if (unknown_hashes_.empty ()) + { + if (! frame_buffer_.empty ()) + return false; + DEBUG(log_) << "Decoder received , shutting down decoder output channel."; + flushing_ = true; + Filter::flush (0); + } + else + { + if (frame_buffer_.empty ()) + return false; + DEBUG(log_) << "Decoder waiting to send until s are answered."; + } + } + + /* + * NB: + * Along with the comment above, there is some relevance here. If we + * use some kind of hierarchical decoding, then we need to be able to + * handle the case where an 's response necessitates us to send + * another or something of that sort. There are other conditions + * where we may still need to send something out of the encoder, but + * thankfully none seem to arise yet. + */ + if (sent_eos_ack_ && received_eos_ack_ && ! upflushed_) + { + ASSERT(log_, pending_.empty()); + ASSERT(log_, frame_buffer_.empty()); + DEBUG(log_) << "Decoder finished, got , shutting down encoder output channel."; + + upflushed_ = true; + upstream_->flush (XCODEC_PIPE_OP_EOS_ACK); + } + + return true; +} + +void DecodeFilter::flush (int flg) +{ + flushing_ = true; + flush_flags_ |= flg; + if (! pending_.empty ()) + DEBUG(log_) << "Flushing decoder with data outstanding."; + if (! frame_buffer_.empty ()) + DEBUG(log_) << "Flushing decoder with frame data outstanding."; + if (! upflushed_ && upstream_) + upstream_->flush (XCODEC_PIPE_OP_EOS_ACK); + Filter::flush (flush_flags_); +} + diff --git a/xcodec/xcodec_filter.h b/xcodec/xcodec_filter.h new file mode 100644 index 0000000..f2d8574 --- /dev/null +++ b/xcodec/xcodec_filter.h @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: xcodec_filter.h // +// Description: instantiation of encoder/decoder in a data filter pair // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-08-31 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef XCODEC_FILTER_H +#define XCODEC_FILTER_H + +#include +#include +#include +#include +#include +#include +#include + +class EncodeFilter : public BufferedFilter +{ +private: + XCodecCache* cache_; + XCodecEncoder* encoder_; + bool sent_eos_; + bool eos_ack_; + +public: + EncodeFilter (const LogHandle& log, XCodecCache* cc) : BufferedFilter (log) + { + cache_ = cc; encoder_ = 0; sent_eos_ = eos_ack_ = false; + } + + virtual ~EncodeFilter () + { + delete encoder_; + } + + virtual bool consume (Buffer& buf); + virtual void flush (int flg); +}; + +class DecodeFilter : public LogisticFilter +{ +private: + XCodecCache* encoder_cache_; + XCodecDecoder* decoder_; + XCodecCache* decoder_cache_; + std::set unknown_hashes_; + Buffer frame_buffer_; + bool received_eos_; + bool sent_eos_ack_; + bool received_eos_ack_; + bool upflushed_; + +public: + DecodeFilter (const LogHandle& log, XCodecCache* cc) : LogisticFilter (log) + { + encoder_cache_ = cc; decoder_ = 0; decoder_cache_ = 0; + received_eos_ = sent_eos_ack_ = received_eos_ack_ = upflushed_ = false; + } + + ~DecodeFilter () + { + delete decoder_; + } + + virtual bool consume (Buffer& buf); + virtual void flush (int flg); +}; + +#endif /* !XCODEC_FILTER_H */ diff --git a/xcodec/xcodec_hash.h b/xcodec/xcodec_hash.h new file mode 100644 index 0000000..366841e --- /dev/null +++ b/xcodec/xcodec_hash.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2008-2012 Juli Mallett. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef XCODEC_XCODEC_HASH_H +#define XCODEC_XCODEC_HASH_H + +#include + +class XCodecHash { + struct RollingHash { + uint32_t sum1_; /* Really <16-bit. */ + uint32_t sum2_; /* Really <32-bit. */ + uint32_t buffer_[XCODEC_SEGMENT_LENGTH]; /* Really >8-bit. */ + + RollingHash(void) + : sum1_(0), + sum2_(0), + buffer_() + { } + + void add(uint32_t ch, unsigned start) + { + buffer_[start] = ch; + + sum1_ += ch; + sum2_ += sum1_; + } + + void reset(void) + { + sum1_ = 0; + sum2_ = 0; + } + + void roll(uint32_t ch, unsigned start) + { + uint32_t dead; + + dead = buffer_[start]; + + sum1_ -= dead; + sum2_ -= dead * XCODEC_SEGMENT_LENGTH; + + buffer_[start] = ch; + + sum1_ += ch; + sum2_ += sum1_; + } + }; + + RollingHash bytes_; + RollingHash bits_; + unsigned start_; +#ifndef NDEBUG + unsigned length_; +#endif + +public: + XCodecHash(void) + : bytes_(), + bits_(), + start_(0) +#ifndef NDEBUG + , length_(0) +#endif + { } + + ~XCodecHash() + { } + + void add(uint8_t ch) + { + unsigned bit = ffs(ch); + unsigned word = (unsigned)ch + 1; + +#ifndef NDEBUG + ASSERT("/xcodec/hash", length_ < XCODEC_SEGMENT_LENGTH); +#endif + + bytes_.add(word, start_); + bits_.add(bit, start_); + +#ifndef NDEBUG + length_++; +#endif + start_ = (start_ + 1) % XCODEC_SEGMENT_LENGTH; + } + + void reset(void) + { + bytes_.reset(); + bits_.reset(); + +#ifndef NDEBUG + length_ = 0; +#endif + start_ = 0; + } + + void roll(uint8_t ch) + { + unsigned bit = ffs(ch); + unsigned word = (unsigned)ch + 1; + +#ifndef NDEBUG + ASSERT("/xcodec/hash", length_ == XCODEC_SEGMENT_LENGTH); +#endif + + bytes_.roll(word, start_); + bits_.roll(bit, start_); + + start_ = (start_ + 1) % XCODEC_SEGMENT_LENGTH; + } + + /* + * XXX + * Need to write a compression function for this; get rid of the + * completely non-entropic bits, anyway, and try to mix the others. + * + * Need to look at what bits can even possibly be set in sum2_, + * looking at all possible ranges resulting from: + * 128*data[0] + 127*data[1] + ... 1*data[127] + * + * It seems like since each data[] must have at least 1 bit set, + * there are a great many impossible values, and there is a large + * minimal value. Should be easy to compress, and would rather + * have the extra bits changing from the normalized (i.e. non-zero) + * input and have to compress than have no bits changing...but + * perhaps those are extensionally-equivalent and that's just + * hocus pocus computer science. Need to think more clearly and + * fully about it. + */ + uint64_t mix(void) const + { +#ifndef NDEBUG + ASSERT("/xcodec/hash", length_ == XCODEC_SEGMENT_LENGTH); +#endif + + uint64_t bits_hash = (bits_.sum1_ << 16) + bits_.sum2_; + uint64_t bytes_hash = (bytes_.sum1_ << 20) + bytes_.sum2_; + return ((bits_hash << 36) + bytes_hash); + } + + static uint64_t hash(const uint8_t *data) + { + XCodecHash xchash; + unsigned i; + + for (i = 0; i < XCODEC_SEGMENT_LENGTH; i++) + xchash.add(*data++); + return (xchash.mix()); + } +}; + +#endif /* !XCODEC_XCODEC_HASH_H */ diff --git a/zlib/Makefile b/zlib/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/zlib/lib.mk b/zlib/lib.mk new file mode 100644 index 0000000..a2ad691 --- /dev/null +++ b/zlib/lib.mk @@ -0,0 +1,5 @@ +VPATH+= ${TOPDIR}/zlib + +SRCS+= zlib_filter.cc + +LDADD+= -lz diff --git a/zlib/zlib_filter.cc b/zlib/zlib_filter.cc new file mode 100644 index 0000000..4979071 --- /dev/null +++ b/zlib/zlib_filter.cc @@ -0,0 +1,163 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: zlib_filter.cc // +// Description: data filters for zlib inflate/deflate streams // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#include "zlib_filter.h" + +// Deflate + +DeflateFilter::DeflateFilter (int level) : BufferedFilter ("/zlib/deflate") +{ + stream_.zalloc = Z_NULL; + stream_.zfree = Z_NULL; + stream_.opaque = Z_NULL; + stream_.next_in = Z_NULL; + stream_.avail_in = 0; + stream_.next_out = outbuf; + stream_.avail_out = sizeof outbuf; + + if (deflateInit (&stream_, level) != Z_OK) + CRITICAL(log_) << "Could not initialize deflate stream."; +} + +DeflateFilter::~DeflateFilter () +{ + if (deflateEnd (&stream_) != Z_OK) + ERROR(log_) << "Deflate stream did not end cleanly."; +} + +bool DeflateFilter::consume (Buffer& buf) +{ + const BufferSegment* seg; + int cnt = 0, i = 0, rv; + + for (Buffer::SegmentIterator it = buf.segments (); ! it.end (); it.next (), ++cnt); + pending_.clear (); + + for (Buffer::SegmentIterator it = buf.segments (); ! it.end (); it.next (), ++i) + { + seg = *it; + stream_.next_in = (Bytef*) (uintptr_t) seg->data (); + stream_.avail_in = seg->length (); + + while (stream_.avail_in > 0) + { + rv = deflate (&stream_, (i < cnt - 1 ? Z_NO_FLUSH : Z_SYNC_FLUSH)); + if (rv == Z_STREAM_ERROR || rv == Z_DATA_ERROR || rv == Z_MEM_ERROR) + { + ERROR(log_) << "deflate(): " << zError(rv); + return false; + } + + if (stream_.avail_out < sizeof outbuf) + { + pending_.append (outbuf, sizeof outbuf - stream_.avail_out); + stream_.next_out = outbuf; + stream_.avail_out = sizeof outbuf; + } + } + } + + return produce (pending_); +} + +void DeflateFilter::flush (int flg) +{ + pending_.clear (); + stream_.next_in = Z_NULL; + stream_.avail_in = 0; + + while (deflate (&stream_, Z_FINISH) == Z_OK && stream_.avail_out < sizeof outbuf) + { + pending_.append (outbuf, sizeof outbuf - stream_.avail_out); + stream_.next_out = outbuf; + stream_.avail_out = sizeof outbuf; + } + + if (! pending_.empty ()) + produce (pending_); + + Filter::flush (flg); +} + +// Inflate + +InflateFilter::InflateFilter () : BufferedFilter ("/zlib/inflate") +{ + stream_.zalloc = Z_NULL; + stream_.zfree = Z_NULL; + stream_.opaque = Z_NULL; + stream_.next_in = Z_NULL; + stream_.avail_in = 0; + stream_.next_out = outbuf; + stream_.avail_out = sizeof outbuf; + + if (inflateInit (&stream_) != Z_OK) + CRITICAL(log_) << "Could not initialize inflate stream."; +} + +InflateFilter::~InflateFilter() +{ + if (inflateEnd (&stream_) != Z_OK) + ERROR(log_) << "Inflate stream did not end cleanly."; +} + +bool InflateFilter::consume (Buffer& buf) +{ + const BufferSegment* seg; + int cnt = 0, i = 0, rv; + + for (Buffer::SegmentIterator it = buf.segments (); ! it.end (); it.next (), ++cnt); + pending_.clear (); + + for (Buffer::SegmentIterator it = buf.segments (); ! it.end (); it.next (), ++i) + { + seg = *it; + stream_.next_in = (Bytef*) (uintptr_t) seg->data (); + stream_.avail_in = seg->length (); + + while (stream_.avail_in > 0) + { + rv = inflate (&stream_, (i < cnt - 1 ? Z_NO_FLUSH : Z_SYNC_FLUSH)); + if (rv == Z_NEED_DICT || rv == Z_DATA_ERROR || rv == Z_MEM_ERROR) + { + ERROR(log_) << "inflate(): " << zError(rv); + return false; + } + + if (stream_.avail_out < sizeof outbuf) + { + pending_.append (outbuf, sizeof outbuf - stream_.avail_out); + stream_.next_out = outbuf; + stream_.avail_out = sizeof outbuf; + } + } + } + + return produce (pending_); +} + +void InflateFilter::flush (int flg) +{ + pending_.clear (); + stream_.next_in = Z_NULL; + stream_.avail_in = 0; + + while (inflate (&stream_, Z_FINISH) == Z_OK && stream_.avail_out < sizeof outbuf) + { + pending_.append (outbuf, sizeof outbuf - stream_.avail_out); + stream_.next_out = outbuf; + stream_.avail_out = sizeof outbuf; + } + + if (! pending_.empty ()) + produce (pending_); + + Filter::flush (flg); +} diff --git a/zlib/zlib_filter.h b/zlib/zlib_filter.h new file mode 100644 index 0000000..eb79e36 --- /dev/null +++ b/zlib/zlib_filter.h @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// File: zlib_filter.h // +// Description: data filters for zlib inflate/deflate streams // +// Project: WANProxy XTech // +// Author: Andreu Vidal Bramfeld-Software // +// Last modified: 2015-04-01 // +// // +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ZLIB_DEFLATE_FILTER_H +#define ZLIB_DEFLATE_FILTER_H + +#include +#include + +#define DEFLATE_CHUNK_SIZE 0x10000 +#define INFLATE_CHUNK_SIZE 0x10000 + +class DeflateFilter : public BufferedFilter +{ +private: + z_stream stream_; + uint8_t outbuf[DEFLATE_CHUNK_SIZE]; + +public: + DeflateFilter (int level = 0); + virtual ~DeflateFilter (); + + virtual bool consume (Buffer& buf); + virtual void flush (int flg); +}; + +class InflateFilter : public BufferedFilter +{ +private: + z_stream stream_; + uint8_t outbuf[INFLATE_CHUNK_SIZE]; + +public: + InflateFilter (); + ~InflateFilter (); + + virtual bool consume (Buffer& buf); + virtual void flush (int flg); +}; + +#endif /* !ZLIB_DEFLATE_FILTER_H */