diff --git a/ChangeLog b/ChangeLog index 4975002a9d..679542c6e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,126 @@ -222. [bug] jerry +238. [func] zhang likun + Implement the simplest forwarder, which pass everything throught + except QID, port number. The response will not be cached. + (Trac #598_new, git 8e28187a582820857ef2dae9b13637a3881f13ba) + +237. [bug] naokikambe + Resolved that the stats module wasn't configurable in bindctl in + spite of its having configuration items. The configuration part + was removed from the original spec file "stats.spec" and was + placed in a new spec file "stats-schema.spec". Because it means + definitions of statistics items. The command part is still + there. Thus stats module currently has no its own configuration, + and the items in "stats-schema.spec" are neither visible nor + configurable through bindctl. "stats-schema.spec" is shared with + stats module and stats-httpd module, and maybe with other + statistical modules in future. "stats.spec" has own configuration + and commands of stats module, if it requires. + (Trac#719, git a234b20dc6617392deb8a1e00eb0eed0ff353c0a) + +236. [func] jelte + C++ client side of configuration now uses BIND10 logging system. + It also has improved error handling when communicating with the + rest of the system. + (Trac #743, git 86632c12308c3ed099d75eb828f740c526dd7ec0) + +235. [func] jinmei + libdns++: added support for TSIG signing and verification. It can + be done using a newly introduced TSIGContext class. + Note: we temporarily disabled support for truncated signature + and modified some part of the code introduced in #226 accordingly. + We plan to fix this pretty soon. + (Trac #812, git ebe0c4b1e66d359227bdd1bd47395fee7b957f14) + (Trac #871, git 7c54055c0e47c7a0e36fcfab4b47ff180c0ca8c8) + (Trac #813, git ffa2f0672084c1f16e5784cdcdd55822f119feaa) + (Trac #893, git 5aaa6c0f628ed7c2093ecdbac93a2c8cf6c94349) + +234. [func] jerry + src/bin/xfrin: update xfrin to use TSIG. Currently it only supports + sending a signed TSIG request or SOA request. + (Trac #815, git a892818fb13a1839c82104523cb6cb359c970e88) + +233. [func] stephen + Added new-style logging statements to the NSAS code. + (Trac #745, git ceef68cd1223ae14d8412adbe18af2812ade8c2d) + +232. [func] stephen + To facilitate the writing of extended descriptions in + message files, altered the message file format. The message + is now flagged with a "%" as the first non-blank character + in the line and the lines in the extended description are + no longer preceded by a "+". + (Trac #900, git b395258c708b49a5da8d0cffcb48d83294354ba3) + +231. [func]* vorner + The logging interface changed slightly. We use + logger.foo(MESSAGE_ID).arg(bar); instead of logger.foo(MESSAGE_ID, + bar); internally. The message definitions use '%1,%2,...' + instead of '%s,%d', which allows us to cope better with + mismatched placeholders and allows reordering of them in + case of translation. + (Trac901, git 4903410e45670b30d7283f5d69dc28c2069237d6) + +230. [bug] naokikambe + Removed too repeated verbose messages in two cases of: + - when auth sends statistics data to stats + - when stats receives statistics data from other modules + (Trac#620, git 0ecb807011196eac01f281d40bc7c9d44565b364) + +229. [doc] jreed + Add manual page for b10-host. + (git a437d4e26b81bb07181ff35a625c540703eee845) + +228. [func]* jreed + The host tool is renamed to b10-host. While the utility is + a work in progress, it is expected to now be shipped with + tarballs. Its initial goal was to be a host(1) clone, + rewritten in C++ from scratch and using BIND 10's libdns++. + It now supports the -a (any), -c class, -d (verbose) switches + and has improved output. + (Trac #872, git d846851699d5c76937533adf9ff9d948dfd593ca) + +227. [build] jreed + Add missing libdns++ rdata files for the distribution (this + fixes distcheck error). Change three generated libdns++ + headers to "nodist" so they aren't included in the distribution + (they were mistakenly included in last tarball). + +226. [func]* jelte + Introduced an API for cryptographic operations. Currently it only + supports HMAC, intended for use with TSIG. The current + implementation uses Botan as the backend library. + This introduces a new dependency, on Botan. Currently only Botan + 1.8.x works; older or newer versions don't. + (Trac #781, git 9df42279a47eb617f586144dce8cce680598558a) + +225. [func] naokikambe + Added the HTTP/XML interface(b10-stats-httpd) to the + statistics feature in BIND 10. b10-stats-httpd is a standalone + HTTP server and it requests statistics data to the stats + daemon(b10-stats) and sends it to HTTP clients in XML + format. Items of the data collected via b10-stats-httpd + are almost equivalent to ones which are collected via + bindctl. Since it also can send XSL(Extensible Stylessheet + Language) document and XSD(XML Schema definition) document, + XML document is human-friendly to view through web browsers + and its data types are strictly defined. + (Trac #547, git 1cbd51919237a6e65983be46e4f5a63d1877b1d3) + +224. [bug] jinmei + b10-auth, src/lib/datasrc: inconsistency between the hot spot + cache and actual data source could cause a crash while query + processing. The crash could happen, e.g., when an sqlite3 DB file + is being updated after a zone transfer while b10-auth handles a + query using the corresponding sqlite3 data source. + (Trac #851, git 2463b96680bb3e9a76e50c38a4d7f1d38d810643) + +223. [bug] feng + If ip address or port isn't usable for name server, name + server process won't exist and give end user chance to + reconfigure them. + (Trac #775, git 572ac2cf62e18f7eb69d670b890e2a3443bfd6e7) + +222. [bug] jerry src/lib/zonemgr: Fix a bug that xfrin not checking for new copy of zone on startup. Imposes some random jitters to avoid many zones need to do refresh at the same time. @@ -13,7 +135,7 @@ (potentially) bad packets to a nameserver and prints the responses. (Trac #703, git 1b666838b6c0fe265522b30971e878d9f0d21fde) -219. [func] ocean +219. [func] ocean src/lib: move some dns related code out of asiolink library to asiodns library (Trac #751, git 262ac6c6fc61224d54705ed4c700dadb606fcb1c) @@ -23,9 +145,9 @@ (Trac #806, git 4e47d5f6b692c63c907af6681a75024450884a88) 217. [bug] jerry - src/lib/dns/python: Use a signed version of larger size of integer and - perform more strict range checks with PyArg_ParseTuple() in case of - overflows. + src/lib/dns/python: Use a signed version of larger size of + integer and perform more strict range checks with + PyArg_ParseTuple() in case of overflows. (Trac #363, git ce281e646be9f0f273229d94ccd75bf7e08d17cf) 216. [func] vorner @@ -1047,7 +1169,7 @@ bind10-devel-20100701 released on July 1, 2010 55. [bug] shane bin/xfrout: xfrout exception on Ctrl-C now no longer generates exception for 'Interrupted system call' - (Track #136, svn r2147) + (Trac #136, svn r2147) 54. [bug] zhanglikun bin/xfrout: Enable b10-xfrout can be launched in source diff --git a/Makefile.am b/Makefile.am index e31a1a5078..bab66795f3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -84,214 +84,310 @@ systest: #### include external sources in the distributed tarball: EXTRA_DIST = ext/asio/README -EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp -EXTRA_DIST += ext/asio/asio/local/basic_endpoint.hpp -EXTRA_DIST += ext/asio/asio/local/datagram_protocol.hpp -EXTRA_DIST += ext/asio/asio/local/connect_pair.hpp -EXTRA_DIST += ext/asio/asio/windows/basic_handle.hpp -EXTRA_DIST += ext/asio/asio/windows/random_access_handle.hpp -EXTRA_DIST += ext/asio/asio/windows/random_access_handle_service.hpp -EXTRA_DIST += ext/asio/asio/windows/basic_random_access_handle.hpp -EXTRA_DIST += ext/asio/asio/windows/overlapped_ptr.hpp -EXTRA_DIST += ext/asio/asio/windows/stream_handle.hpp -EXTRA_DIST += ext/asio/asio/windows/stream_handle_service.hpp -EXTRA_DIST += ext/asio/asio/windows/basic_stream_handle.hpp -EXTRA_DIST += ext/asio/asio/impl/write.ipp -EXTRA_DIST += ext/asio/asio/impl/read.ipp -EXTRA_DIST += ext/asio/asio/impl/serial_port_base.ipp -EXTRA_DIST += ext/asio/asio/impl/write_at.ipp -EXTRA_DIST += ext/asio/asio/impl/read_at.ipp -EXTRA_DIST += ext/asio/asio/impl/error_code.ipp -EXTRA_DIST += ext/asio/asio/impl/read_until.ipp -EXTRA_DIST += ext/asio/asio/impl/io_service.ipp -EXTRA_DIST += ext/asio/asio/detail/win_iocp_serial_port_service.hpp -EXTRA_DIST += ext/asio/asio/detail/reactive_socket_service.hpp -EXTRA_DIST += ext/asio/asio/detail/wince_thread.hpp -EXTRA_DIST += ext/asio/asio/detail/win_event.hpp -EXTRA_DIST += ext/asio/asio/detail/descriptor_ops.hpp -EXTRA_DIST += ext/asio/asio/detail/pipe_select_interrupter.hpp -EXTRA_DIST += ext/asio/asio/detail/task_io_service_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/handler_alloc_helpers.hpp -EXTRA_DIST += ext/asio/asio/detail/pop_options.hpp -EXTRA_DIST += ext/asio/asio/detail/strand_service.hpp -EXTRA_DIST += ext/asio/asio/detail/buffer_resize_guard.hpp -EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/timer_scheduler.hpp -EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/hash_map.hpp -EXTRA_DIST += ext/asio/asio/detail/event.hpp -EXTRA_DIST += ext/asio/asio/detail/buffered_stream_storage.hpp -EXTRA_DIST += ext/asio/asio/detail/posix_mutex.hpp -EXTRA_DIST += ext/asio/asio/detail/timer_queue_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/deadline_timer_service.hpp -EXTRA_DIST += ext/asio/asio/detail/posix_fd_set_adapter.hpp -EXTRA_DIST += ext/asio/asio/detail/service_registry.hpp +EXTRA_DIST += ext/asio/README +EXTRA_DIST += ext/asio/asio.hpp +EXTRA_DIST += ext/asio/asio/basic_socket.hpp +EXTRA_DIST += ext/asio/asio/streambuf.hpp +EXTRA_DIST += ext/asio/asio/thread.hpp +EXTRA_DIST += ext/asio/asio/detail/wait_handler.hpp +EXTRA_DIST += ext/asio/asio/detail/resolve_op.hpp +EXTRA_DIST += ext/asio/asio/detail/gcc_hppa_fenced_block.hpp EXTRA_DIST += ext/asio/asio/detail/null_fenced_block.hpp -EXTRA_DIST += ext/asio/asio/detail/task_io_service_operation.hpp -EXTRA_DIST += ext/asio/asio/detail/epoll_reactor.hpp -EXTRA_DIST += ext/asio/asio/detail/local_free_on_block_exit.hpp -EXTRA_DIST += ext/asio/asio/detail/socket_select_interrupter.hpp -EXTRA_DIST += ext/asio/asio/detail/null_mutex.hpp -EXTRA_DIST += ext/asio/asio/detail/reactor_op.hpp -EXTRA_DIST += ext/asio/asio/detail/fenced_block.hpp -EXTRA_DIST += ext/asio/asio/detail/tss_ptr.hpp -EXTRA_DIST += ext/asio/asio/detail/timer_queue_set.hpp -EXTRA_DIST += ext/asio/asio/detail/winsock_init.hpp -EXTRA_DIST += ext/asio/asio/detail/signal_init.hpp -EXTRA_DIST += ext/asio/asio/detail/call_stack.hpp -EXTRA_DIST += ext/asio/asio/detail/operation.hpp -EXTRA_DIST += ext/asio/asio/detail/posix_signal_blocker.hpp -EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_service.hpp -EXTRA_DIST += ext/asio/asio/detail/fd_set_adapter.hpp -EXTRA_DIST += ext/asio/asio/detail/io_control.hpp -EXTRA_DIST += ext/asio/asio/detail/null_signal_blocker.hpp -EXTRA_DIST += ext/asio/asio/detail/socket_ops.hpp -EXTRA_DIST += ext/asio/asio/detail/bind_handler.hpp -EXTRA_DIST += ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp -EXTRA_DIST += ext/asio/asio/detail/timer_scheduler_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/signal_blocker.hpp -EXTRA_DIST += ext/asio/asio/detail/consuming_buffers.hpp -EXTRA_DIST += ext/asio/asio/detail/socket_option.hpp -EXTRA_DIST += ext/asio/asio/detail/reactor.hpp -EXTRA_DIST += ext/asio/asio/detail/win_fd_set_adapter.hpp -EXTRA_DIST += ext/asio/asio/detail/select_interrupter.hpp -EXTRA_DIST += ext/asio/asio/detail/null_buffers_op.hpp -EXTRA_DIST += ext/asio/asio/detail/socket_holder.hpp -EXTRA_DIST += ext/asio/asio/detail/scoped_lock.hpp -EXTRA_DIST += ext/asio/asio/detail/service_registry_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/base_from_completion_cond.hpp -EXTRA_DIST += ext/asio/asio/detail/posix_thread.hpp -EXTRA_DIST += ext/asio/asio/detail/solaris_fenced_block.hpp -EXTRA_DIST += ext/asio/asio/detail/epoll_reactor_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/push_options.hpp -EXTRA_DIST += ext/asio/asio/detail/win_signal_blocker.hpp -EXTRA_DIST += ext/asio/asio/detail/eventfd_select_interrupter.hpp -EXTRA_DIST += ext/asio/asio/detail/select_reactor.hpp -EXTRA_DIST += ext/asio/asio/detail/old_win_sdk_compat.hpp -EXTRA_DIST += ext/asio/asio/detail/reactor_op_queue.hpp -EXTRA_DIST += ext/asio/asio/detail/null_thread.hpp -EXTRA_DIST += ext/asio/asio/detail/buffer_sequence_adapter.hpp -EXTRA_DIST += ext/asio/asio/detail/posix_event.hpp -EXTRA_DIST += ext/asio/asio/detail/thread.hpp -EXTRA_DIST += ext/asio/asio/detail/handler_invoke_helpers.hpp -EXTRA_DIST += ext/asio/asio/detail/null_event.hpp -EXTRA_DIST += ext/asio/asio/detail/service_id.hpp -EXTRA_DIST += ext/asio/asio/detail/socket_types.hpp -EXTRA_DIST += ext/asio/asio/detail/throw_error.hpp -EXTRA_DIST += ext/asio/asio/detail/timer_op.hpp -EXTRA_DIST += ext/asio/asio/detail/win_mutex.hpp -EXTRA_DIST += ext/asio/asio/detail/reactive_descriptor_service.hpp -EXTRA_DIST += ext/asio/asio/detail/resolver_service.hpp -EXTRA_DIST += ext/asio/asio/detail/op_queue.hpp -EXTRA_DIST += ext/asio/asio/detail/dev_poll_reactor.hpp -EXTRA_DIST += ext/asio/asio/detail/win_thread.hpp -EXTRA_DIST += ext/asio/asio/detail/win_iocp_operation.hpp -EXTRA_DIST += ext/asio/asio/detail/service_base.hpp -EXTRA_DIST += ext/asio/asio/detail/select_reactor_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/reactor_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_service.hpp -EXTRA_DIST += ext/asio/asio/detail/wrapped_handler.hpp -EXTRA_DIST += ext/asio/asio/detail/mutex.hpp -EXTRA_DIST += ext/asio/asio/detail/completion_handler.hpp EXTRA_DIST += ext/asio/asio/detail/noncopyable.hpp -EXTRA_DIST += ext/asio/asio/detail/task_io_service.hpp -EXTRA_DIST += ext/asio/asio/detail/gcc_fenced_block.hpp -EXTRA_DIST += ext/asio/asio/detail/win_fenced_block.hpp -EXTRA_DIST += ext/asio/asio/detail/win_tss_ptr.hpp -EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor.hpp -EXTRA_DIST += ext/asio/asio/detail/timer_queue_base.hpp -EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service.hpp -EXTRA_DIST += ext/asio/asio/detail/gcc_x86_fenced_block.hpp -EXTRA_DIST += ext/asio/asio/detail/posix_tss_ptr.hpp +EXTRA_DIST += ext/asio/asio/detail/eventfd_select_interrupter.hpp +EXTRA_DIST += ext/asio/asio/detail/task_io_service_operation.hpp +EXTRA_DIST += ext/asio/asio/detail/service_base.hpp +EXTRA_DIST += ext/asio/asio/detail/task_io_service_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/null_buffers_op.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_write_op.hpp +EXTRA_DIST += ext/asio/asio/detail/thread.hpp +EXTRA_DIST += ext/asio/asio/detail/select_reactor_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/event.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_descriptor_service.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_overlapped_op.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_recv_op.hpp EXTRA_DIST += ext/asio/asio/detail/macos_fenced_block.hpp EXTRA_DIST += ext/asio/asio/detail/dev_poll_reactor_fwd.hpp -EXTRA_DIST += ext/asio/asio/detail/timer_queue.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_service.hpp +EXTRA_DIST += ext/asio/asio/detail/posix_tss_ptr.hpp +EXTRA_DIST += ext/asio/asio/detail/local_free_on_block_exit.hpp +EXTRA_DIST += ext/asio/asio/detail/timer_scheduler.hpp +EXTRA_DIST += ext/asio/asio/detail/signal_blocker.hpp +EXTRA_DIST += ext/asio/asio/detail/resolver_service_base.hpp +EXTRA_DIST += ext/asio/asio/detail/socket_holder.hpp +EXTRA_DIST += ext/asio/asio/detail/dev_poll_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/select_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/gcc_arm_fenced_block.hpp +EXTRA_DIST += ext/asio/asio/detail/consuming_buffers.hpp +EXTRA_DIST += ext/asio/asio/detail/reactor_op.hpp +EXTRA_DIST += ext/asio/asio/detail/base_from_completion_cond.hpp +EXTRA_DIST += ext/asio/asio/detail/epoll_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/bind_handler.hpp +EXTRA_DIST += ext/asio/asio/detail/strand_service.hpp +EXTRA_DIST += ext/asio/asio/detail/op_queue.hpp +EXTRA_DIST += ext/asio/asio/detail/win_mutex.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_operation.hpp +EXTRA_DIST += ext/asio/asio/detail/pipe_select_interrupter.hpp +EXTRA_DIST += ext/asio/asio/detail/wince_thread.hpp +EXTRA_DIST += ext/asio/asio/detail/buffered_stream_storage.hpp +EXTRA_DIST += ext/asio/asio/detail/mutex.hpp +EXTRA_DIST += ext/asio/asio/detail/posix_mutex.hpp +EXTRA_DIST += ext/asio/asio/detail/reactor_op_queue.hpp +EXTRA_DIST += ext/asio/asio/detail/win_event.hpp +EXTRA_DIST += ext/asio/asio/detail/select_interrupter.hpp +EXTRA_DIST += ext/asio/asio/detail/io_control.hpp +EXTRA_DIST += ext/asio/asio/detail/buffer_sequence_adapter.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_service.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_send_op.hpp +EXTRA_DIST += ext/asio/asio/detail/epoll_reactor_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/operation.hpp +EXTRA_DIST += ext/asio/asio/detail/descriptor_ops.hpp +EXTRA_DIST += ext/asio/asio/detail/reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/shared_ptr.hpp +EXTRA_DIST += ext/asio/asio/detail/winsock_init.hpp +EXTRA_DIST += ext/asio/asio/detail/timer_queue_set.hpp +EXTRA_DIST += ext/asio/asio/detail/completion_handler.hpp EXTRA_DIST += ext/asio/asio/detail/reactive_serial_port_service.hpp +EXTRA_DIST += ext/asio/asio/detail/fenced_block.hpp +EXTRA_DIST += ext/asio/asio/detail/null_event.hpp +EXTRA_DIST += ext/asio/asio/detail/hash_map.hpp +EXTRA_DIST += ext/asio/asio/detail/gcc_sync_fenced_block.hpp +EXTRA_DIST += ext/asio/asio/detail/win_tss_ptr.hpp +EXTRA_DIST += ext/asio/asio/detail/win_fd_set_adapter.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_null_buffers_op.hpp +EXTRA_DIST += ext/asio/asio/detail/timer_queue_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/old_win_sdk_compat.hpp +EXTRA_DIST += ext/asio/asio/detail/call_stack.hpp +EXTRA_DIST += ext/asio/asio/detail/weak_ptr.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_accept_op.hpp +EXTRA_DIST += ext/asio/asio/detail/gcc_x86_fenced_block.hpp +EXTRA_DIST += ext/asio/asio/detail/gcc_fenced_block.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_service.hpp +EXTRA_DIST += ext/asio/asio/detail/null_mutex.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp +EXTRA_DIST += ext/asio/asio/detail/posix_event.hpp +EXTRA_DIST += ext/asio/asio/detail/service_id.hpp +EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/regex_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_sendto_op.hpp +EXTRA_DIST += ext/asio/asio/detail/push_options.hpp +EXTRA_DIST += ext/asio/asio/detail/null_thread.hpp +EXTRA_DIST += ext/asio/asio/detail/socket_select_interrupter.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_service_base.hpp +EXTRA_DIST += ext/asio/asio/detail/throw_error.hpp +EXTRA_DIST += ext/asio/asio/detail/null_signal_blocker.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_accept_op.hpp +EXTRA_DIST += ext/asio/asio/detail/wrapped_handler.hpp +EXTRA_DIST += ext/asio/asio/detail/object_pool.hpp +EXTRA_DIST += ext/asio/asio/detail/timer_scheduler_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/resolve_endpoint_op.hpp +EXTRA_DIST += ext/asio/asio/detail/array_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/config.hpp +EXTRA_DIST += ext/asio/asio/detail/socket_option.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp +EXTRA_DIST += ext/asio/asio/detail/win_fenced_block.hpp +EXTRA_DIST += ext/asio/asio/detail/socket_types.hpp EXTRA_DIST += ext/asio/asio/detail/null_tss_ptr.hpp -EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_stream_service.hpp -EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_operation.hpp -EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_init.hpp -EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_context_service.hpp -EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_types.hpp -EXTRA_DIST += ext/asio/asio/ssl/stream.hpp -EXTRA_DIST += ext/asio/asio/ssl/stream_service.hpp +EXTRA_DIST += ext/asio/asio/detail/handler_invoke_helpers.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_send_op.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_null_buffers_op.hpp +EXTRA_DIST += ext/asio/asio/detail/pop_options.hpp +EXTRA_DIST += ext/asio/asio/detail/resolver_service.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_service_base.hpp +EXTRA_DIST += ext/asio/asio/detail/descriptor_read_op.hpp +EXTRA_DIST += ext/asio/asio/detail/reactive_socket_connect_op.hpp +EXTRA_DIST += ext/asio/asio/detail/timer_queue_base.hpp +EXTRA_DIST += ext/asio/asio/detail/reactor_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_serial_port_service.hpp +EXTRA_DIST += ext/asio/asio/detail/tss_ptr.hpp +EXTRA_DIST += ext/asio/asio/detail/buffer_resize_guard.hpp +EXTRA_DIST += ext/asio/asio/detail/kqueue_reactor_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/win_thread.hpp +EXTRA_DIST += ext/asio/asio/detail/deadline_timer_service.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_socket_recv_op.hpp +EXTRA_DIST += ext/asio/asio/detail/timer_op.hpp +EXTRA_DIST += ext/asio/asio/detail/posix_thread.hpp +EXTRA_DIST += ext/asio/asio/detail/signal_init.hpp +EXTRA_DIST += ext/asio/asio/detail/descriptor_write_op.hpp +EXTRA_DIST += ext/asio/asio/detail/win_signal_blocker.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/reactive_socket_service_base.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_mutex.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/posix_event.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_io_service.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/select_reactor.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/posix_tss_ptr.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/task_io_service.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/dev_poll_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/select_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/epoll_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/strand_service.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/winsock_init.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/pipe_select_interrupter.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/dev_poll_reactor.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_io_service.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/strand_service.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/timer_queue.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/posix_mutex.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/reactive_serial_port_service.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/socket_ops.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/socket_select_interrupter.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/posix_thread.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/reactive_descriptor_service.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/kqueue_reactor.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/kqueue_reactor.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_event.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/timer_queue_set.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_tss_ptr.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_iocp_handle_service.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/resolver_service_base.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/win_thread.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/task_io_service.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/throw_error.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/epoll_reactor.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/service_registry.hpp +EXTRA_DIST += ext/asio/asio/detail/impl/descriptor_ops.ipp +EXTRA_DIST += ext/asio/asio/detail/impl/service_registry.ipp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_io_service_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/fd_set_adapter.hpp +EXTRA_DIST += ext/asio/asio/detail/task_io_service.hpp +EXTRA_DIST += ext/asio/asio/detail/solaris_fenced_block.hpp +EXTRA_DIST += ext/asio/asio/detail/timer_queue.hpp +EXTRA_DIST += ext/asio/asio/detail/handler_alloc_helpers.hpp +EXTRA_DIST += ext/asio/asio/detail/scoped_lock.hpp +EXTRA_DIST += ext/asio/asio/detail/win_iocp_handle_read_op.hpp +EXTRA_DIST += ext/asio/asio/detail/service_registry_fwd.hpp +EXTRA_DIST += ext/asio/asio/detail/service_registry.hpp +EXTRA_DIST += ext/asio/asio/detail/posix_fd_set_adapter.hpp +EXTRA_DIST += ext/asio/asio/detail/socket_ops.hpp +EXTRA_DIST += ext/asio/asio/detail/posix_signal_blocker.hpp +EXTRA_DIST += ext/asio/asio/serial_port_base.hpp EXTRA_DIST += ext/asio/asio/ssl/context_base.hpp EXTRA_DIST += ext/asio/asio/ssl/context.hpp EXTRA_DIST += ext/asio/asio/ssl/context_service.hpp +EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_types.hpp +EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_context_service.hpp +EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_stream_service.hpp +EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_operation.hpp +EXTRA_DIST += ext/asio/asio/ssl/detail/openssl_init.hpp EXTRA_DIST += ext/asio/asio/ssl/basic_context.hpp +EXTRA_DIST += ext/asio/asio/ssl/stream_service.hpp +EXTRA_DIST += ext/asio/asio/ssl/stream.hpp EXTRA_DIST += ext/asio/asio/ssl/stream_base.hpp -EXTRA_DIST += ext/asio/asio/posix/stream_descriptor.hpp -EXTRA_DIST += ext/asio/asio/posix/stream_descriptor_service.hpp +EXTRA_DIST += ext/asio/asio/basic_streambuf.hpp +EXTRA_DIST += ext/asio/asio/serial_port_service.hpp +EXTRA_DIST += ext/asio/asio/error.hpp +EXTRA_DIST += ext/asio/asio/handler_alloc_hook.hpp +EXTRA_DIST += ext/asio/asio/buffers_iterator.hpp +EXTRA_DIST += ext/asio/asio/is_read_buffered.hpp +EXTRA_DIST += ext/asio/asio/buffered_stream_fwd.hpp +EXTRA_DIST += ext/asio/asio/placeholders.hpp +EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp +EXTRA_DIST += ext/asio/asio/local/detail/impl/endpoint.ipp +EXTRA_DIST += ext/asio/asio/local/detail/endpoint.hpp +EXTRA_DIST += ext/asio/asio/local/datagram_protocol.hpp +EXTRA_DIST += ext/asio/asio/local/connect_pair.hpp +EXTRA_DIST += ext/asio/asio/local/basic_endpoint.hpp +EXTRA_DIST += ext/asio/asio/buffered_stream.hpp +EXTRA_DIST += ext/asio/asio/basic_serial_port.hpp +EXTRA_DIST += ext/asio/asio/datagram_socket_service.hpp +EXTRA_DIST += ext/asio/asio/socket_base.hpp +EXTRA_DIST += ext/asio/asio/io_service.hpp +EXTRA_DIST += ext/asio/asio/ssl.hpp +EXTRA_DIST += ext/asio/asio/basic_socket_iostream.hpp +EXTRA_DIST += ext/asio/asio/basic_io_object.hpp +EXTRA_DIST += ext/asio/asio/basic_socket_streambuf.hpp +EXTRA_DIST += ext/asio/asio/error_code.hpp +EXTRA_DIST += ext/asio/asio/basic_stream_socket.hpp +EXTRA_DIST += ext/asio/asio/read_until.hpp +EXTRA_DIST += ext/asio/asio/basic_streambuf_fwd.hpp +EXTRA_DIST += ext/asio/asio/is_write_buffered.hpp +EXTRA_DIST += ext/asio/asio/basic_datagram_socket.hpp +EXTRA_DIST += ext/asio/asio/buffered_write_stream_fwd.hpp +EXTRA_DIST += ext/asio/asio/basic_deadline_timer.hpp +EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp +EXTRA_DIST += ext/asio/asio/raw_socket_service.hpp +EXTRA_DIST += ext/asio/asio/buffered_read_stream.hpp +EXTRA_DIST += ext/asio/asio/time_traits.hpp +EXTRA_DIST += ext/asio/asio/completion_condition.hpp EXTRA_DIST += ext/asio/asio/posix/basic_stream_descriptor.hpp EXTRA_DIST += ext/asio/asio/posix/basic_descriptor.hpp EXTRA_DIST += ext/asio/asio/posix/descriptor_base.hpp +EXTRA_DIST += ext/asio/asio/posix/stream_descriptor_service.hpp +EXTRA_DIST += ext/asio/asio/posix/stream_descriptor.hpp +EXTRA_DIST += ext/asio/asio/write.hpp +EXTRA_DIST += ext/asio/asio/write_at.hpp +EXTRA_DIST += ext/asio/asio/basic_raw_socket.hpp +EXTRA_DIST += ext/asio/asio/serial_port.hpp +EXTRA_DIST += ext/asio/asio/windows/basic_stream_handle.hpp +EXTRA_DIST += ext/asio/asio/windows/basic_handle.hpp +EXTRA_DIST += ext/asio/asio/windows/random_access_handle.hpp +EXTRA_DIST += ext/asio/asio/windows/overlapped_ptr.hpp +EXTRA_DIST += ext/asio/asio/windows/stream_handle.hpp +EXTRA_DIST += ext/asio/asio/windows/random_access_handle_service.hpp +EXTRA_DIST += ext/asio/asio/windows/stream_handle_service.hpp +EXTRA_DIST += ext/asio/asio/windows/basic_random_access_handle.hpp +EXTRA_DIST += ext/asio/asio/read.hpp +EXTRA_DIST += ext/asio/asio/deadline_timer_service.hpp +EXTRA_DIST += ext/asio/asio/buffered_write_stream.hpp +EXTRA_DIST += ext/asio/asio/buffer.hpp +EXTRA_DIST += ext/asio/asio/impl/read_until.ipp +EXTRA_DIST += ext/asio/asio/impl/serial_port_base.hpp +EXTRA_DIST += ext/asio/asio/impl/read_at.ipp +EXTRA_DIST += ext/asio/asio/impl/read.ipp +EXTRA_DIST += ext/asio/asio/impl/error.ipp +EXTRA_DIST += ext/asio/asio/impl/io_service.ipp +EXTRA_DIST += ext/asio/asio/impl/io_service.hpp +EXTRA_DIST += ext/asio/asio/impl/src.hpp +EXTRA_DIST += ext/asio/asio/impl/src.cpp +EXTRA_DIST += ext/asio/asio/impl/read_until.hpp +EXTRA_DIST += ext/asio/asio/impl/serial_port_base.ipp +EXTRA_DIST += ext/asio/asio/impl/write.hpp +EXTRA_DIST += ext/asio/asio/impl/write_at.hpp +EXTRA_DIST += ext/asio/asio/impl/write.ipp +EXTRA_DIST += ext/asio/asio/impl/read.hpp +EXTRA_DIST += ext/asio/asio/impl/write_at.ipp +EXTRA_DIST += ext/asio/asio/impl/error_code.ipp +EXTRA_DIST += ext/asio/asio/impl/read_at.hpp +EXTRA_DIST += ext/asio/asio/strand.hpp +EXTRA_DIST += ext/asio/asio/version.hpp +EXTRA_DIST += ext/asio/asio/basic_socket_acceptor.hpp +EXTRA_DIST += ext/asio/asio/ip/basic_resolver_query.hpp +EXTRA_DIST += ext/asio/asio/ip/address.hpp +EXTRA_DIST += ext/asio/asio/ip/host_name.hpp EXTRA_DIST += ext/asio/asio/ip/detail/socket_option.hpp +EXTRA_DIST += ext/asio/asio/ip/detail/impl/endpoint.ipp +EXTRA_DIST += ext/asio/asio/ip/detail/endpoint.hpp +EXTRA_DIST += ext/asio/asio/ip/udp.hpp +EXTRA_DIST += ext/asio/asio/ip/basic_resolver_iterator.hpp EXTRA_DIST += ext/asio/asio/ip/v6_only.hpp EXTRA_DIST += ext/asio/asio/ip/address_v4.hpp -EXTRA_DIST += ext/asio/asio/ip/address_v6.hpp -EXTRA_DIST += ext/asio/asio/ip/basic_resolver.hpp -EXTRA_DIST += ext/asio/asio/ip/multicast.hpp -EXTRA_DIST += ext/asio/asio/ip/unicast.hpp -EXTRA_DIST += ext/asio/asio/ip/basic_resolver_iterator.hpp -EXTRA_DIST += ext/asio/asio/ip/host_name.hpp EXTRA_DIST += ext/asio/asio/ip/resolver_query_base.hpp -EXTRA_DIST += ext/asio/asio/ip/basic_endpoint.hpp -EXTRA_DIST += ext/asio/asio/ip/resolver_service.hpp -EXTRA_DIST += ext/asio/asio/ip/basic_resolver_entry.hpp -EXTRA_DIST += ext/asio/asio/ip/address.hpp +EXTRA_DIST += ext/asio/asio/ip/multicast.hpp +EXTRA_DIST += ext/asio/asio/ip/address_v6.hpp EXTRA_DIST += ext/asio/asio/ip/tcp.hpp -EXTRA_DIST += ext/asio/asio/ip/basic_resolver_query.hpp -EXTRA_DIST += ext/asio/asio/ip/udp.hpp +EXTRA_DIST += ext/asio/asio/ip/basic_resolver_entry.hpp +EXTRA_DIST += ext/asio/asio/ip/unicast.hpp +EXTRA_DIST += ext/asio/asio/ip/resolver_service.hpp EXTRA_DIST += ext/asio/asio/ip/icmp.hpp -EXTRA_DIST += ext/asio/asio/error.hpp -EXTRA_DIST += ext/asio/asio/basic_socket.hpp -EXTRA_DIST += ext/asio/asio/buffered_stream.hpp -EXTRA_DIST += ext/asio/asio/system_error.hpp -EXTRA_DIST += ext/asio/asio/basic_io_object.hpp -EXTRA_DIST += ext/asio/asio/read_at.hpp -EXTRA_DIST += ext/asio/asio/basic_raw_socket.hpp -EXTRA_DIST += ext/asio/asio/serial_port_service.hpp -EXTRA_DIST += ext/asio/asio/basic_stream_socket.hpp -EXTRA_DIST += ext/asio/asio/placeholders.hpp -EXTRA_DIST += ext/asio/asio/basic_deadline_timer.hpp -EXTRA_DIST += ext/asio/asio/thread.hpp -EXTRA_DIST += ext/asio/asio/buffered_write_stream_fwd.hpp -EXTRA_DIST += ext/asio/asio/datagram_socket_service.hpp +EXTRA_DIST += ext/asio/asio/ip/basic_endpoint.hpp +EXTRA_DIST += ext/asio/asio/ip/basic_resolver.hpp +EXTRA_DIST += ext/asio/asio/ip/impl/address.hpp +EXTRA_DIST += ext/asio/asio/ip/impl/address_v4.hpp +EXTRA_DIST += ext/asio/asio/ip/impl/address_v4.ipp +EXTRA_DIST += ext/asio/asio/ip/impl/address_v6.hpp +EXTRA_DIST += ext/asio/asio/ip/impl/address.ipp +EXTRA_DIST += ext/asio/asio/ip/impl/host_name.ipp +EXTRA_DIST += ext/asio/asio/ip/impl/basic_endpoint.hpp +EXTRA_DIST += ext/asio/asio/ip/impl/address_v6.ipp EXTRA_DIST += ext/asio/asio/handler_invoke_hook.hpp -EXTRA_DIST += ext/asio/asio/is_read_buffered.hpp -EXTRA_DIST += ext/asio/asio/buffer.hpp -EXTRA_DIST += ext/asio/asio/basic_socket_acceptor.hpp -EXTRA_DIST += ext/asio/asio/write_at.hpp -EXTRA_DIST += ext/asio/asio/completion_condition.hpp -EXTRA_DIST += ext/asio/asio/raw_socket_service.hpp -EXTRA_DIST += ext/asio/asio/socket_base.hpp -EXTRA_DIST += ext/asio/asio/serial_port.hpp -EXTRA_DIST += ext/asio/asio/error_code.hpp -EXTRA_DIST += ext/asio/asio/basic_serial_port.hpp -EXTRA_DIST += ext/asio/asio/version.hpp -EXTRA_DIST += ext/asio/asio/deadline_timer_service.hpp -EXTRA_DIST += ext/asio/asio/io_service.hpp -EXTRA_DIST += ext/asio/asio/buffered_read_stream.hpp -EXTRA_DIST += ext/asio/asio/streambuf.hpp -EXTRA_DIST += ext/asio/asio/basic_datagram_socket.hpp -EXTRA_DIST += ext/asio/asio/basic_streambuf.hpp -EXTRA_DIST += ext/asio/asio/write.hpp -EXTRA_DIST += ext/asio/asio/strand.hpp -EXTRA_DIST += ext/asio/asio/basic_socket_iostream.hpp -EXTRA_DIST += ext/asio/asio/buffered_stream_fwd.hpp -EXTRA_DIST += ext/asio/asio/basic_socket_streambuf.hpp -EXTRA_DIST += ext/asio/asio/ssl.hpp -EXTRA_DIST += ext/asio/asio/deadline_timer.hpp -EXTRA_DIST += ext/asio/asio/buffers_iterator.hpp -EXTRA_DIST += ext/asio/asio/handler_alloc_hook.hpp -EXTRA_DIST += ext/asio/asio/buffered_write_stream.hpp -EXTRA_DIST += ext/asio/asio/read.hpp -EXTRA_DIST += ext/asio/asio/serial_port_base.hpp -EXTRA_DIST += ext/asio/asio/stream_socket_service.hpp -EXTRA_DIST += ext/asio/asio/time_traits.hpp -EXTRA_DIST += ext/asio/asio/read_until.hpp -EXTRA_DIST += ext/asio/asio/is_write_buffered.hpp +EXTRA_DIST += ext/asio/asio/read_at.hpp EXTRA_DIST += ext/asio/asio/buffered_read_stream_fwd.hpp -EXTRA_DIST += ext/asio/asio/socket_acceptor_service.hpp -EXTRA_DIST += ext/asio/asio.hpp +EXTRA_DIST += ext/asio/asio/system_error.hpp +EXTRA_DIST += ext/asio/asio/deadline_timer.hpp +EXTRA_DIST += ext/asio/asio/stream_socket_service.hpp EXTRA_DIST += ext/coroutine/coroutine.h diff --git a/configure.ac b/configure.ac index 81df4765dc..c95396ae25 100644 --- a/configure.ac +++ b/configure.ac @@ -374,6 +374,79 @@ if test "$lcov" != "no"; then fi AC_SUBST(USE_LCOV) +# Check for Botan +botan_path="yes" +AC_ARG_WITH([botan], + AC_HELP_STRING([--with-botan=PATH], + [specify exact directory of Botan library]), + [botan_path="$withval"]) +if test "${botan_path}" == "no" ; then + AC_MSG_ERROR([Need botan for libcryptolink]) +fi +if test "${botan_path}" != "yes" ; then + if test -x "${botan_path}/bin/botan-config" ; then + BOTAN_CONFIG="${botan_path}/bin/botan-config" + else + AC_MSG_ERROR([${botan_path}/bin/botan-config not found]) + fi +else + AC_PATH_PROG([BOTAN_CONFIG], [botan-config]) +fi + +if test -x "${BOTAN_CONFIG}" ; then + BOTAN_LDFLAGS=`${BOTAN_CONFIG} --libs` + # We expect botan-config --libs to contain -L, but + # this is not always the case. As a heuristics workaround we add + # -L`botan-config --prefix/lib` in this case. Same for BOTAN_INCLUDES + # (but using include instead of lib) below. + echo ${BOTAN_LDFLAGS} | grep -- -L > /dev/null || \ + BOTAN_LDFLAGS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LDFLAGS}" + BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags` + echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \ + BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}" + # See python_rpath for some info on why we do this + if test $rpath_available = yes; then + BOTAN_RPATH= + for flag in ${BOTAN_LDFLAGS}; do + BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | sed -ne 's/^\(\-L\)/-R/p'`" + done + AC_SUBST(BOTAN_RPATH) + + # According to the libtool manual, it should be sufficient if we + # specify the "-R libdir" in our wrapper library of botan (no other + # programs will need libbotan directly); "libdir" should be added to + # the program's binary image. But we've seen in our build environments + # that (some versions of?) libtool doesn't propagate -R as documented, + # and it caused a linker error at run time. To work around this, we + # also add the rpath to the global LDFLAGS. + LDFLAGS="$BOTAN_RPATH $LDFLAGS" + fi + + AC_SUBST(BOTAN_LDFLAGS) + AC_SUBST(BOTAN_INCLUDES) +fi + +CPPFLAGS_SAVED=$CPPFLAGS +CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS" +LDFLAGS_SAVED="$LDFLAGS" +LDFLAGS="$BOTAN_LDFLAGS $LDFLAGS" + +AC_CHECK_HEADERS([botan/botan.h],,AC_MSG_ERROR([Missing required header files.])) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([#include + #include + ], + [using namespace Botan; + LibraryInitializer::initialize(); + HashFunction *h = get_hash("MD5"); + ])], + [AC_MSG_RESULT([checking for Botan library... yes])], + [AC_MSG_RESULT([checking for Botan library... no]) + AC_MSG_ERROR([Needs Botan library 1.8 or higher])] +) +CPPFLAGS=$CPPFLAGS_SAVED +LDFLAGS=$LDFLAGS_SAVED + # # Configure Boost header path # @@ -613,6 +686,7 @@ AC_CONFIG_FILES([Makefile src/bin/bindctl/tests/Makefile src/bin/cfgmgr/Makefile src/bin/cfgmgr/plugins/Makefile + src/bin/cfgmgr/plugins/tests/Makefile src/bin/cfgmgr/tests/Makefile src/bin/host/Makefile src/bin/loadzone/Makefile @@ -640,6 +714,7 @@ AC_CONFIG_FILES([Makefile src/bin/stats/tests/isc/config/Makefile src/bin/stats/tests/isc/util/Makefile src/bin/stats/tests/testdata/Makefile + src/bin/stats/tests/http/Makefile src/bin/usermgr/Makefile src/bin/tests/Makefile src/lib/Makefile @@ -672,11 +747,8 @@ AC_CONFIG_FILES([Makefile src/lib/config/Makefile src/lib/config/tests/Makefile src/lib/config/tests/testdata/Makefile - src/lib/util/Makefile - src/lib/util/io/Makefile - src/lib/util/io/tests/Makefile - src/lib/util/unittests/Makefile - src/lib/util/tests/Makefile + src/lib/cryptolink/Makefile + src/lib/cryptolink/tests/Makefile src/lib/dns/Makefile src/lib/dns/tests/Makefile src/lib/dns/tests/testdata/Makefile @@ -701,6 +773,11 @@ AC_CONFIG_FILES([Makefile src/lib/cache/tests/Makefile src/lib/server_common/Makefile src/lib/server_common/tests/Makefile + src/lib/util/Makefile + src/lib/util/io/Makefile + src/lib/util/io/tests/Makefile + src/lib/util/unittests/Makefile + src/lib/util/tests/Makefile tests/Makefile tests/system/Makefile tests/tools/Makefile @@ -729,11 +806,13 @@ AC_OUTPUT([doc/version.ent src/bin/zonemgr/tests/zonemgr_test src/bin/zonemgr/run_b10-zonemgr.sh src/bin/stats/stats.py - src/bin/stats/stats_stub.py - src/bin/stats/stats.spec.pre - src/bin/stats/run_b10-stats.sh - src/bin/stats/run_b10-stats_stub.sh - src/bin/stats/tests/stats_test + src/bin/stats/stats_httpd.py + src/bin/stats/stats.spec + src/bin/stats/stats-schema.spec + src/bin/stats/stats-httpd.spec + src/bin/stats/stats-httpd-xml.tpl + src/bin/stats/stats-httpd-xsd.tpl + src/bin/stats/stats-httpd-xsl.tpl src/bin/bind10/bind10.py src/bin/bind10/run_bind10.sh src/bin/bind10/tests/bind10_test.py @@ -763,6 +842,7 @@ AC_OUTPUT([doc/version.ent src/lib/cc/session_config.h.pre src/lib/cc/tests/session_unittests_config.h src/lib/log/tests/run_time_init_test.sh + src/lib/util/python/mkpywrapper.py tests/system/conf.sh tests/system/glue/setup.sh tests/system/glue/nsx1/b10-config.db @@ -772,9 +852,6 @@ AC_OUTPUT([doc/version.ent chmod +x src/bin/xfrin/run_b10-xfrin.sh chmod +x src/bin/xfrout/run_b10-xfrout.sh chmod +x src/bin/zonemgr/run_b10-zonemgr.sh - chmod +x src/bin/stats/tests/stats_test - chmod +x src/bin/stats/run_b10-stats.sh - chmod +x src/bin/stats/run_b10-stats_stub.sh chmod +x src/bin/bind10/run_bind10.sh chmod +x src/bin/cmdctl/tests/cmdctl_test chmod +x src/bin/xfrin/tests/xfrin_test @@ -791,6 +868,7 @@ AC_OUTPUT([doc/version.ent chmod +x src/lib/dns/gen-rdatacode.py chmod +x src/lib/dns/tests/testdata/gen-wiredata.py chmod +x src/lib/log/tests/run_time_init_test.sh + chmod +x src/lib/util/python/mkpywrapper.py chmod +x tests/system/conf.sh ]) AC_OUTPUT @@ -813,12 +891,15 @@ Flags: DEFS: $DEFS CPPFLAGS: $CPPFLAGS CXXFLAGS: $CXXFLAGS + LDFLAGS: $LDFLAGS B10_CXXFLAGS: $B10_CXXFLAGS dnl includes too Python: ${PYTHON_INCLUDES} ${PYTHON_LDFLAGS} ${PYTHON_LIB} Boost: ${BOOST_INCLUDES} + Botan: ${BOTAN_INCLUDES} + ${BOTAN_LDFLAGS} SQLite: $SQLITE_CFLAGS $SQLITE_LIBS diff --git a/doc/Doxyfile b/doc/Doxyfile index 83e85b823e..05b6ef38c5 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -568,11 +568,13 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns \ - ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth \ - ../src/bin/resolver ../src/lib/bench ../src/lib/log ../src/lib/asiolink/ \ - ../src/lib/nsas ../src/lib/testutils ../src/lib/cache \ - ../src/lib/server_common/ ../src/bin/sockcreator/ ../src/lib/util/ +INPUT = ../src/lib/cc ../src/lib/config \ + ../src/lib/cryptolink ../src/lib/dns ../src/lib/datasrc \ + ../src/bin/auth ../src/bin/resolver ../src/lib/bench \ + ../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas \ + ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \ + ../src/bin/sockcreator/ ../src/lib/util/ + ../src/lib/resolve # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -1163,7 +1165,7 @@ XML_DTD = # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. -XML_PROGRAMLISTING = YES +XML_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output diff --git a/ext/asio/README b/ext/asio/README index c581e8e90c..da12a9d860 100644 --- a/ext/asio/README +++ b/ext/asio/README @@ -1,5 +1,10 @@ ASIO library header files -Version 1.4.5 (2010-05-12) +Version 1.4.8 (2011-04-19) Downloaded from http://sourceforge.net/projects/asio/files Project page: http://think-async.com/Asio -No local modifications. + +Local modifications: +Added ASIO_DECL to a number of function definitions +git commit c32718be9f5409b6f72d98ddcd0b1ccd4c5c2293 +See also the bug report at: +http://sourceforge.net/tracker/?func=detail&aid=3291113&group_id=122478&atid=694037 diff --git a/ext/asio/asio.hpp b/ext/asio/asio.hpp index d8acd5859e..b0ebc96b00 100644 --- a/ext/asio/asio.hpp +++ b/ext/asio/asio.hpp @@ -2,7 +2,7 @@ // asio.hpp // ~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/ext/asio/asio/basic_datagram_socket.hpp b/ext/asio/asio/basic_datagram_socket.hpp index cb149f9835..2cbb3544a0 100644 --- a/ext/asio/asio/basic_datagram_socket.hpp +++ b/ext/asio/asio/basic_datagram_socket.hpp @@ -2,7 +2,7 @@ // basic_datagram_socket.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/basic_socket.hpp" #include "asio/datagram_socket_service.hpp" -#include "asio/error.hpp" #include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/basic_deadline_timer.hpp b/ext/asio/asio/basic_deadline_timer.hpp index 0d183f76ca..27781e7bd4 100644 --- a/ext/asio/asio/basic_deadline_timer.hpp +++ b/ext/asio/asio/basic_deadline_timer.hpp @@ -2,7 +2,7 @@ // basic_deadline_timer.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/basic_io_object.hpp" #include "asio/deadline_timer_service.hpp" -#include "asio/error.hpp" #include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/basic_io_object.hpp b/ext/asio/asio/basic_io_object.hpp index 092170df94..da35462024 100644 --- a/ext/asio/asio/basic_io_object.hpp +++ b/ext/asio/asio/basic_io_object.hpp @@ -2,7 +2,7 @@ // basic_io_object.hpp // ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/io_service.hpp" +#include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/io_service.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/basic_raw_socket.hpp b/ext/asio/asio/basic_raw_socket.hpp index 9d93b97f14..7a55c4a49c 100644 --- a/ext/asio/asio/basic_raw_socket.hpp +++ b/ext/asio/asio/basic_raw_socket.hpp @@ -2,7 +2,7 @@ // basic_raw_socket.hpp // ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/basic_socket.hpp" -#include "asio/raw_socket_service.hpp" -#include "asio/error.hpp" #include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/raw_socket_service.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/basic_serial_port.hpp b/ext/asio/asio/basic_serial_port.hpp index edb7bb07a0..38fe5e99fa 100644 --- a/ext/asio/asio/basic_serial_port.hpp +++ b/ext/asio/asio/basic_serial_port.hpp @@ -2,7 +2,7 @@ // basic_serial_port.hpp // ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,22 +16,20 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/basic_io_object.hpp" -#include "asio/error.hpp" -#include "asio/serial_port_base.hpp" -#include "asio/serial_port_service.hpp" -#include "asio/detail/throw_error.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) +#include +#include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/serial_port_base.hpp" +#include "asio/serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { /// Provides serial port functionality. @@ -614,9 +612,9 @@ public: } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_BASIC_SERIAL_PORT_HPP diff --git a/ext/asio/asio/basic_socket.hpp b/ext/asio/asio/basic_socket.hpp index 8b3f1d7f6b..5c66b4ad22 100644 --- a/ext/asio/asio/basic_socket.hpp +++ b/ext/asio/asio/basic_socket.hpp @@ -2,7 +2,7 @@ // basic_socket.hpp // ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/socket_base.hpp" -#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/basic_socket_acceptor.hpp b/ext/asio/asio/basic_socket_acceptor.hpp index 97fa56b776..15f9220dca 100644 --- a/ext/asio/asio/basic_socket_acceptor.hpp +++ b/ext/asio/asio/basic_socket_acceptor.hpp @@ -2,7 +2,7 @@ // basic_socket_acceptor.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/basic_io_object.hpp" #include "asio/basic_socket.hpp" +#include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/socket_acceptor_service.hpp" #include "asio/socket_base.hpp" -#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/basic_socket_iostream.hpp b/ext/asio/asio/basic_socket_iostream.hpp index 361ecb82cd..efcd51e94b 100644 --- a/ext/asio/asio/basic_socket_iostream.hpp +++ b/ext/asio/asio/basic_socket_iostream.hpp @@ -2,7 +2,7 @@ // basic_socket_iostream.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,22 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/push_options.hpp" #include #include #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/basic_socket_streambuf.hpp" #include "asio/stream_socket_service.hpp" @@ -79,6 +72,8 @@ } \ /**/ +#include "asio/detail/push_options.hpp" + namespace asio { /// Iostream interface for a socket. @@ -146,11 +141,11 @@ public: } // namespace asio +#include "asio/detail/pop_options.hpp" + #undef ASIO_PRIVATE_CTR_DEF #undef ASIO_PRIVATE_CONNECT_DEF #endif // defined(BOOST_NO_IOSTREAM) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_BASIC_SOCKET_IOSTREAM_HPP diff --git a/ext/asio/asio/basic_socket_streambuf.hpp b/ext/asio/asio/basic_socket_streambuf.hpp index ae0264e3ea..05fc86591d 100644 --- a/ext/asio/asio/basic_socket_streambuf.hpp +++ b/ext/asio/asio/basic_socket_streambuf.hpp @@ -2,7 +2,7 @@ // basic_socket_streambuf.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/push_options.hpp" #include #include #include @@ -31,12 +26,10 @@ #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/basic_socket.hpp" +#include "asio/detail/throw_error.hpp" #include "asio/io_service.hpp" #include "asio/stream_socket_service.hpp" -#include "asio/detail/throw_error.hpp" #if !defined(ASIO_SOCKET_STREAMBUF_MAX_ARITY) #define ASIO_SOCKET_STREAMBUF_MAX_ARITY 5 @@ -74,6 +67,8 @@ } \ /**/ +#include "asio/detail/push_options.hpp" + namespace asio { /// Iostream streambuf for a socket. @@ -286,10 +281,10 @@ private: } // namespace asio +#include "asio/detail/pop_options.hpp" + #undef ASIO_PRIVATE_CONNECT_DEF #endif // !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP diff --git a/ext/asio/asio/basic_stream_socket.hpp b/ext/asio/asio/basic_stream_socket.hpp index b7b4f065fa..41543fc189 100644 --- a/ext/asio/asio/basic_stream_socket.hpp +++ b/ext/asio/asio/basic_stream_socket.hpp @@ -2,7 +2,7 @@ // basic_stream_socket.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/basic_socket.hpp" +#include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/stream_socket_service.hpp" -#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/basic_streambuf.hpp b/ext/asio/asio/basic_streambuf.hpp index b34b3fecf7..17157da89f 100644 --- a/ext/asio/asio/basic_streambuf.hpp +++ b/ext/asio/asio/basic_streambuf.hpp @@ -2,7 +2,7 @@ // basic_streambuf.hpp // ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,28 +15,23 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/push_options.hpp" #include #include -#include #include #include #include #include #include -#include "asio/detail/pop_options.hpp" - +#include "asio/basic_streambuf_fwd.hpp" #include "asio/buffer.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /// Automatically resizable buffer class based on std::streambuf. @@ -107,7 +102,11 @@ namespace asio { * is >> s; * @endcode */ +#if defined(GENERATING_DOCUMENTATION) template > +#else +template +#endif class basic_streambuf : public std::streambuf, private noncopyable @@ -337,8 +336,27 @@ protected: private: std::size_t max_size_; std::vector buffer_; + + // Helper function to get the preferred size for reading data. + friend std::size_t read_size_helper( + basic_streambuf& sb, std::size_t max_size) + { + return std::min( + std::max(512, sb.buffer_.capacity() - sb.size()), + std::min(max_size, sb.max_size() - sb.size())); + } }; +// Helper function to get the preferred size for reading data. Used for any +// user-provided specialisations of basic_streambuf. +template +inline std::size_t read_size_helper( + basic_streambuf& sb, std::size_t max_size) +{ + return std::min(512, + std::min(max_size, sb.max_size() - sb.size())); +} + } // namespace asio #include "asio/detail/pop_options.hpp" diff --git a/ext/asio/asio/basic_streambuf_fwd.hpp b/ext/asio/asio/basic_streambuf_fwd.hpp new file mode 100644 index 0000000000..e1d81ac5e3 --- /dev/null +++ b/ext/asio/asio/basic_streambuf_fwd.hpp @@ -0,0 +1,33 @@ +// +// basic_streambuf_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_BASIC_STREAMBUF_FWD_HPP +#define ASIO_BASIC_STREAMBUF_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(BOOST_NO_IOSTREAM) + +#include + +namespace asio { + +template > +class basic_streambuf; + +} // namespace asio + +#endif // !defined(BOOST_NO_IOSTREAM) + +#endif // ASIO_BASIC_STREAMBUF_FWD_HPP diff --git a/ext/asio/asio/buffer.hpp b/ext/asio/asio/buffer.hpp index 43b475c5be..6810fca644 100644 --- a/ext/asio/asio/buffer.hpp +++ b/ext/asio/asio/buffer.hpp @@ -2,7 +2,7 @@ // buffer.hpp // ~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include -#include #include #include -#include "asio/detail/pop_options.hpp" +#include +#include "asio/detail/array_fwd.hpp" #if defined(BOOST_MSVC) # if defined(_HAS_ITERATOR_DEBUGGING) && (_HAS_ITERATOR_DEBUGGING != 0) @@ -43,11 +39,17 @@ #endif // defined(__GNUC__) #if defined(ASIO_ENABLE_BUFFER_DEBUGGING) -# include "asio/detail/push_options.hpp" # include -# include "asio/detail/pop_options.hpp" #endif // ASIO_ENABLE_BUFFER_DEBUGGING +#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) \ + || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) +# include +#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) + // || BOOST_WORKAROUND(__SUNPRO_CC, BOOST_TESTED_AT(0x590)) + +#include "asio/detail/push_options.hpp" + namespace asio { class mutable_buffer; diff --git a/ext/asio/asio/buffered_read_stream.hpp b/ext/asio/asio/buffered_read_stream.hpp index afd22cec86..d9dc31160f 100644 --- a/ext/asio/asio/buffered_read_stream.hpp +++ b/ext/asio/asio/buffered_read_stream.hpp @@ -2,7 +2,7 @@ // buffered_read_stream.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,23 +15,20 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include #include -#include "asio/detail/pop_options.hpp" - #include "asio/buffered_read_stream_fwd.hpp" #include "asio/buffer.hpp" -#include "asio/error.hpp" -#include "asio/io_service.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_resize_guard.hpp" #include "asio/detail/buffered_stream_storage.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/buffered_read_stream_fwd.hpp b/ext/asio/asio/buffered_read_stream_fwd.hpp index 5078775d11..0e8495a04a 100644 --- a/ext/asio/asio/buffered_read_stream_fwd.hpp +++ b/ext/asio/asio/buffered_read_stream_fwd.hpp @@ -2,7 +2,7 @@ // buffered_read_stream_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,8 +15,6 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - namespace asio { template @@ -24,6 +22,4 @@ class buffered_read_stream; } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_BUFFERED_READ_STREAM_FWD_HPP diff --git a/ext/asio/asio/buffered_stream.hpp b/ext/asio/asio/buffered_stream.hpp index 23dc9c33f9..11b99f6484 100644 --- a/ext/asio/asio/buffered_stream.hpp +++ b/ext/asio/asio/buffered_stream.hpp @@ -2,7 +2,7 @@ // buffered_stream.hpp // ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,19 +15,16 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/buffered_read_stream.hpp" #include "asio/buffered_write_stream.hpp" #include "asio/buffered_stream_fwd.hpp" +#include "asio/detail/noncopyable.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/buffered_stream_fwd.hpp b/ext/asio/asio/buffered_stream_fwd.hpp index f26cd43c2d..a8e958e0c2 100644 --- a/ext/asio/asio/buffered_stream_fwd.hpp +++ b/ext/asio/asio/buffered_stream_fwd.hpp @@ -2,7 +2,7 @@ // buffered_stream_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,8 +15,6 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - namespace asio { template @@ -24,6 +22,4 @@ class buffered_stream; } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_BUFFERED_STREAM_FWD_HPP diff --git a/ext/asio/asio/buffered_write_stream.hpp b/ext/asio/asio/buffered_write_stream.hpp index 30a92ce658..ee0ec7764a 100644 --- a/ext/asio/asio/buffered_write_stream.hpp +++ b/ext/asio/asio/buffered_write_stream.hpp @@ -2,7 +2,7 @@ // buffered_write_stream.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,24 +15,21 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include #include -#include "asio/detail/pop_options.hpp" - #include "asio/buffered_write_stream_fwd.hpp" #include "asio/buffer.hpp" #include "asio/completion_condition.hpp" -#include "asio/error.hpp" -#include "asio/io_service.hpp" -#include "asio/write.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffered_stream_storage.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/write.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/buffered_write_stream_fwd.hpp b/ext/asio/asio/buffered_write_stream_fwd.hpp index dc0e014732..cb09fe8be4 100644 --- a/ext/asio/asio/buffered_write_stream_fwd.hpp +++ b/ext/asio/asio/buffered_write_stream_fwd.hpp @@ -2,7 +2,7 @@ // buffered_write_stream_fwd.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,8 +15,6 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - namespace asio { template @@ -24,6 +22,4 @@ class buffered_write_stream; } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_BUFFERED_WRITE_STREAM_FWD_HPP diff --git a/ext/asio/asio/buffers_iterator.hpp b/ext/asio/asio/buffers_iterator.hpp index 7da55f2f34..dcca2d37d0 100644 --- a/ext/asio/asio/buffers_iterator.hpp +++ b/ext/asio/asio/buffers_iterator.hpp @@ -2,7 +2,7 @@ // buffers_iterator.hpp // ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,20 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include #include #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/buffer.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail @@ -210,6 +207,15 @@ public: return tmp; } + /// Addition operator. + friend buffers_iterator operator+(std::ptrdiff_t difference, + const buffers_iterator& iter) + { + buffers_iterator tmp(iter); + tmp.advance(difference); + return tmp; + } + /// Subtraction operator. friend buffers_iterator operator-(const buffers_iterator& iter, std::ptrdiff_t difference) diff --git a/ext/asio/asio/completion_condition.hpp b/ext/asio/asio/completion_condition.hpp index d4e631d035..dd1fb274fd 100644 --- a/ext/asio/asio/completion_condition.hpp +++ b/ext/asio/asio/completion_condition.hpp @@ -2,7 +2,7 @@ // completion_condition.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include #include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" namespace asio { diff --git a/ext/asio/asio/datagram_socket_service.hpp b/ext/asio/asio/datagram_socket_service.hpp index 7d135eed7c..87821c377c 100644 --- a/ext/asio/asio/datagram_socket_service.hpp +++ b/ext/asio/asio/datagram_socket_service.hpp @@ -2,7 +2,7 @@ // datagram_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" @@ -32,6 +26,8 @@ # include "asio/detail/reactive_socket_service.hpp" #endif +#include "asio/detail/push_options.hpp" + namespace asio { /// Default service implementation for a datagram socket. diff --git a/ext/asio/asio/deadline_timer.hpp b/ext/asio/asio/deadline_timer.hpp index e0905f289d..fd0d5dc7c2 100644 --- a/ext/asio/asio/deadline_timer.hpp +++ b/ext/asio/asio/deadline_timer.hpp @@ -2,7 +2,7 @@ // deadline_timer.hpp // ~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/socket_types.hpp" // Must come before posix_time. +#include "asio/basic_deadline_timer.hpp" #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" -#include "asio/basic_deadline_timer.hpp" - namespace asio { /// Typedef for the typical usage of timer. Uses a UTC clock. @@ -32,6 +30,4 @@ typedef basic_deadline_timer deadline_timer; } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DEADLINE_TIMER_HPP diff --git a/ext/asio/asio/deadline_timer_service.hpp b/ext/asio/asio/deadline_timer_service.hpp index 284a690ffb..60d898b2b6 100644 --- a/ext/asio/asio/deadline_timer_service.hpp +++ b/ext/asio/asio/deadline_timer_service.hpp @@ -2,7 +2,7 @@ // deadline_timer_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/deadline_timer_service.hpp" #include "asio/io_service.hpp" #include "asio/time_traits.hpp" -#include "asio/detail/deadline_timer_service.hpp" -#include "asio/detail/service_base.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/detail/array_fwd.hpp b/ext/asio/asio/detail/array_fwd.hpp new file mode 100644 index 0000000000..12c295cde8 --- /dev/null +++ b/ext/asio/asio/detail/array_fwd.hpp @@ -0,0 +1,25 @@ +// +// detail/array_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_ARRAY_FWD_HPP +#define ASIO_DETAIL_ARRAY_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace boost { + +template +class array; + +} // namespace boost + +#endif // ASIO_DETAIL_ARRAY_FWD_HPP diff --git a/ext/asio/asio/detail/base_from_completion_cond.hpp b/ext/asio/asio/detail/base_from_completion_cond.hpp index 90a11f9900..93cc8c45ff 100644 --- a/ext/asio/asio/detail/base_from_completion_cond.hpp +++ b/ext/asio/asio/detail/base_from_completion_cond.hpp @@ -1,8 +1,8 @@ // -// base_from_completion_cond.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/base_from_completion_cond.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/completion_condition.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -31,7 +32,8 @@ protected: { } - std::size_t check(const asio::error_code& ec, + std::size_t check_for_completion( + const asio::error_code& ec, std::size_t total_transferred) { return detail::adapt_completion_condition_result( @@ -50,7 +52,8 @@ protected: { } - static std::size_t check(const asio::error_code& ec, + static std::size_t check_for_completion( + const asio::error_code& ec, std::size_t total_transferred) { return transfer_all_t()(ec, total_transferred); diff --git a/ext/asio/asio/detail/bind_handler.hpp b/ext/asio/asio/detail/bind_handler.hpp index 5d9eb004ae..f663a5bab2 100644 --- a/ext/asio/asio/detail/bind_handler.hpp +++ b/ext/asio/asio/detail/bind_handler.hpp @@ -1,8 +1,8 @@ // -// bind_handler.hpp -// ~~~~~~~~~~~~~~~~ +// detail/bind_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -35,7 +36,7 @@ public: void operator()() { - handler_(arg1_); + handler_(static_cast(arg1_)); } void operator()() const @@ -92,7 +93,8 @@ public: void operator()() { - handler_(arg1_, arg2_); + handler_(static_cast(arg1_), + static_cast(arg2_)); } void operator()() const @@ -152,7 +154,9 @@ public: void operator()() { - handler_(arg1_, arg2_, arg3_); + handler_(static_cast(arg1_), + static_cast(arg2_), + static_cast(arg3_)); } void operator()() const @@ -216,7 +220,10 @@ public: void operator()() { - handler_(arg1_, arg2_, arg3_, arg4_); + handler_(static_cast(arg1_), + static_cast(arg2_), + static_cast(arg3_), + static_cast(arg4_)); } void operator()() const @@ -287,7 +294,11 @@ public: void operator()() { - handler_(arg1_, arg2_, arg3_, arg4_, arg5_); + handler_(static_cast(arg1_), + static_cast(arg2_), + static_cast(arg3_), + static_cast(arg4_), + static_cast(arg5_)); } void operator()() const diff --git a/ext/asio/asio/detail/buffer_resize_guard.hpp b/ext/asio/asio/detail/buffer_resize_guard.hpp index 368de323fe..4bf6a8ed89 100644 --- a/ext/asio/asio/detail/buffer_resize_guard.hpp +++ b/ext/asio/asio/detail/buffer_resize_guard.hpp @@ -1,8 +1,8 @@ // -// buffer_resize_guard.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~ +// detail/buffer_resize_guard.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include #include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/buffer_sequence_adapter.hpp b/ext/asio/asio/detail/buffer_sequence_adapter.hpp index 6269d6ae28..17044b7920 100644 --- a/ext/asio/asio/detail/buffer_sequence_adapter.hpp +++ b/ext/asio/asio/detail/buffer_sequence_adapter.hpp @@ -1,8 +1,8 @@ // -// buffer_sequence_adapter.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/buffer_sequence_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,9 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/buffer.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -32,14 +34,14 @@ protected: const asio::mutable_buffer& buffer) { buf.buf = asio::buffer_cast(buffer); - buf.len = asio::buffer_size(buffer); + buf.len = static_cast(asio::buffer_size(buffer)); } static void init_native_buffer(WSABUF& buf, const asio::const_buffer& buffer) { buf.buf = const_cast(asio::buffer_cast(buffer)); - buf.len = asio::buffer_size(buffer); + buf.len = static_cast(asio::buffer_size(buffer)); } #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef iovec native_buffer_type; @@ -158,7 +160,7 @@ public: explicit buffer_sequence_adapter( const asio::mutable_buffers_1& buffers) { - init_native_buffer(buffer_, buffers); + init_native_buffer(buffer_, Buffer(buffers)); total_buffer_size_ = asio::buffer_size(buffers); } @@ -205,7 +207,7 @@ public: explicit buffer_sequence_adapter( const asio::const_buffers_1& buffers) { - init_native_buffer(buffer_, buffers); + init_native_buffer(buffer_, Buffer(buffers)); total_buffer_size_ = asio::buffer_size(buffers); } diff --git a/ext/asio/asio/detail/buffered_stream_storage.hpp b/ext/asio/asio/detail/buffered_stream_storage.hpp index 51f6556fcb..7e1b7467a9 100644 --- a/ext/asio/asio/detail/buffered_stream_storage.hpp +++ b/ext/asio/asio/detail/buffered_stream_storage.hpp @@ -1,8 +1,8 @@ // -// buffered_stream_storage.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/buffered_stream_storage.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include #include #include #include -#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/call_stack.hpp b/ext/asio/asio/detail/call_stack.hpp index 0e9ddafaa3..7ad5dc5588 100644 --- a/ext/asio/asio/detail/call_stack.hpp +++ b/ext/asio/asio/detail/call_stack.hpp @@ -1,8 +1,8 @@ // -// call_stack.hpp -// ~~~~~~~~~~~~~~ +// detail/call_stack.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/tss_ptr.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/completion_handler.hpp b/ext/asio/asio/detail/completion_handler.hpp index 16167dfbe2..0224d7e83d 100644 --- a/ext/asio/asio/detail/completion_handler.hpp +++ b/ext/asio/asio/detail/completion_handler.hpp @@ -1,8 +1,8 @@ // -// completion_handler.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/completion_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/operation.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -29,6 +30,8 @@ template class completion_handler : public operation { public: + ASIO_DEFINE_HANDLER_PTR(completion_handler); + completion_handler(Handler h) : operation(&completion_handler::do_complete), handler_(h) @@ -40,20 +43,21 @@ public: { // Take ownership of the handler object. completion_handler* h(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(h->handler_, h); + ptr p = { boost::addressof(h->handler_), h, h }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + Handler handler(h->handler_); + p.h = boost::addressof(handler); + p.reset(); // Make the upcall if required. if (owner) { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - Handler handler(h->handler_); - ptr.reset(); asio::detail::fenced_block b; asio_handler_invoke_helpers::invoke(handler, handler); } diff --git a/ext/asio/asio/detail/config.hpp b/ext/asio/asio/detail/config.hpp new file mode 100644 index 0000000000..8328d7661c --- /dev/null +++ b/ext/asio/asio/detail/config.hpp @@ -0,0 +1,205 @@ +// +// detail/config.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_CONFIG_HPP +#define ASIO_DETAIL_CONFIG_HPP + +#include + +// Default to a header-only implementation. The user must specifically request +// separate compilation by defining either ASIO_SEPARATE_COMPILATION or +// ASIO_DYN_LINK (as a DLL/shared library implies separate compilation). +#if !defined(ASIO_HEADER_ONLY) +# if !defined(ASIO_SEPARATE_COMPILATION) +# if !defined(ASIO_DYN_LINK) +# define ASIO_HEADER_ONLY +# endif // !defined(ASIO_DYN_LINK) +# endif // !defined(ASIO_SEPARATE_COMPILATION) +#endif // !defined(ASIO_HEADER_ONLY) + +#if defined(ASIO_HEADER_ONLY) +# define ASIO_DECL inline +#else // defined(ASIO_HEADER_ONLY) +# if defined(BOOST_HAS_DECLSPEC) +// We need to import/export our code only if the user has specifically asked +// for it by defining ASIO_DYN_LINK. +# if defined(ASIO_DYN_LINK) +// Export if this is our own source, otherwise import. +# if defined(ASIO_SOURCE) +# define ASIO_DECL __declspec(dllexport) +# else // defined(ASIO_SOURCE) +# define ASIO_DECL __declspec(dllimport) +# endif // defined(ASIO_SOURCE) +# endif // defined(ASIO_DYN_LINK) +# endif // defined(BOOST_HAS_DECLSPEC) +#endif // defined(ASIO_HEADER_ONLY) + +// If ASIO_DECL isn't defined yet define it now. +#if !defined(ASIO_DECL) +# define ASIO_DECL +#endif // !defined(ASIO_DECL) + +// Windows: target OS version. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) || defined(__BORLANDC__) +# pragma message( \ + "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\ + "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\ + "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\ + "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).") +# else // defined(_MSC_VER) || defined(__BORLANDC__) +# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. +# warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line. +# warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target). +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +# define _WIN32_WINNT 0x0501 +# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) +# if defined(_MSC_VER) +# if defined(_WIN32) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(_WIN32) && !defined(WIN32) +# endif // defined(_MSC_VER) +# if defined(__BORLANDC__) +# if defined(__WIN32__) && !defined(WIN32) +# if !defined(_WINSOCK2API_) +# define WIN32 // Needed for correct types in winsock2.h +# else // !defined(_WINSOCK2API_) +# error Please define the macro WIN32 in your compiler options +# endif // !defined(_WINSOCK2API_) +# endif // defined(__WIN32__) && !defined(WIN32) +# endif // defined(__BORLANDC__) +# if defined(__CYGWIN__) +# if !defined(__USE_W32_SOCKETS) +# error You must add -D__USE_W32_SOCKETS to your compiler options. +# endif // !defined(__USE_W32_SOCKETS) +# endif // defined(__CYGWIN__) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +// Windows: minimise header inclusion. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +# if !defined(WIN32_LEAN_AND_MEAN) +# define WIN32_LEAN_AND_MEAN +# endif // !defined(WIN32_LEAN_AND_MEAN) +# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +// Windows: suppress definition of "min" and "max" macros. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if !defined(ASIO_NO_NOMINMAX) +# if !defined(NOMINMAX) +# define NOMINMAX 1 +# endif // !defined(NOMINMAX) +# endif // !defined(ASIO_NO_NOMINMAX) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +// Windows: IO Completion Ports. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +# if !defined(UNDER_CE) +# if !defined(ASIO_DISABLE_IOCP) +# define ASIO_HAS_IOCP 1 +# endif // !defined(ASIO_DISABLE_IOCP) +# endif // !defined(UNDER_CE) +# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +// Linux: epoll, eventfd and timerfd. +#if defined(__linux__) +# include +# if !defined(ASIO_DISABLE_EPOLL) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +# define ASIO_HAS_EPOLL 1 +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45) +# endif // !defined(ASIO_DISABLE_EVENTFD) +# if !defined(ASIO_DISABLE_EVENTFD) +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# define ASIO_HAS_EVENTFD 1 +# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +# endif // !defined(ASIO_DISABLE_EVENTFD) +# if defined(ASIO_HAS_EPOLL) +# if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# define ASIO_HAS_TIMERFD 1 +# endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) +# endif // defined(ASIO_HAS_EPOLL) +#endif // defined(__linux__) + +// Mac OS X, FreeBSD, NetBSD, OpenBSD: kqueue. +#if (defined(__MACH__) && defined(__APPLE__)) \ + || defined(__FreeBSD__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) +# if !defined(ASIO_DISABLE_KQUEUE) +# define ASIO_HAS_KQUEUE 1 +# endif // !defined(ASIO_DISABLE_KQUEUE) +#endif // (defined(__MACH__) && defined(__APPLE__)) + // || defined(__FreeBSD__) + // || defined(__NetBSD__) + // || defined(__OpenBSD__) + +// Solaris: /dev/poll. +#if defined(__sun) +# if !defined(ASIO_DISABLE_DEV_POLL) +# define ASIO_HAS_DEV_POLL 1 +# endif // !defined(ASIO_DISABLE_DEV_POLL) +#endif // defined(__sun) + +// Serial ports. +#if defined(ASIO_HAS_IOCP) \ + || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# if !defined(__SYMBIAN32__) +# if !defined(ASIO_DISABLE_SERIAL_PORT) +# define ASIO_HAS_SERIAL_PORT 1 +# endif // !defined(ASIO_DISABLE_SERIAL_PORT) +# endif // !defined(__SYMBIAN32__) +#endif // defined(ASIO_HAS_IOCP) + // || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +// Windows: stream handles. +#if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_STREAM_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +#endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) + +// Windows: random access handles. +#if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1 +# endif // defined(ASIO_HAS_IOCP) +#endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) + +// Windows: OVERLAPPED wrapper. +#if !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) +# if defined(ASIO_HAS_IOCP) +# define ASIO_HAS_WINDOWS_OVERLAPPED_PTR 1 +# endif // defined(ASIO_HAS_IOCP) +#endif // !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) + +// POSIX: stream-oriented file descriptors. +#if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) +# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1 +# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) + +// UNIX domain sockets. +#if !defined(ASIO_DISABLE_LOCAL_SOCKETS) +# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# define ASIO_HAS_LOCAL_SOCKETS 1 +# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS) + +#endif // ASIO_DETAIL_CONFIG_HPP diff --git a/ext/asio/asio/detail/consuming_buffers.hpp b/ext/asio/asio/detail/consuming_buffers.hpp index 80b6a8e803..8969ae4d98 100644 --- a/ext/asio/asio/detail/consuming_buffers.hpp +++ b/ext/asio/asio/detail/consuming_buffers.hpp @@ -1,8 +1,8 @@ // -// consuming_buffers.hpp -// ~~~~~~~~~~~~~~~~~~~~~ +// detail/consuming_buffers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,18 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include -#include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/buffer.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -156,12 +152,14 @@ public: consuming_buffers(const Buffers& buffers) : buffers_(buffers), at_end_(buffers_.begin() == buffers_.end()), - first_(*buffers_.begin()), begin_remainder_(buffers_.begin()), max_size_((std::numeric_limits::max)()) { if (!at_end_) + { + first_ = *buffers_.begin(); ++begin_remainder_; + } } // Copy constructor. diff --git a/ext/asio/asio/detail/deadline_timer_service.hpp b/ext/asio/asio/detail/deadline_timer_service.hpp index 6daf7acb35..782f5c3b4c 100644 --- a/ext/asio/asio/detail/deadline_timer_service.hpp +++ b/ext/asio/asio/detail/deadline_timer_service.hpp @@ -1,8 +1,8 @@ // -// deadline_timer_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/deadline_timer_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,8 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/bind_handler.hpp" @@ -33,6 +27,13 @@ #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue.hpp" #include "asio/detail/timer_scheduler.hpp" +#include "asio/detail/wait_handler.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -54,6 +55,7 @@ public: { time_type expiry; bool might_have_pending_waits; + typename timer_queue::per_timer_data timer_data; }; // Constructor. @@ -97,7 +99,7 @@ public: ec = asio::error_code(); return 0; } - std::size_t count = scheduler_.cancel_timer(timer_queue_, &impl); + std::size_t count = scheduler_.cancel_timer(timer_queue_, impl.timer_data); impl.might_have_pending_waits = false; ec = asio::error_code(); return count; @@ -151,59 +153,21 @@ public: ec = asio::error_code(); } - template - class wait_handler : public timer_op - { - public: - wait_handler(Handler handler) - : timer_op(&wait_handler::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - wait_handler* h(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(h->handler_, h); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder1 - handler(h->handler_, h->ec_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous wait on the timer. template void async_wait(implementation_type& impl, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef wait_handler value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef wait_handler op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); impl.might_have_pending_waits = true; - scheduler_.schedule_timer(timer_queue_, impl.expiry, ptr.get(), &impl); - ptr.release(); + scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p); + p.v = p.p = 0; } private: diff --git a/ext/asio/asio/detail/descriptor_ops.hpp b/ext/asio/asio/detail/descriptor_ops.hpp index ea3731e7ac..5cac91dc4b 100644 --- a/ext/asio/asio/detail/descriptor_ops.hpp +++ b/ext/asio/asio/detail/descriptor_ops.hpp @@ -1,8 +1,8 @@ // -// descriptor_ops.hpp -// ~~~~~~~~~~~~~~~~~~ +// detail/descriptor_ops.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,27 +15,34 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/detail/socket_types.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#include +#include "asio/error_code.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { namespace descriptor_ops { -inline void clear_error(asio::error_code& ec) +// Descriptor state bits. +enum { - errno = 0; - ec = asio::error_code(); -} + // The user wants a non-blocking descriptor. + user_set_non_blocking = 1, + + // The descriptor has been set non-blocking. + internal_non_blocking = 2, + + // Helper "state" used to determine whether the descriptor is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking +}; + +typedef unsigned char state_type; template inline ReturnType error_wrapper(ReturnType return_value, @@ -46,131 +53,53 @@ inline ReturnType error_wrapper(ReturnType return_value, return return_value; } -inline int open(const char* path, int flags, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::open(path, flags), ec); - if (result >= 0) - clear_error(ec); - return result; -} +ASIO_DECL int open(const char* path, int flags, + asio::error_code& ec); -inline int close(int d, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::close(d), ec); - if (result == 0) - clear_error(ec); - return result; -} +ASIO_DECL int close(int d, state_type& state, + asio::error_code& ec); -inline void init_buf_iov_base(void*& base, void* addr) -{ - base = addr; -} - -template -inline void init_buf_iov_base(T& base, void* addr) -{ - base = static_cast(addr); -} +ASIO_DECL bool set_internal_non_blocking(int d, + state_type& state, asio::error_code& ec); typedef iovec buf; -inline void init_buf(buf& b, void* data, size_t size) -{ - init_buf_iov_base(b.iov_base, data); - b.iov_len = size; -} +ASIO_DECL std::size_t sync_read(int d, state_type state, buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec); -inline void init_buf(buf& b, const void* data, size_t size) -{ - init_buf_iov_base(b.iov_base, const_cast(data)); - b.iov_len = size; -} +ASIO_DECL bool non_blocking_read(int d, buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred); -inline int scatter_read(int d, buf* bufs, size_t count, - asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::readv(d, bufs, static_cast(count)), ec); - if (result >= 0) - clear_error(ec); - return result; -} +ASIO_DECL std::size_t sync_write(int d, state_type state, + const buf* bufs, std::size_t count, bool all_empty, + asio::error_code& ec); -inline int gather_write(int d, const buf* bufs, size_t count, - asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::writev(d, bufs, static_cast(count)), ec); - if (result >= 0) - clear_error(ec); - return result; -} +ASIO_DECL bool non_blocking_write(int d, + const buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred); -inline int ioctl(int d, long cmd, ioctl_arg_type* arg, - asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::ioctl(d, cmd, arg), ec); - if (result >= 0) - clear_error(ec); - return result; -} +ASIO_DECL int ioctl(int d, state_type& state, long cmd, + ioctl_arg_type* arg, asio::error_code& ec); -inline int fcntl(int d, long cmd, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::fcntl(d, cmd), ec); - if (result != -1) - clear_error(ec); - return result; -} +ASIO_DECL int fcntl(int d, long cmd, asio::error_code& ec); -inline int fcntl(int d, long cmd, long arg, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::fcntl(d, cmd, arg), ec); - if (result != -1) - clear_error(ec); - return result; -} +ASIO_DECL int fcntl(int d, long cmd, + long arg, asio::error_code& ec); -inline int poll_read(int d, asio::error_code& ec) -{ - clear_error(ec); - pollfd fds; - fds.fd = d; - fds.events = POLLIN; - fds.revents = 0; - clear_error(ec); - int result = error_wrapper(::poll(&fds, 1, -1), ec); - if (result >= 0) - clear_error(ec); - return result; -} +ASIO_DECL int poll_read(int d, asio::error_code& ec); -inline int poll_write(int d, asio::error_code& ec) -{ - clear_error(ec); - pollfd fds; - fds.fd = d; - fds.events = POLLOUT; - fds.revents = 0; - clear_error(ec); - int result = error_wrapper(::poll(&fds, 1, -1), ec); - if (result >= 0) - clear_error(ec); - return result; -} +ASIO_DECL int poll_write(int d, asio::error_code& ec); } // namespace descriptor_ops } // namespace detail } // namespace asio -#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/descriptor_ops.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + #endif // ASIO_DETAIL_DESCRIPTOR_OPS_HPP diff --git a/ext/asio/asio/detail/descriptor_read_op.hpp b/ext/asio/asio/detail/descriptor_read_op.hpp new file mode 100644 index 0000000000..0d4516721c --- /dev/null +++ b/ext/asio/asio/detail/descriptor_read_op.hpp @@ -0,0 +1,114 @@ +// +// detail/descriptor_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP +#define ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class descriptor_read_op_base : public reactor_op +{ +public: + descriptor_read_op_base(int descriptor, + const MutableBufferSequence& buffers, func_type complete_func) + : reactor_op(&descriptor_read_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static bool do_perform(reactor_op* base) + { + descriptor_read_op_base* o(static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + return descriptor_ops::non_blocking_read(o->descriptor_, + bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_); + } + +private: + int descriptor_; + MutableBufferSequence buffers_; +}; + +template +class descriptor_read_op + : public descriptor_read_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(descriptor_read_op); + + descriptor_read_op(int descriptor, + const MutableBufferSequence& buffers, Handler handler) + : descriptor_read_op_base( + descriptor, buffers, &descriptor_read_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + descriptor_read_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_READ_OP_HPP diff --git a/ext/asio/asio/detail/descriptor_write_op.hpp b/ext/asio/asio/detail/descriptor_write_op.hpp new file mode 100644 index 0000000000..9caa283e70 --- /dev/null +++ b/ext/asio/asio/detail/descriptor_write_op.hpp @@ -0,0 +1,114 @@ +// +// detail/descriptor_write_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP +#define ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class descriptor_write_op_base : public reactor_op +{ +public: + descriptor_write_op_base(int descriptor, + const ConstBufferSequence& buffers, func_type complete_func) + : reactor_op(&descriptor_write_op_base::do_perform, complete_func), + descriptor_(descriptor), + buffers_(buffers) + { + } + + static bool do_perform(reactor_op* base) + { + descriptor_write_op_base* o(static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + return descriptor_ops::non_blocking_write(o->descriptor_, + bufs.buffers(), bufs.count(), o->ec_, o->bytes_transferred_); + } + +private: + int descriptor_; + ConstBufferSequence buffers_; +}; + +template +class descriptor_write_op + : public descriptor_write_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(descriptor_write_op); + + descriptor_write_op(int descriptor, + const ConstBufferSequence& buffers, Handler handler) + : descriptor_write_op_base( + descriptor, buffers, &descriptor_write_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + descriptor_write_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_DESCRIPTOR_WRITE_OP_HPP diff --git a/ext/asio/asio/detail/dev_poll_reactor.hpp b/ext/asio/asio/detail/dev_poll_reactor.hpp index 1367b7190f..6bbf1b203d 100644 --- a/ext/asio/asio/detail/dev_poll_reactor.hpp +++ b/ext/asio/asio/detail/dev_poll_reactor.hpp @@ -1,8 +1,8 @@ // -// dev_poll_reactor.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// detail/dev_poll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,36 +15,28 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/dev_poll_reactor_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_DEV_POLL) -#include "asio/detail/push_options.hpp" #include #include -#include -#include -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/io_service.hpp" -#include "asio/system_error.hpp" +#include "asio/detail/dev_poll_reactor_fwd.hpp" #include "asio/detail/hash_map.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/reactor_op_queue.hpp" #include "asio/detail/select_interrupter.hpp" -#include "asio/detail/service_base.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" +#include "asio/io_service.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -62,358 +54,93 @@ public: }; // Constructor. - dev_poll_reactor(asio::io_service& io_service) - : asio::detail::service_base(io_service), - io_service_(use_service(io_service)), - mutex_(), - dev_poll_fd_(do_dev_poll_create()), - interrupter_(), - shutdown_(false) - { - // Add the interrupter's descriptor to /dev/poll. - ::pollfd ev = { 0 }; - ev.fd = interrupter_.read_descriptor(); - ev.events = POLLIN | POLLERR; - ev.revents = 0; - ::write(dev_poll_fd_, &ev, sizeof(ev)); - } + ASIO_DECL dev_poll_reactor(asio::io_service& io_service); // Destructor. - ~dev_poll_reactor() - { - shutdown_service(); - ::close(dev_poll_fd_); - } + ASIO_DECL ~dev_poll_reactor(); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - asio::detail::mutex::scoped_lock lock(mutex_); - shutdown_ = true; - lock.unlock(); - - op_queue ops; - - for (int i = 0; i < max_ops; ++i) - op_queue_[i].get_all_operations(ops); - - timer_queues_.get_all_timers(ops); - } + ASIO_DECL void shutdown_service(); // Initialise the task. - void init_task() - { - io_service_.init_task(); - } + ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type, per_descriptor_data&) + ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op) { - return 0; + io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. - void start_op(int op_type, socket_type descriptor, - per_descriptor_data&, reactor_op* op, bool allow_speculative) - { - asio::detail::mutex::scoped_lock lock(mutex_); - - if (shutdown_) - return; - - if (allow_speculative) - { - if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor)) - { - if (!op_queue_[op_type].has_operation(descriptor)) - { - if (op->perform()) - { - lock.unlock(); - io_service_.post_immediate_completion(op); - return; - } - } - } - } - - bool first = op_queue_[op_type].enqueue_operation(descriptor, op); - io_service_.work_started(); - if (first) - { - ::pollfd& ev = add_pending_event_change(descriptor); - ev.events = POLLERR | POLLHUP; - if (op_type == read_op - || op_queue_[read_op].has_operation(descriptor)) - ev.events |= POLLIN; - if (op_type == write_op - || op_queue_[write_op].has_operation(descriptor)) - ev.events |= POLLOUT; - if (op_type == except_op - || op_queue_[except_op].has_operation(descriptor)) - ev.events |= POLLPRI; - interrupter_.interrupt(); - } - } + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, bool allow_speculative); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. - void cancel_ops(socket_type descriptor, per_descriptor_data&) - { - asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor, asio::error::operation_aborted); - } + ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor, per_descriptor_data&) - { - asio::detail::mutex::scoped_lock lock(mutex_); - - // Remove the descriptor from /dev/poll. - ::pollfd& ev = add_pending_event_change(descriptor); - ev.events = POLLREMOVE; - interrupter_.interrupt(); - - // Cancel any outstanding operations associated with the descriptor. - cancel_ops_unlocked(descriptor, asio::error::operation_aborted); - } + ASIO_DECL void close_descriptor( + socket_type descriptor, per_descriptor_data&); // Add a new timer queue to the reactor. template - void add_timer_queue(timer_queue& timer_queue) - { - asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.insert(&timer_queue); - } + void add_timer_queue(timer_queue& queue); // Remove a timer queue from the reactor. template - void remove_timer_queue(timer_queue& timer_queue) - { - asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.erase(&timer_queue); - } + void remove_timer_queue(timer_queue& queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template - void schedule_timer(timer_queue& timer_queue, - const typename Time_Traits::time_type& time, timer_op* op, void* token) - { - asio::detail::mutex::scoped_lock lock(mutex_); - if (!shutdown_) - { - bool earliest = timer_queue.enqueue_timer(time, op, token); - io_service_.work_started(); - if (earliest) - interrupter_.interrupt(); - } - } + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template - std::size_t cancel_timer(timer_queue& timer_queue, void* token) - { - asio::detail::mutex::scoped_lock lock(mutex_); - op_queue ops; - std::size_t n = timer_queue.cancel_timer(token, ops); - lock.unlock(); - io_service_.post_deferred_completions(ops); - return n; - } + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer); // Run /dev/poll once until interrupted or events are ready to be dispatched. - void run(bool block, op_queue& ops) - { - asio::detail::mutex::scoped_lock lock(mutex_); - - // We can return immediately if there's no work to do and the reactor is - // not supposed to block. - if (!block && op_queue_[read_op].empty() && op_queue_[write_op].empty() - && op_queue_[except_op].empty() && timer_queues_.all_empty()) - return; - - // Write the pending event registration changes to the /dev/poll descriptor. - std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size(); - if (events_size > 0) - { - errno = 0; - int result = ::write(dev_poll_fd_, - &pending_event_changes_[0], events_size); - if (result != static_cast(events_size)) - { - asio::error_code ec = asio::error_code( - errno, asio::error::get_system_category()); - for (std::size_t i = 0; i < pending_event_changes_.size(); ++i) - { - int descriptor = pending_event_changes_[i].fd; - for (int j = 0; j < max_ops; ++j) - op_queue_[j].cancel_operations(descriptor, ops, ec); - } - } - pending_event_changes_.clear(); - pending_event_change_index_.clear(); - } - - int timeout = block ? get_timeout() : 0; - lock.unlock(); - - // Block on the /dev/poll descriptor. - ::pollfd events[128] = { { 0 } }; - ::dvpoll dp = { 0 }; - dp.dp_fds = events; - dp.dp_nfds = 128; - dp.dp_timeout = timeout; - int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp); - - lock.lock(); - - // Dispatch the waiting events. - for (int i = 0; i < num_events; ++i) - { - int descriptor = events[i].fd; - if (descriptor == interrupter_.read_descriptor()) - { - interrupter_.reset(); - } - else - { - bool more_reads = false; - bool more_writes = false; - bool more_except = false; - - // Exception operations must be processed first to ensure that any - // out-of-band data is read before normal data. - if (events[i].events & (POLLPRI | POLLERR | POLLHUP)) - more_except = - op_queue_[except_op].perform_operations(descriptor, ops); - else - more_except = op_queue_[except_op].has_operation(descriptor); - - if (events[i].events & (POLLIN | POLLERR | POLLHUP)) - more_reads = op_queue_[read_op].perform_operations(descriptor, ops); - else - more_reads = op_queue_[read_op].has_operation(descriptor); - - if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) - more_writes = op_queue_[write_op].perform_operations(descriptor, ops); - else - more_writes = op_queue_[write_op].has_operation(descriptor); - - if ((events[i].events & (POLLERR | POLLHUP)) != 0 - && !more_except && !more_reads && !more_writes) - { - // If we have an event and no operations associated with the - // descriptor then we need to delete the descriptor from /dev/poll. - // The poll operation can produce POLLHUP or POLLERR events when there - // is no operation pending, so if we do not remove the descriptor we - // can end up in a tight polling loop. - ::pollfd ev = { 0 }; - ev.fd = descriptor; - ev.events = POLLREMOVE; - ev.revents = 0; - ::write(dev_poll_fd_, &ev, sizeof(ev)); - } - else - { - ::pollfd ev = { 0 }; - ev.fd = descriptor; - ev.events = POLLERR | POLLHUP; - if (more_reads) - ev.events |= POLLIN; - if (more_writes) - ev.events |= POLLOUT; - if (more_except) - ev.events |= POLLPRI; - ev.revents = 0; - int result = ::write(dev_poll_fd_, &ev, sizeof(ev)); - if (result != sizeof(ev)) - { - asio::error_code ec(errno, - asio::error::get_system_category()); - for (int j = 0; j < max_ops; ++j) - op_queue_[j].cancel_operations(descriptor, ops, ec); - } - } - } - } - timer_queues_.get_ready_timers(ops); - } + ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the select loop. - void interrupt() - { - interrupter_.interrupt(); - } + ASIO_DECL void interrupt(); private: // Create the /dev/poll file descriptor. Throws an exception if the descriptor // cannot be created. - static int do_dev_poll_create() - { - int fd = ::open("/dev/poll", O_RDWR); - if (fd == -1) - { - boost::throw_exception( - asio::system_error( - asio::error_code(errno, - asio::error::get_system_category()), - "/dev/poll")); - } - return fd; - } + ASIO_DECL static int do_dev_poll_create(); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Get the timeout value for the /dev/poll DP_POLL operation. The timeout // value is returned as a number of milliseconds. A return value of -1 // indicates that the poll should block indefinitely. - int get_timeout() - { - // By default we will wait no longer than 5 minutes. This will ensure that - // any changes to the system clock are detected after no longer than this. - return timer_queues_.wait_duration_msec(5 * 60 * 1000); - } + ASIO_DECL int get_timeout(); // Cancel all operations associated with the given descriptor. The do_cancel // function of the handler objects will be invoked. This function does not // acquire the dev_poll_reactor's mutex. - void cancel_ops_unlocked(socket_type descriptor, - const asio::error_code& ec) - { - bool need_interrupt = false; - op_queue ops; - for (int i = 0; i < max_ops; ++i) - need_interrupt = op_queue_[i].cancel_operations( - descriptor, ops, ec) || need_interrupt; - io_service_.post_deferred_completions(ops); - if (need_interrupt) - interrupter_.interrupt(); - } + ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec); // Add a pending event entry for the given descriptor. - ::pollfd& add_pending_event_change(int descriptor) - { - hash_map::iterator iter - = pending_event_change_index_.find(descriptor); - if (iter == pending_event_change_index_.end()) - { - std::size_t index = pending_event_changes_.size(); - pending_event_changes_.reserve(pending_event_changes_.size() + 1); - pending_event_change_index_.insert(std::make_pair(descriptor, index)); - pending_event_changes_.push_back(::pollfd()); - pending_event_changes_[index].fd = descriptor; - pending_event_changes_[index].revents = 0; - return pending_event_changes_[index]; - } - else - { - return pending_event_changes_[iter->second]; - } - } + ASIO_DECL ::pollfd& add_pending_event_change(int descriptor); // The io_service implementation used to post completions. io_service_impl& io_service_; @@ -446,8 +173,13 @@ private: } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_DEV_POLL) - #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/dev_poll_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/dev_poll_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_DEV_POLL) + #endif // ASIO_DETAIL_DEV_POLL_REACTOR_HPP diff --git a/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp b/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp index f7f1aebab4..c54f5e17db 100644 --- a/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp +++ b/ext/asio/asio/detail/dev_poll_reactor_fwd.hpp @@ -1,8 +1,8 @@ // -// dev_poll_reactor_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/dev_poll_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,9 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#if !defined(ASIO_DISABLE_DEV_POLL) -#if defined(__sun) // This service is only supported on Solaris. - -// Define this to indicate that /dev/poll is supported on the target platform. -#define ASIO_HAS_DEV_POLL 1 +#if defined(ASIO_HAS_DEV_POLL) namespace asio { namespace detail { @@ -31,9 +27,6 @@ class dev_poll_reactor; } // namespace detail } // namespace asio -#endif // defined(__sun) -#endif // !defined(ASIO_DISABLE_DEV_POLL) - -#include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_DEV_POLL) #endif // ASIO_DETAIL_DEV_POLL_REACTOR_FWD_HPP diff --git a/ext/asio/asio/detail/epoll_reactor.hpp b/ext/asio/asio/detail/epoll_reactor.hpp index 0a1eac0265..a085766b5f 100644 --- a/ext/asio/asio/detail/epoll_reactor.hpp +++ b/ext/asio/asio/detail/epoll_reactor.hpp @@ -1,8 +1,8 @@ // -// epoll_reactor.hpp -// ~~~~~~~~~~~~~~~~~ +// detail/epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,43 +15,24 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/epoll_reactor_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_EPOLL) -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/hash_map.hpp" +#include "asio/detail/epoll_reactor_fwd.hpp" #include "asio/detail/mutex.hpp" +#include "asio/detail/object_pool.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/select_interrupter.hpp" -#include "asio/detail/service_base.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" -#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) -# define ASIO_HAS_TIMERFD 1 -#endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) - -#if defined(ASIO_HAS_TIMERFD) -# include "asio/detail/push_options.hpp" -# include -# include "asio/detail/pop_options.hpp" -#endif // defined(ASIO_HAS_TIMERFD) +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -64,357 +45,86 @@ public: connect_op = 1, except_op = 2, max_ops = 3 }; // Per-descriptor queues. - struct descriptor_state + class descriptor_state { - descriptor_state() {} - descriptor_state(const descriptor_state&) {} - void operator=(const descriptor_state&) {} - + friend class epoll_reactor; + friend class object_pool_access; mutex mutex_; op_queue op_queue_[max_ops]; bool shutdown_; + descriptor_state* next_; + descriptor_state* prev_; }; // Per-descriptor data. typedef descriptor_state* per_descriptor_data; // Constructor. - epoll_reactor(asio::io_service& io_service) - : asio::detail::service_base(io_service), - io_service_(use_service(io_service)), - mutex_(), - epoll_fd_(do_epoll_create()), -#if defined(ASIO_HAS_TIMERFD) - timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)), -#else // defined(ASIO_HAS_TIMERFD) - timer_fd_(-1), -#endif // defined(ASIO_HAS_TIMERFD) - interrupter_(), - shutdown_(false) - { - // Add the interrupter's descriptor to epoll. - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR | EPOLLET; - ev.data.ptr = &interrupter_; - epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); - interrupter_.interrupt(); - - // Add the timer descriptor to epoll. - if (timer_fd_ != -1) - { - ev.events = EPOLLIN | EPOLLERR; - ev.data.ptr = &timer_fd_; - epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); - } - } + ASIO_DECL epoll_reactor(asio::io_service& io_service); // Destructor. - ~epoll_reactor() - { - close(epoll_fd_); - if (timer_fd_ != -1) - close(timer_fd_); - } + ASIO_DECL ~epoll_reactor(); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - mutex::scoped_lock lock(mutex_); - shutdown_ = true; - lock.unlock(); - - op_queue ops; - - descriptor_map::iterator iter = registered_descriptors_.begin(); - descriptor_map::iterator end = registered_descriptors_.end(); - while (iter != end) - { - for (int i = 0; i < max_ops; ++i) - ops.push(iter->second.op_queue_[i]); - iter->second.shutdown_ = true; - ++iter; - } - - timer_queues_.get_all_timers(ops); - } + ASIO_DECL void shutdown_service(); // Initialise the task. - void init_task() - { - io_service_.init_task(); - } + ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type descriptor, - per_descriptor_data& descriptor_data) + ASIO_DECL int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op) { - mutex::scoped_lock lock(registered_descriptors_mutex_); - - descriptor_map::iterator new_entry = registered_descriptors_.insert( - std::make_pair(descriptor, descriptor_state())).first; - descriptor_data = &new_entry->second; - - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; - ev.data.ptr = descriptor_data; - int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); - if (result != 0) - return errno; - - descriptor_data->shutdown_ = false; - - return 0; + io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. - void start_op(int op_type, socket_type descriptor, + ASIO_DECL void start_op(int op_type, socket_type descriptor, per_descriptor_data& descriptor_data, - reactor_op* op, bool allow_speculative) - { - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - if (descriptor_data->shutdown_) - return; - - if (descriptor_data->op_queue_[op_type].empty()) - { - if (allow_speculative - && (op_type != read_op - || descriptor_data->op_queue_[except_op].empty())) - { - if (op->perform()) - { - descriptor_lock.unlock(); - io_service_.post_immediate_completion(op); - return; - } - } - else - { - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR | EPOLLHUP - | EPOLLOUT | EPOLLPRI | EPOLLET; - ev.data.ptr = descriptor_data; - epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); - } - } - - descriptor_data->op_queue_[op_type].push(op); - io_service_.work_started(); - } + reactor_op* op, bool allow_speculative); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. - void cancel_ops(socket_type, per_descriptor_data& descriptor_data) - { - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - - op_queue ops; - for (int i = 0; i < max_ops; ++i) - { - while (reactor_op* op = descriptor_data->op_queue_[i].front()) - { - op->ec_ = asio::error::operation_aborted; - descriptor_data->op_queue_[i].pop(); - ops.push(op); - } - } - - descriptor_lock.unlock(); - - io_service_.post_deferred_completions(ops); - } + ASIO_DECL void cancel_ops(socket_type descriptor, + per_descriptor_data& descriptor_data); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor, - per_descriptor_data& descriptor_data) - { - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); - - // Remove the descriptor from the set of known descriptors. The descriptor - // will be automatically removed from the epoll set when it is closed. - descriptor_data->shutdown_ = true; - - op_queue ops; - for (int i = 0; i < max_ops; ++i) - { - while (reactor_op* op = descriptor_data->op_queue_[i].front()) - { - op->ec_ = asio::error::operation_aborted; - descriptor_data->op_queue_[i].pop(); - ops.push(op); - } - } - - descriptor_lock.unlock(); - - registered_descriptors_.erase(descriptor); - - descriptors_lock.unlock(); - - io_service_.post_deferred_completions(ops); - } + ASIO_DECL void close_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); // Add a new timer queue to the reactor. template - void add_timer_queue(timer_queue& timer_queue) - { - mutex::scoped_lock lock(mutex_); - timer_queues_.insert(&timer_queue); - } + void add_timer_queue(timer_queue& timer_queue); // Remove a timer queue from the reactor. template - void remove_timer_queue(timer_queue& timer_queue) - { - mutex::scoped_lock lock(mutex_); - timer_queues_.erase(&timer_queue); - } + void remove_timer_queue(timer_queue& timer_queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template - void schedule_timer(timer_queue& timer_queue, - const typename Time_Traits::time_type& time, timer_op* op, void* token) - { - mutex::scoped_lock lock(mutex_); - if (!shutdown_) - { - bool earliest = timer_queue.enqueue_timer(time, op, token); - io_service_.work_started(); - if (earliest) - { -#if defined(ASIO_HAS_TIMERFD) - if (timer_fd_ != -1) - { - itimerspec new_timeout; - itimerspec old_timeout; - int flags = get_timeout(new_timeout); - timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); - return; - } -#endif // defined(ASIO_HAS_TIMERFD) - interrupter_.interrupt(); - } - } - } + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template - std::size_t cancel_timer(timer_queue& timer_queue, void* token) - { - mutex::scoped_lock lock(mutex_); - op_queue ops; - std::size_t n = timer_queue.cancel_timer(token, ops); - lock.unlock(); - io_service_.post_deferred_completions(ops); - return n; - } + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer); // Run epoll once until interrupted or events are ready to be dispatched. - void run(bool block, op_queue& ops) - { - // Calculate a timeout only if timerfd is not used. - int timeout; - if (timer_fd_ != -1) - timeout = block ? -1 : 0; - else - { - mutex::scoped_lock lock(mutex_); - timeout = block ? get_timeout() : 0; - } - - // Block on the epoll descriptor. - epoll_event events[128]; - int num_events = epoll_wait(epoll_fd_, events, 128, timeout); - -#if defined(ASIO_HAS_TIMERFD) - bool check_timers = (timer_fd_ == -1); -#else // defined(ASIO_HAS_TIMERFD) - bool check_timers = true; -#endif // defined(ASIO_HAS_TIMERFD) - - // Dispatch the waiting events. - for (int i = 0; i < num_events; ++i) - { - void* ptr = events[i].data.ptr; - if (ptr == &interrupter_) - { - // No need to reset the interrupter since we're leaving the descriptor - // in a ready-to-read state and relying on edge-triggered notifications - // to make it so that we only get woken up when the descriptor's epoll - // registration is updated. - -#if defined(ASIO_HAS_TIMERFD) - if (timer_fd_ == -1) - check_timers = true; -#else // defined(ASIO_HAS_TIMERFD) - check_timers = true; -#endif // defined(ASIO_HAS_TIMERFD) - } -#if defined(ASIO_HAS_TIMERFD) - else if (ptr == &timer_fd_) - { - check_timers = true; - } -#endif // defined(ASIO_HAS_TIMERFD) - else - { - descriptor_state* descriptor_data = static_cast(ptr); - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - - // Exception operations must be processed first to ensure that any - // out-of-band data is read before normal data. - static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; - for (int j = max_ops - 1; j >= 0; --j) - { - if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP)) - { - while (reactor_op* op = descriptor_data->op_queue_[j].front()) - { - if (op->perform()) - { - descriptor_data->op_queue_[j].pop(); - ops.push(op); - } - else - break; - } - } - } - } - } - - if (check_timers) - { - mutex::scoped_lock common_lock(mutex_); - timer_queues_.get_ready_timers(ops); - -#if defined(ASIO_HAS_TIMERFD) - if (timer_fd_ != -1) - { - itimerspec new_timeout; - itimerspec old_timeout; - int flags = get_timeout(new_timeout); - timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); - } -#endif // defined(ASIO_HAS_TIMERFD) - } - } + ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the select loop. - void interrupt() - { - epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLIN | EPOLLERR | EPOLLET; - ev.data.ptr = &interrupter_; - epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev); - } + ASIO_DECL void interrupt(); private: // The hint to pass to epoll_create to size its data structures. @@ -422,44 +132,26 @@ private: // Create the epoll file descriptor. Throws an exception if the descriptor // cannot be created. - static int do_epoll_create() - { - int fd = epoll_create(epoll_size); - if (fd == -1) - { - boost::throw_exception( - asio::system_error( - asio::error_code(errno, - asio::error::get_system_category()), - "epoll")); - } - return fd; - } + ASIO_DECL static int do_epoll_create(); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); + + // Called to recalculate and update the timeout. + ASIO_DECL void update_timeout(); // Get the timeout value for the epoll_wait call. The timeout value is // returned as a number of milliseconds. A return value of -1 indicates // that epoll_wait should block indefinitely. - int get_timeout() - { - // By default we will wait no longer than 5 minutes. This will ensure that - // any changes to the system clock are detected after no longer than this. - return timer_queues_.wait_duration_msec(5 * 60 * 1000); - } + ASIO_DECL int get_timeout(); #if defined(ASIO_HAS_TIMERFD) // Get the timeout value for the timer descriptor. The return value is the // flag argument to be used when calling timerfd_settime. - int get_timeout(itimerspec& ts) - { - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - - long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); - ts.it_value.tv_sec = usec / 1000000; - ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1; - - return usec ? 0 : TFD_TIMER_ABSTIME; - } + ASIO_DECL int get_timeout(itimerspec& ts); #endif // defined(ASIO_HAS_TIMERFD) // The io_service implementation used to post completions. @@ -486,22 +178,20 @@ private: // Mutex to protect access to the registered descriptors. mutex registered_descriptors_mutex_; - // Keep track of all registered descriptors. This code relies on the fact that - // the hash_map implementation pools deleted nodes, meaning that we can assume - // our descriptor_state pointer remains valid even after the entry is removed. - // Technically this is not true for C++98, as that standard says that spliced - // elements in a list are invalidated. However, C++0x fixes this shortcoming - // so we'll just assume that C++98 std::list implementations will do the right - // thing anyway. - typedef detail::hash_map descriptor_map; - descriptor_map registered_descriptors_; + // Keep track of all registered descriptors. + object_pool registered_descriptors_; }; } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_EPOLL) - #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/epoll_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/epoll_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_EPOLL) + #endif // ASIO_DETAIL_EPOLL_REACTOR_HPP diff --git a/ext/asio/asio/detail/epoll_reactor_fwd.hpp b/ext/asio/asio/detail/epoll_reactor_fwd.hpp index 266bccdab0..49df6ed9b7 100644 --- a/ext/asio/asio/detail/epoll_reactor_fwd.hpp +++ b/ext/asio/asio/detail/epoll_reactor_fwd.hpp @@ -1,8 +1,8 @@ // -// epoll_reactor_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~ +// detail/epoll_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,19 +15,9 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#if !defined(ASIO_DISABLE_EPOLL) -#if defined(__linux__) // This service is only supported on Linux. - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) // Only kernels >= 2.5.45. - -// Define this to indicate that epoll is supported on the target platform. -#define ASIO_HAS_EPOLL 1 +#if defined(ASIO_HAS_EPOLL) namespace asio { namespace detail { @@ -37,10 +27,6 @@ class epoll_reactor; } // namespace detail } // namespace asio -#endif // LINUX_VERSION_CODE >= KERNEL_VERSION (2,5,45) -#endif // defined(__linux__) -#endif // !defined(ASIO_DISABLE_EPOLL) - -#include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_EPOLL) #endif // ASIO_DETAIL_EPOLL_REACTOR_FWD_HPP diff --git a/ext/asio/asio/detail/event.hpp b/ext/asio/asio/detail/event.hpp index 65aa4cba7f..46ee8a6bd9 100644 --- a/ext/asio/asio/detail/event.hpp +++ b/ext/asio/asio/detail/event.hpp @@ -1,8 +1,8 @@ // -// event.hpp -// ~~~~~~~~~ +// detail/event.hpp +// ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_event.hpp" @@ -45,6 +41,4 @@ typedef posix_event event; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_EVENT_HPP diff --git a/ext/asio/asio/detail/eventfd_select_interrupter.hpp b/ext/asio/asio/detail/eventfd_select_interrupter.hpp index 63b7ac4ff5..0d3b95802d 100644 --- a/ext/asio/asio/detail/eventfd_select_interrupter.hpp +++ b/ext/asio/asio/detail/eventfd_select_interrupter.hpp @@ -1,8 +1,8 @@ // -// eventfd_select_interrupter.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/eventfd_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,36 +16,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#if defined(__linux__) -# if !defined(ASIO_DISABLE_EVENTFD) -# include -# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -# define ASIO_HAS_EVENTFD -# endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) -# endif // !defined(ASIO_DISABLE_EVENTFD) -#endif // defined(__linux__) +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_EVENTFD) #include "asio/detail/push_options.hpp" -#include -#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 -# include -#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 -# include -#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/socket_types.hpp" namespace asio { namespace detail { @@ -54,87 +29,16 @@ class eventfd_select_interrupter { public: // Constructor. - eventfd_select_interrupter() - { -#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 - write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); -#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 - write_descriptor_ = read_descriptor_ = ::eventfd(0, 0); -#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 - if (read_descriptor_ != -1) - { - ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); - } - else - { - int pipe_fds[2]; - if (pipe(pipe_fds) == 0) - { - read_descriptor_ = pipe_fds[0]; - ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); - write_descriptor_ = pipe_fds[1]; - ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); - } - else - { - asio::error_code ec(errno, - asio::error::get_system_category()); - asio::system_error e(ec, "eventfd_select_interrupter"); - boost::throw_exception(e); - } - } - } + ASIO_DECL eventfd_select_interrupter(); // Destructor. - ~eventfd_select_interrupter() - { - if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) - ::close(write_descriptor_); - if (read_descriptor_ != -1) - ::close(read_descriptor_); - } + ASIO_DECL ~eventfd_select_interrupter(); // Interrupt the select call. - void interrupt() - { - uint64_t counter(1UL); - int result = ::write(write_descriptor_, &counter, sizeof(uint64_t)); - (void)result; - } + ASIO_DECL void interrupt(); // Reset the select interrupt. Returns true if the call was interrupted. - bool reset() - { - if (write_descriptor_ == read_descriptor_) - { - for (;;) - { - // Only perform one read. The kernel maintains an atomic counter. - uint64_t counter(0); - errno = 0; - int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t)); - if (bytes_read < 0 && errno == EINTR) - continue; - bool was_interrupted = (bytes_read > 0); - return was_interrupted; - } - } - else - { - for (;;) - { - // Clear all data from the pipe. - char data[1024]; - int bytes_read = ::read(read_descriptor_, data, sizeof(data)); - if (bytes_read < 0 && errno == EINTR) - continue; - bool was_interrupted = (bytes_read > 0); - while (bytes_read == sizeof(data)) - bytes_read = ::read(read_descriptor_, data, sizeof(data)); - return was_interrupted; - } - } - } + ASIO_DECL bool reset(); // Get the read descriptor to be passed to select. int read_descriptor() const @@ -159,8 +63,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_EVENTFD) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/eventfd_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_EVENTFD) + #endif // ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/asio/detail/fd_set_adapter.hpp b/ext/asio/asio/detail/fd_set_adapter.hpp index a575491bfb..953cad1729 100644 --- a/ext/asio/asio/detail/fd_set_adapter.hpp +++ b/ext/asio/asio/detail/fd_set_adapter.hpp @@ -1,8 +1,8 @@ // -// fd_set_adapter.hpp -// ~~~~~~~~~~~~~~~~~~ +// detail/fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/posix_fd_set_adapter.hpp" #include "asio/detail/win_fd_set_adapter.hpp" @@ -36,6 +31,4 @@ typedef posix_fd_set_adapter fd_set_adapter; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_FD_SET_ADAPTER_HPP diff --git a/ext/asio/asio/detail/fenced_block.hpp b/ext/asio/asio/detail/fenced_block.hpp index 60198bc65b..70c47e13cf 100644 --- a/ext/asio/asio/detail/fenced_block.hpp +++ b/ext/asio/asio/detail/fenced_block.hpp @@ -1,8 +1,8 @@ // -// fenced_block.hpp -// ~~~~~~~~~~~~~~~~ +// detail/fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,23 +15,25 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +#if !defined(BOOST_HAS_THREADS) \ + || defined(ASIO_DISABLE_THREADS) \ + || defined(ASIO_DISABLE_FENCED_BLOCK) # include "asio/detail/null_fenced_block.hpp" #elif defined(__MACH__) && defined(__APPLE__) # include "asio/detail/macos_fenced_block.hpp" #elif defined(__sun) # include "asio/detail/solaris_fenced_block.hpp" +#elif defined(__GNUC__) && defined(__arm__) +# include "asio/detail/gcc_arm_fenced_block.hpp" +#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) +# include "asio/detail/gcc_hppa_fenced_block.hpp" #elif defined(__GNUC__) \ && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ && !defined(__INTEL_COMPILER) && !defined(__ICL) \ && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) -# include "asio/detail/gcc_fenced_block.hpp" +# include "asio/detail/gcc_sync_fenced_block.hpp" #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) # include "asio/detail/gcc_x86_fenced_block.hpp" #elif defined(BOOST_WINDOWS) && !defined(UNDER_CE) @@ -43,17 +45,23 @@ namespace asio { namespace detail { -#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +#if !defined(BOOST_HAS_THREADS) \ + || defined(ASIO_DISABLE_THREADS) \ + || defined(ASIO_DISABLE_FENCED_BLOCK) typedef null_fenced_block fenced_block; #elif defined(__MACH__) && defined(__APPLE__) typedef macos_fenced_block fenced_block; #elif defined(__sun) typedef solaris_fenced_block fenced_block; +#elif defined(__GNUC__) && defined(__arm__) +typedef gcc_arm_fenced_block fenced_block; +#elif defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) +typedef gcc_hppa_fenced_block fenced_block; #elif defined(__GNUC__) \ && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ && !defined(__INTEL_COMPILER) && !defined(__ICL) \ && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) -typedef gcc_fenced_block fenced_block; +typedef gcc_sync_fenced_block fenced_block; #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) typedef gcc_x86_fenced_block fenced_block; #elif defined(BOOST_WINDOWS) && !defined(UNDER_CE) @@ -65,6 +73,4 @@ typedef null_fenced_block fenced_block; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/gcc_arm_fenced_block.hpp b/ext/asio/asio/detail/gcc_arm_fenced_block.hpp new file mode 100644 index 0000000000..76c4d99900 --- /dev/null +++ b/ext/asio/asio/detail/gcc_arm_fenced_block.hpp @@ -0,0 +1,76 @@ +// +// detail/gcc_arm_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && defined(__arm__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_arm_fenced_block + : private noncopyable +{ +public: + // Constructor. + gcc_arm_fenced_block() + { + barrier(); + } + + // Destructor. + ~gcc_arm_fenced_block() + { + barrier(); + } + +private: + static void barrier() + { +#if defined(__ARM_ARCH_4__) \ + || defined(__ARM_ARCH_4T__) \ + || defined(__ARM_ARCH_5__) \ + || defined(__ARM_ARCH_5E__) \ + || defined(__ARM_ARCH_5T__) \ + || defined(__ARM_ARCH_5TE__) \ + || defined(__ARM_ARCH_5TEJ__) \ + || defined(__ARM_ARCH_6__) \ + || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6T2__) + int a = 0, b = 0; + __asm__ __volatile__ ("swp %0, %1, [%2]" + : "=&r"(a) : "r"(1), "r"(&b) : "memory", "cc"); +#else + // ARMv7 and later. + __asm__ __volatile__ ("dmb" : : : "memory"); +#endif + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && defined(__arm__) + +#endif // ASIO_DETAIL_GCC_ARM_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/gcc_hppa_fenced_block.hpp b/ext/asio/asio/detail/gcc_hppa_fenced_block.hpp new file mode 100644 index 0000000000..215086a6cc --- /dev/null +++ b/ext/asio/asio/detail/gcc_hppa_fenced_block.hpp @@ -0,0 +1,58 @@ +// +// detail/gcc_hppa_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_hppa_fenced_block + : private noncopyable +{ +public: + // Constructor. + gcc_hppa_fenced_block() + { + barrier(); + } + + // Destructor. + ~gcc_hppa_fenced_block() + { + barrier(); + } + +private: + static void barrier() + { + // This is just a placeholder and almost certainly not sufficient. + __asm__ __volatile__ ("" : : : "memory"); + } +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) && (defined(__hppa) || defined(__hppa__)) + +#endif // ASIO_DETAIL_GCC_HPPA_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/gcc_sync_fenced_block.hpp b/ext/asio/asio/detail/gcc_sync_fenced_block.hpp new file mode 100644 index 0000000000..569b559124 --- /dev/null +++ b/ext/asio/asio/detail/gcc_sync_fenced_block.hpp @@ -0,0 +1,61 @@ +// +// detail/gcc_sync_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP +#define ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(__GNUC__) \ + && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) \ + && !defined(__INTEL_COMPILER) && !defined(__ICL) \ + && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class gcc_sync_fenced_block + : private noncopyable +{ +public: + // Constructor. + gcc_sync_fenced_block() + : value_(0) + { + __sync_lock_test_and_set(&value_, 1); + } + + // Destructor. + ~gcc_sync_fenced_block() + { + __sync_lock_release(&value_); + } + +private: + int value_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(__GNUC__) + // && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)) + // && !defined(__INTEL_COMPILER) && !defined(__ICL) + // && !defined(__ICC) && !defined(__ECC) && !defined(__PATHSCALE__) + +#endif // ASIO_DETAIL_GCC_SYNC_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/gcc_x86_fenced_block.hpp b/ext/asio/asio/detail/gcc_x86_fenced_block.hpp index 48119f4c39..3e2e5bcb7e 100644 --- a/ext/asio/asio/detail/gcc_x86_fenced_block.hpp +++ b/ext/asio/asio/detail/gcc_x86_fenced_block.hpp @@ -1,8 +1,8 @@ // -// gcc_x86_fenced_block.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/gcc_x86_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -54,8 +52,8 @@ private: } // namespace detail } // namespace asio -#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - #include "asio/detail/pop_options.hpp" +#endif // defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + #endif // ASIO_DETAIL_GCC_X86_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/handler_alloc_helpers.hpp b/ext/asio/asio/detail/handler_alloc_helpers.hpp index 0ac42a7b12..9ae80de916 100644 --- a/ext/asio/asio/detail/handler_alloc_helpers.hpp +++ b/ext/asio/asio/detail/handler_alloc_helpers.hpp @@ -1,8 +1,8 @@ // -// handler_alloc_helpers.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/handler_alloc_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/handler_alloc_hook.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/handler_alloc_hook.hpp" + +#include "asio/detail/push_options.hpp" // Calls to asio_handler_allocate and asio_handler_deallocate must be made from // a namespace that does not contain any overloads of these functions. The @@ -56,203 +54,31 @@ inline void deallocate(void* p, std::size_t s, Handler& h) } // namespace asio_handler_alloc_helpers -namespace asio { -namespace detail { - -// Traits for handler allocation. -template -struct handler_alloc_traits -{ - typedef Handler handler_type; - typedef Object value_type; - typedef Object* pointer_type; - BOOST_STATIC_CONSTANT(std::size_t, value_size = sizeof(Object)); -}; - -template -class handler_ptr; - -// Helper class to provide RAII on uninitialised handler memory. -template -class raw_handler_ptr - : private noncopyable -{ -public: - typedef typename Alloc_Traits::handler_type handler_type; - typedef typename Alloc_Traits::value_type value_type; - typedef typename Alloc_Traits::pointer_type pointer_type; - BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); - - // Constructor allocates the memory. - raw_handler_ptr(handler_type& handler) - : handler_(handler), - pointer_(static_cast( - asio_handler_alloc_helpers::allocate(value_size, handler_))) - { - } - - // Destructor automatically deallocates memory, unless it has been stolen by - // a handler_ptr object. - ~raw_handler_ptr() - { - if (pointer_) - asio_handler_alloc_helpers::deallocate( - pointer_, value_size, handler_); - } - -private: - friend class handler_ptr; - handler_type& handler_; - pointer_type pointer_; -}; - -// Helper class to provide RAII on uninitialised handler memory. -template -class handler_ptr - : private noncopyable -{ -public: - typedef typename Alloc_Traits::handler_type handler_type; - typedef typename Alloc_Traits::value_type value_type; - typedef typename Alloc_Traits::pointer_type pointer_type; - BOOST_STATIC_CONSTANT(std::size_t, value_size = Alloc_Traits::value_size); - typedef raw_handler_ptr raw_ptr_type; - - // Take ownership of existing memory. - handler_ptr(handler_type& handler, pointer_type pointer) - : handler_(handler), - pointer_(pointer) - { - } - - // Construct object in raw memory and take ownership if construction succeeds. - handler_ptr(raw_ptr_type& raw_ptr) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type(a1)) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type(a1, a2)) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3)) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4)) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, - Arg5& a5) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5)) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, - Arg5& a5, Arg6& a6) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6)) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, - Arg5& a5, Arg6& a6, Arg7& a7) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type(a1, a2, a3, a4, a5, a6, a7)) - { - raw_ptr.pointer_ = 0; - } - - // Construct object in raw memory and take ownership if construction succeeds. - template - handler_ptr(raw_ptr_type& raw_ptr, Arg1& a1, Arg2& a2, Arg3& a3, Arg4& a4, - Arg5& a5, Arg6& a6, Arg7& a7, Arg8& a8) - : handler_(raw_ptr.handler_), - pointer_(new (raw_ptr.pointer_) value_type( - a1, a2, a3, a4, a5, a6, a7, a8)) - { - raw_ptr.pointer_ = 0; - } - - // Destructor automatically deallocates memory, unless it has been released. - ~handler_ptr() - { - reset(); - } - - // Get the memory. - pointer_type get() const - { - return pointer_; - } - - // Release ownership of the memory. - pointer_type release() - { - pointer_type tmp = pointer_; - pointer_ = 0; - return tmp; - } - - // Explicitly destroy and deallocate the memory. - void reset() - { - if (pointer_) - { - pointer_->value_type::~value_type(); - asio_handler_alloc_helpers::deallocate( - pointer_, value_size, handler_); - pointer_ = 0; - } - } - -private: - handler_type& handler_; - pointer_type pointer_; -}; - -} // namespace detail -} // namespace asio +#define ASIO_DEFINE_HANDLER_PTR(op) \ + struct ptr \ + { \ + Handler* h; \ + void* v; \ + op* p; \ + ~ptr() \ + { \ + reset(); \ + } \ + void reset() \ + { \ + if (p) \ + { \ + p->~op(); \ + p = 0; \ + } \ + if (v) \ + { \ + asio_handler_alloc_helpers::deallocate(v, sizeof(op), *h); \ + v = 0; \ + } \ + } \ + } \ + /**/ #include "asio/detail/pop_options.hpp" diff --git a/ext/asio/asio/detail/handler_invoke_helpers.hpp b/ext/asio/asio/detail/handler_invoke_helpers.hpp index 8332567c9f..5b122d9f69 100644 --- a/ext/asio/asio/detail/handler_invoke_helpers.hpp +++ b/ext/asio/asio/detail/handler_invoke_helpers.hpp @@ -1,8 +1,8 @@ // -// handler_invoke_helpers.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/handler_invoke_helpers.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/handler_invoke_hook.hpp" +#include "asio/detail/push_options.hpp" + // Calls to asio_handler_invoke must be made from a namespace that does not // contain overloads of this function. The asio_handler_invoke_helpers // namespace is defined here for that purpose. diff --git a/ext/asio/asio/detail/hash_map.hpp b/ext/asio/asio/detail/hash_map.hpp index b2a1517865..a3957dfa33 100644 --- a/ext/asio/asio/detail/hash_map.hpp +++ b/ext/asio/asio/detail/hash_map.hpp @@ -1,8 +1,8 @@ // -// hash_map.hpp -// ~~~~~~~~~~~~ +// detail/hash_map.hpp +// ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,33 +15,38 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/noncopyable.hpp" -#include "asio/detail/socket_types.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/socket_types.hpp" +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { -template -inline std::size_t calculate_hash_value(const T& t) +inline std::size_t calculate_hash_value(int i) { - return boost::hash_value(t); + return static_cast(i); } -#if defined(_WIN64) +inline std::size_t calculate_hash_value(void* p) +{ + return reinterpret_cast(p) + + (reinterpret_cast(p) >> 3); +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) inline std::size_t calculate_hash_value(SOCKET s) { return static_cast(s); } -#endif // defined(_WIN64) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) // Note: assumes K and V are POD types. template diff --git a/ext/asio/asio/detail/impl/descriptor_ops.ipp b/ext/asio/asio/detail/impl/descriptor_ops.ipp new file mode 100644 index 0000000000..8e0c27d223 --- /dev/null +++ b/ext/asio/asio/detail/impl/descriptor_ops.ipp @@ -0,0 +1,382 @@ +// +// detail/impl/descriptor_ops.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP +#define ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/descriptor_ops.hpp" +#include "asio/error.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace descriptor_ops { + +ASIO_DECL +int open(const char* path, int flags, asio::error_code& ec) +{ + errno = 0; + int result = error_wrapper(::open(path, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +int close(int d, state_type& state, asio::error_code& ec) +{ + int result = 0; + if (d != -1) + { + if (state & internal_non_blocking) + { +#if defined(__SYMBIAN32__) + int flags = ::fcntl(d, F_GETFL, 0); + if (flags >= 0) + ::fcntl(d, F_SETFL, flags & ~O_NONBLOCK); +#else // defined(__SYMBIAN32__) + ioctl_arg_type arg = 0; + ::ioctl(d, FIONBIO, &arg); +#endif // defined(__SYMBIAN32__) + state &= ~internal_non_blocking; + } + + errno = 0; + result = error_wrapper(::close(d), ec); + } + + if (result == 0) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +bool set_internal_non_blocking(int d, + state_type& state, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return false; + } + + errno = 0; +#if defined(__SYMBIAN32__) + int result = error_wrapper(::fcntl(d, F_GETFL, 0), ec); + if (result >= 0) + { + errno = 0; + result = error_wrapper(::fcntl(d, F_SETFL, result | O_NONBLOCK), ec); + } +#else // defined(__SYMBIAN32__) + ioctl_arg_type arg = 1; + int result = error_wrapper(::ioctl(d, FIONBIO, &arg), ec); +#endif // defined(__SYMBIAN32__) + + if (result >= 0) + { + ec = asio::error_code(); + state |= internal_non_blocking; + return true; + } + + return false; +} + +ASIO_DECL +std::size_t sync_read(int d, state_type state, buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (all_empty) + { + ec = asio::error_code(); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + errno = 0; + int bytes = error_wrapper(::readv(d, bufs, static_cast(count)), ec); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Check for EOF. + if (bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_read(d, ec) < 0) + return 0; + } +} + +ASIO_DECL +bool non_blocking_read(int d, buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + errno = 0; + int bytes = error_wrapper(::readv(d, bufs, static_cast(count)), ec); + + // Check for end of stream. + if (bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes > 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +ASIO_DECL +std::size_t sync_write(int d, state_type state, const buf* bufs, + std::size_t count, bool all_empty, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes on a stream is a no-op. + if (all_empty) + { + ec = asio::error_code(); + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + errno = 0; + int bytes = error_wrapper(::writev(d, bufs, static_cast(count)), ec); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for descriptor to become ready. + if (descriptor_ops::poll_write(d, ec) < 0) + return 0; + } +} + +ASIO_DECL +bool non_blocking_write(int d, const buf* bufs, std::size_t count, + asio::error_code& ec, std::size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + errno = 0; + int bytes = error_wrapper(::writev(d, bufs, static_cast(count)), ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +ASIO_DECL +int ioctl(int d, state_type& state, long cmd, + ioctl_arg_type* arg, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + errno = 0; + int result = error_wrapper(::ioctl(d, cmd, arg), ec); + + if (result >= 0) + { + ec = asio::error_code(); + + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the descriptor is + // already in the correct state. This ensures that the underlying + // descriptor is put into the state that has been requested by the user. If + // the ioctl syscall was successful then we need to update the flags to + // match. + if (cmd == static_cast(FIONBIO)) + { + if (*arg) + { + state |= user_set_non_blocking; + } + else + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + } + } + + return result; +} + +ASIO_DECL +int fcntl(int d, long cmd, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + errno = 0; + int result = error_wrapper(::fcntl(d, cmd), ec); + if (result != -1) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +int fcntl(int d, long cmd, long arg, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + errno = 0; + int result = error_wrapper(::fcntl(d, cmd, arg), ec); + if (result != -1) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +int poll_read(int d, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLIN; + fds.revents = 0; + errno = 0; + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +int poll_write(int d, asio::error_code& ec) +{ + if (d == -1) + { + ec = asio::error::bad_descriptor; + return -1; + } + + pollfd fds; + fds.fd = d; + fds.events = POLLOUT; + fds.revents = 0; + errno = 0; + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +} + +} // namespace descriptor_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_DESCRIPTOR_OPS_IPP diff --git a/ext/asio/asio/detail/impl/dev_poll_reactor.hpp b/ext/asio/asio/detail/impl/dev_poll_reactor.hpp new file mode 100644 index 0000000000..7fbef82c3d --- /dev/null +++ b/ext/asio/asio/detail/impl/dev_poll_reactor.hpp @@ -0,0 +1,77 @@ +// +// detail/impl/dev_poll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP +#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void dev_poll_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void dev_poll_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void dev_poll_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + io_service_.post_immediate_completion(op); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + io_service_.work_started(); + if (earliest) + interrupter_.interrupt(); +} + +template +std::size_t dev_poll_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); + return n; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_HPP diff --git a/ext/asio/asio/detail/impl/dev_poll_reactor.ipp b/ext/asio/asio/detail/impl/dev_poll_reactor.ipp new file mode 100644 index 0000000000..0d1456fcbb --- /dev/null +++ b/ext/asio/asio/detail/impl/dev_poll_reactor.ipp @@ -0,0 +1,338 @@ +// +// detail/impl/dev_poll_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP +#define ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_DEV_POLL) + +#include "asio/detail/dev_poll_reactor.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +dev_poll_reactor::dev_poll_reactor(asio::io_service& io_service) + : asio::detail::service_base(io_service), + io_service_(use_service(io_service)), + mutex_(), + dev_poll_fd_(do_dev_poll_create()), + interrupter_(), + shutdown_(false) +{ + // Add the interrupter's descriptor to /dev/poll. + ::pollfd ev = { 0 }; + ev.fd = interrupter_.read_descriptor(); + ev.events = POLLIN | POLLERR; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); +} + +dev_poll_reactor::~dev_poll_reactor() +{ + shutdown_service(); + ::close(dev_poll_fd_); +} + +void dev_poll_reactor::shutdown_service() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); +} + +void dev_poll_reactor::init_task() +{ + io_service_.init_task(); +} + +int dev_poll_reactor::register_descriptor(socket_type, per_descriptor_data&) +{ + return 0; +} + +void dev_poll_reactor::start_op(int op_type, socket_type descriptor, + dev_poll_reactor::per_descriptor_data&, + reactor_op* op, bool allow_speculative) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + post_immediate_completion(op); + return; + } + + if (allow_speculative) + { + if (op_type != read_op || !op_queue_[except_op].has_operation(descriptor)) + { + if (!op_queue_[op_type].has_operation(descriptor)) + { + if (op->perform()) + { + lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } + } + } + } + + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + io_service_.work_started(); + if (first) + { + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLERR | POLLHUP; + if (op_type == read_op + || op_queue_[read_op].has_operation(descriptor)) + ev.events |= POLLIN; + if (op_type == write_op + || op_queue_[write_op].has_operation(descriptor)) + ev.events |= POLLOUT; + if (op_type == except_op + || op_queue_[except_op].has_operation(descriptor)) + ev.events |= POLLPRI; + interrupter_.interrupt(); + } +} + +void dev_poll_reactor::cancel_ops(socket_type descriptor, + dev_poll_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void dev_poll_reactor::close_descriptor(socket_type descriptor, + dev_poll_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from /dev/poll. + ::pollfd& ev = add_pending_event_change(descriptor); + ev.events = POLLREMOVE; + interrupter_.interrupt(); + + // Cancel any outstanding operations associated with the descriptor. + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void dev_poll_reactor::run(bool block, op_queue& ops) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && op_queue_[read_op].empty() && op_queue_[write_op].empty() + && op_queue_[except_op].empty() && timer_queues_.all_empty()) + return; + + // Write the pending event registration changes to the /dev/poll descriptor. + std::size_t events_size = sizeof(::pollfd) * pending_event_changes_.size(); + if (events_size > 0) + { + errno = 0; + int result = ::write(dev_poll_fd_, + &pending_event_changes_[0], events_size); + if (result != static_cast(events_size)) + { + asio::error_code ec = asio::error_code( + errno, asio::error::get_system_category()); + for (std::size_t i = 0; i < pending_event_changes_.size(); ++i) + { + int descriptor = pending_event_changes_[i].fd; + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + pending_event_changes_.clear(); + pending_event_change_index_.clear(); + } + + int timeout = block ? get_timeout() : 0; + lock.unlock(); + + // Block on the /dev/poll descriptor. + ::pollfd events[128] = { { 0 } }; + ::dvpoll dp = { 0 }; + dp.dp_fds = events; + dp.dp_nfds = 128; + dp.dp_timeout = timeout; + int num_events = ::ioctl(dev_poll_fd_, DP_POLL, &dp); + + lock.lock(); + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].fd; + if (descriptor == interrupter_.read_descriptor()) + { + interrupter_.reset(); + } + else + { + bool more_reads = false; + bool more_writes = false; + bool more_except = false; + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + if (events[i].events & (POLLPRI | POLLERR | POLLHUP)) + more_except = + op_queue_[except_op].perform_operations(descriptor, ops); + else + more_except = op_queue_[except_op].has_operation(descriptor); + + if (events[i].events & (POLLIN | POLLERR | POLLHUP)) + more_reads = op_queue_[read_op].perform_operations(descriptor, ops); + else + more_reads = op_queue_[read_op].has_operation(descriptor); + + if (events[i].events & (POLLOUT | POLLERR | POLLHUP)) + more_writes = op_queue_[write_op].perform_operations(descriptor, ops); + else + more_writes = op_queue_[write_op].has_operation(descriptor); + + if ((events[i].events & (POLLERR | POLLHUP)) != 0 + && !more_except && !more_reads && !more_writes) + { + // If we have an event and no operations associated with the + // descriptor then we need to delete the descriptor from /dev/poll. + // The poll operation can produce POLLHUP or POLLERR events when there + // is no operation pending, so if we do not remove the descriptor we + // can end up in a tight polling loop. + ::pollfd ev = { 0 }; + ev.fd = descriptor; + ev.events = POLLREMOVE; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + } + else + { + ::pollfd ev = { 0 }; + ev.fd = descriptor; + ev.events = POLLERR | POLLHUP; + if (more_reads) + ev.events |= POLLIN; + if (more_writes) + ev.events |= POLLOUT; + if (more_except) + ev.events |= POLLPRI; + ev.revents = 0; + int result = ::write(dev_poll_fd_, &ev, sizeof(ev)); + if (result != sizeof(ev)) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + for (int j = 0; j < max_ops; ++j) + op_queue_[j].cancel_operations(descriptor, ops, ec); + } + } + } + } + timer_queues_.get_ready_timers(ops); +} + +void dev_poll_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +int dev_poll_reactor::do_dev_poll_create() +{ + int fd = ::open("/dev/poll", O_RDWR); + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "/dev/poll"); + } + return fd; +} + +void dev_poll_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void dev_poll_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +int dev_poll_reactor::get_timeout() +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + return timer_queues_.wait_duration_msec(5 * 60 * 1000); +} + +void dev_poll_reactor::cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec) +{ + bool need_interrupt = false; + op_queue ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + io_service_.post_deferred_completions(ops); + if (need_interrupt) + interrupter_.interrupt(); +} + +::pollfd& dev_poll_reactor::add_pending_event_change(int descriptor) +{ + hash_map::iterator iter + = pending_event_change_index_.find(descriptor); + if (iter == pending_event_change_index_.end()) + { + std::size_t index = pending_event_changes_.size(); + pending_event_changes_.reserve(pending_event_changes_.size() + 1); + pending_event_change_index_.insert(std::make_pair(descriptor, index)); + pending_event_changes_.push_back(::pollfd()); + pending_event_changes_[index].fd = descriptor; + pending_event_changes_[index].revents = 0; + return pending_event_changes_[index]; + } + else + { + return pending_event_changes_[iter->second]; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_DEV_POLL) + +#endif // ASIO_DETAIL_IMPL_DEV_POLL_REACTOR_IPP diff --git a/ext/asio/asio/detail/impl/epoll_reactor.hpp b/ext/asio/asio/detail/impl/epoll_reactor.hpp new file mode 100644 index 0000000000..9f50a235bc --- /dev/null +++ b/ext/asio/asio/detail/impl/epoll_reactor.hpp @@ -0,0 +1,75 @@ +// +// detail/impl/epoll_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP +#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if defined(ASIO_HAS_EPOLL) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void epoll_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void epoll_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void epoll_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op) +{ + mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + io_service_.post_immediate_completion(op); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + io_service_.work_started(); + if (earliest) + update_timeout(); +} + +template +std::size_t epoll_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer) +{ + mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); + return n; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_HPP diff --git a/ext/asio/asio/detail/impl/epoll_reactor.ipp b/ext/asio/asio/detail/impl/epoll_reactor.ipp new file mode 100644 index 0000000000..a95b8f290d --- /dev/null +++ b/ext/asio/asio/detail/impl/epoll_reactor.ipp @@ -0,0 +1,390 @@ +// +// detail/impl/epoll_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP +#define ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EPOLL) + +#include +#include +#include "asio/detail/epoll_reactor.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#if defined(ASIO_HAS_TIMERFD) +# include +#endif // defined(ASIO_HAS_TIMERFD) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +epoll_reactor::epoll_reactor(asio::io_service& io_service) + : asio::detail::service_base(io_service), + io_service_(use_service(io_service)), + mutex_(), + epoll_fd_(do_epoll_create()), +#if defined(ASIO_HAS_TIMERFD) + timer_fd_(timerfd_create(CLOCK_MONOTONIC, 0)), +#else // defined(ASIO_HAS_TIMERFD) + timer_fd_(-1), +#endif // defined(ASIO_HAS_TIMERFD) + interrupter_(), + shutdown_(false) +{ + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } +} + +epoll_reactor::~epoll_reactor() +{ + close(epoll_fd_); + if (timer_fd_ != -1) + close(timer_fd_); +} + +void epoll_reactor::shutdown_service() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + while (descriptor_state* state = registered_descriptors_.first()) + { + for (int i = 0; i < max_ops; ++i) + ops.push(state->op_queue_[i]); + state->shutdown_ = true; + registered_descriptors_.free(state); + } + + timer_queues_.get_all_timers(ops); +} + +void epoll_reactor::init_task() +{ + io_service_.init_task(); +} + +int epoll_reactor::register_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + mutex::scoped_lock lock(registered_descriptors_mutex_); + + descriptor_data = registered_descriptors_.alloc(); + descriptor_data->shutdown_ = false; + + lock.unlock(); + + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; + ev.data.ptr = descriptor_data; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); + if (result != 0) + return errno; + + return 0; +} + +void epoll_reactor::start_op(int op_type, socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data, + reactor_op* op, bool allow_speculative) +{ + if (!descriptor_data) + { + op->ec_ = asio::error::bad_descriptor; + post_immediate_completion(op); + return; + } + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (descriptor_data->shutdown_) + { + post_immediate_completion(op); + return; + } + + if (descriptor_data->op_queue_[op_type].empty()) + { + if (allow_speculative + && (op_type != read_op + || descriptor_data->op_queue_[except_op].empty())) + { + if (op->perform()) + { + descriptor_lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } + } + else + { + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP + | EPOLLOUT | EPOLLPRI | EPOLLET; + ev.data.ptr = descriptor_data; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, descriptor, &ev); + } + } + + descriptor_data->op_queue_[op_type].push(op); + io_service_.work_started(); +} + +void epoll_reactor::cancel_ops(socket_type, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + io_service_.post_deferred_completions(ops); +} + +void epoll_reactor::close_descriptor(socket_type, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + + if (!descriptor_data->shutdown_) + { + // Remove the descriptor from the set of known descriptors. The descriptor + // will be automatically removed from the epoll set when it is closed. + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + registered_descriptors_.free(descriptor_data); + descriptor_data = 0; + + descriptors_lock.unlock(); + + io_service_.post_deferred_completions(ops); + } +} + +void epoll_reactor::run(bool block, op_queue& ops) +{ + // Calculate a timeout only if timerfd is not used. + int timeout; + if (timer_fd_ != -1) + timeout = block ? -1 : 0; + else + { + mutex::scoped_lock lock(mutex_); + timeout = block ? get_timeout() : 0; + } + + // Block on the epoll descriptor. + epoll_event events[128]; + int num_events = epoll_wait(epoll_fd_, events, 128, timeout); + +#if defined(ASIO_HAS_TIMERFD) + bool check_timers = (timer_fd_ == -1); +#else // defined(ASIO_HAS_TIMERFD) + bool check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + void* ptr = events[i].data.ptr; + if (ptr == &interrupter_) + { + // No need to reset the interrupter since we're leaving the descriptor + // in a ready-to-read state and relying on edge-triggered notifications + // to make it so that we only get woken up when the descriptor's epoll + // registration is updated. + +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ == -1) + check_timers = true; +#else // defined(ASIO_HAS_TIMERFD) + check_timers = true; +#endif // defined(ASIO_HAS_TIMERFD) + } +#if defined(ASIO_HAS_TIMERFD) + else if (ptr == &timer_fd_) + { + check_timers = true; + } +#endif // defined(ASIO_HAS_TIMERFD) + else + { + descriptor_state* descriptor_data = static_cast(ptr); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events[i].events & (flag[j] | EPOLLERR | EPOLLHUP)) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + if (op->perform()) + { + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + else + break; + } + } + } + } + } + + if (check_timers) + { + mutex::scoped_lock common_lock(mutex_); + timer_queues_.get_ready_timers(ops); + +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + } +#endif // defined(ASIO_HAS_TIMERFD) + } +} + +void epoll_reactor::interrupt() +{ + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_MOD, interrupter_.read_descriptor(), &ev); +} + +int epoll_reactor::do_epoll_create() +{ + int fd = epoll_create(epoll_size); + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "epoll"); + } + return fd; +} + +void epoll_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void epoll_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +void epoll_reactor::update_timeout() +{ +#if defined(ASIO_HAS_TIMERFD) + if (timer_fd_ != -1) + { + itimerspec new_timeout; + itimerspec old_timeout; + int flags = get_timeout(new_timeout); + timerfd_settime(timer_fd_, flags, &new_timeout, &old_timeout); + return; + } +#endif // defined(ASIO_HAS_TIMERFD) + interrupt(); +} + +int epoll_reactor::get_timeout() +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + return timer_queues_.wait_duration_msec(5 * 60 * 1000); +} + +#if defined(ASIO_HAS_TIMERFD) +int epoll_reactor::get_timeout(itimerspec& ts) +{ + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + ts.it_value.tv_sec = usec / 1000000; + ts.it_value.tv_nsec = usec ? (usec % 1000000) * 1000 : 1; + + return usec ? 0 : TFD_TIMER_ABSTIME; +} +#endif // defined(ASIO_HAS_TIMERFD) + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EPOLL) + +#endif // ASIO_DETAIL_IMPL_EPOLL_REACTOR_IPP diff --git a/ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp b/ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp new file mode 100644 index 0000000000..959d356af7 --- /dev/null +++ b/ext/asio/asio/detail/impl/eventfd_select_interrupter.ipp @@ -0,0 +1,125 @@ +// +// detail/impl/eventfd_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_EVENTFD) + +#include +#include +#include +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +# include +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +# include +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +#include "asio/detail/eventfd_select_interrupter.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +eventfd_select_interrupter::eventfd_select_interrupter() +{ +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); +#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + write_descriptor_ = read_descriptor_ = ::eventfd(0, 0); +#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 + if (read_descriptor_ != -1) + { + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + } + else + { + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "eventfd_select_interrupter"); + } + } +} + +eventfd_select_interrupter::~eventfd_select_interrupter() +{ + if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) + ::close(write_descriptor_); + if (read_descriptor_ != -1) + ::close(read_descriptor_); +} + +void eventfd_select_interrupter::interrupt() +{ + uint64_t counter(1UL); + int result = ::write(write_descriptor_, &counter, sizeof(uint64_t)); + (void)result; +} + +bool eventfd_select_interrupter::reset() +{ + if (write_descriptor_ == read_descriptor_) + { + for (;;) + { + // Only perform one read. The kernel maintains an atomic counter. + uint64_t counter(0); + errno = 0; + int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + return was_interrupted; + } + } + else + { + for (;;) + { + // Clear all data from the pipe. + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_EVENTFD) + +#endif // ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP diff --git a/ext/asio/asio/detail/impl/kqueue_reactor.hpp b/ext/asio/asio/detail/impl/kqueue_reactor.hpp new file mode 100644 index 0000000000..7950c3b976 --- /dev/null +++ b/ext/asio/asio/detail/impl/kqueue_reactor.hpp @@ -0,0 +1,79 @@ +// +// detail/impl/kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP +#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void kqueue_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void kqueue_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void kqueue_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + io_service_.post_immediate_completion(op); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + io_service_.work_started(); + if (earliest) + interrupt(); +} + +template +std::size_t kqueue_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); + return n; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_HPP diff --git a/ext/asio/asio/detail/impl/kqueue_reactor.ipp b/ext/asio/asio/detail/impl/kqueue_reactor.ipp new file mode 100644 index 0000000000..8db77cb3e5 --- /dev/null +++ b/ext/asio/asio/detail/impl/kqueue_reactor.ipp @@ -0,0 +1,385 @@ +// +// detail/impl/kqueue_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP +#define ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_KQUEUE) + +#include "asio/detail/kqueue_reactor.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +#if defined(__NetBSD__) +# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ + EV_SET(ev, ident, filt, flags, fflags, \ + data, reinterpret_cast(udata)) +#else +# define ASIO_KQUEUE_EV_SET(ev, ident, filt, flags, fflags, data, udata) \ + EV_SET(ev, ident, filt, flags, fflags, data, udata) +#endif + +namespace asio { +namespace detail { + +kqueue_reactor::kqueue_reactor(asio::io_service& io_service) + : asio::detail::service_base(io_service), + io_service_(use_service(io_service)), + mutex_(), + kqueue_fd_(do_kqueue_create()), + interrupter_(), + shutdown_(false) +{ + // The interrupter is put into a permanently readable state. Whenever we + // want to interrupt the blocked kevent call we register a one-shot read + // operation against the descriptor. + interrupter_.interrupt(); +} + +kqueue_reactor::~kqueue_reactor() +{ + close(kqueue_fd_); +} + +void kqueue_reactor::shutdown_service() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + op_queue ops; + + while (descriptor_state* state = registered_descriptors_.first()) + { + for (int i = 0; i < max_ops; ++i) + ops.push(state->op_queue_[i]); + state->shutdown_ = true; + registered_descriptors_.free(state); + } + + timer_queues_.get_all_timers(ops); +} + +void kqueue_reactor::init_task() +{ + io_service_.init_task(); +} + +int kqueue_reactor::register_descriptor(socket_type, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + mutex::scoped_lock lock(registered_descriptors_mutex_); + + descriptor_data = registered_descriptors_.alloc(); + descriptor_data->shutdown_ = false; + + return 0; +} + +void kqueue_reactor::start_op(int op_type, socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data, + reactor_op* op, bool allow_speculative) +{ + if (!descriptor_data) + { + op->ec_ = asio::error::bad_descriptor; + post_immediate_completion(op); + return; + } + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + if (descriptor_data->shutdown_) + { + post_immediate_completion(op); + return; + } + + bool first = descriptor_data->op_queue_[op_type].empty(); + if (first) + { + if (allow_speculative) + { + if (op_type != read_op || descriptor_data->op_queue_[except_op].empty()) + { + if (op->perform()) + { + descriptor_lock.unlock(); + io_service_.post_immediate_completion(op); + return; + } + } + } + } + + descriptor_data->op_queue_[op_type].push(op); + io_service_.work_started(); + + if (first) + { + struct kevent event; + switch (op_type) + { + case read_op: + ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + break; + case write_op: + ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + break; + case except_op: + if (!descriptor_data->op_queue_[read_op].empty()) + return; // Already registered for read events. + ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); + break; + } + + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + op->ec_ = asio::error_code(errno, + asio::error::get_system_category()); + descriptor_data->op_queue_[op_type].pop(); + io_service_.post_deferred_completion(op); + } + } +} + +void kqueue_reactor::cancel_ops(socket_type, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_lock.unlock(); + + io_service_.post_deferred_completions(ops); +} + +void kqueue_reactor::close_descriptor(socket_type, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + + if (!descriptor_data->shutdown_) + { + // Remove the descriptor from the set of known descriptors. The descriptor + // will be automatically removed from the kqueue set when it is closed. + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + { + while (reactor_op* op = descriptor_data->op_queue_[i].front()) + { + op->ec_ = asio::error::operation_aborted; + descriptor_data->op_queue_[i].pop(); + ops.push(op); + } + } + + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + registered_descriptors_.free(descriptor_data); + descriptor_data = 0; + + descriptors_lock.unlock(); + + io_service_.post_deferred_completions(ops); + } +} + +void kqueue_reactor::run(bool block, op_queue& ops) +{ + mutex::scoped_lock lock(mutex_); + + // Determine how long to block while waiting for events. + timespec timeout_buf = { 0, 0 }; + timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf; + + lock.unlock(); + + // Block on the kqueue descriptor. + struct kevent events[128]; + int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); + + // Dispatch the waiting events. + for (int i = 0; i < num_events; ++i) + { + int descriptor = events[i].ident; + void* ptr = reinterpret_cast(events[i].udata); + if (ptr == &interrupter_) + { + // No need to reset the interrupter since we're leaving the descriptor + // in a ready-to-read state and relying on one-shot notifications. + } + else + { + descriptor_state* descriptor_data = static_cast(ptr); + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. +#if defined(__NetBSD__) + static const unsigned int filter[max_ops] = +#else + static const int filter[max_ops] = +#endif + { EVFILT_READ, EVFILT_WRITE, EVFILT_READ }; + for (int j = max_ops - 1; j >= 0; --j) + { + if (events[i].filter == filter[j]) + { + if (j != except_op || events[i].flags & EV_OOBAND) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + if (events[i].flags & EV_ERROR) + { + op->ec_ = asio::error_code(events[i].data, + asio::error::get_system_category()); + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + if (op->perform()) + { + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + else + break; + } + } + } + } + + // Renew registration for event notifications. + struct kevent event; + switch (events[i].filter) + { + case EVFILT_READ: + if (!descriptor_data->op_queue_[read_op].empty()) + ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + else if (!descriptor_data->op_queue_[except_op].empty()) + ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, + EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); + else + continue; + case EVFILT_WRITE: + if (!descriptor_data->op_queue_[write_op].empty()) + ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE, + EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + else + continue; + default: + break; + } + if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) + { + asio::error_code error(errno, + asio::error::get_system_category()); + for (int j = 0; j < max_ops; ++j) + { + while (reactor_op* op = descriptor_data->op_queue_[j].front()) + { + op->ec_ = error; + descriptor_data->op_queue_[j].pop(); + ops.push(op); + } + } + } + } + } + + lock.lock(); + timer_queues_.get_ready_timers(ops); +} + +void kqueue_reactor::interrupt() +{ + struct kevent event; + ASIO_KQUEUE_EV_SET(&event, interrupter_.read_descriptor(), + EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &interrupter_); + ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); +} + +int kqueue_reactor::do_kqueue_create() +{ + int fd = ::kqueue(); + if (fd == -1) + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "kqueue"); + } + return fd; +} + +void kqueue_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void kqueue_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +timespec* kqueue_reactor::get_timeout(timespec& ts) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + return &ts; +} + +} // namespace detail +} // namespace asio + +#undef ASIO_KQUEUE_EV_SET + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_KQUEUE) + +#endif // ASIO_DETAIL_IMPL_KQUEUE_REACTOR_IPP diff --git a/ext/asio/asio/detail/impl/pipe_select_interrupter.ipp b/ext/asio/asio/detail/impl/pipe_select_interrupter.ipp new file mode 100644 index 0000000000..aac20d43d2 --- /dev/null +++ b/ext/asio/asio/detail/impl/pipe_select_interrupter.ipp @@ -0,0 +1,96 @@ +// +// detail/impl/pipe_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(BOOST_WINDOWS) +#if !defined(__CYGWIN__) +#if !defined(__SYMBIAN32__) +#if !defined(ASIO_HAS_EVENTFD) + +#include +#include +#include +#include +#include "asio/detail/pipe_select_interrupter.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +pipe_select_interrupter::pipe_select_interrupter() +{ + int pipe_fds[2]; + if (pipe(pipe_fds) == 0) + { + read_descriptor_ = pipe_fds[0]; + ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); + write_descriptor_ = pipe_fds[1]; + ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); + } + else + { + asio::error_code ec(errno, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "pipe_select_interrupter"); + } +} + +pipe_select_interrupter::~pipe_select_interrupter() +{ + if (read_descriptor_ != -1) + ::close(read_descriptor_); + if (write_descriptor_ != -1) + ::close(write_descriptor_); +} + +void pipe_select_interrupter::interrupt() +{ + char byte = 0; + int result = ::write(write_descriptor_, &byte, 1); + (void)result; +} + +bool pipe_select_interrupter::reset() +{ + for (;;) + { + char data[1024]; + int bytes_read = ::read(read_descriptor_, data, sizeof(data)); + if (bytes_read < 0 && errno == EINTR) + continue; + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = ::read(read_descriptor_, data, sizeof(data)); + return was_interrupted; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_EVENTFD) +#endif // !defined(__SYMBIAN32__) +#endif // !defined(__CYGWIN__) +#endif // !defined(BOOST_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_PIPE_SELECT_INTERRUPTER_IPP diff --git a/ext/asio/asio/detail/impl/posix_event.ipp b/ext/asio/asio/detail/impl/posix_event.ipp new file mode 100644 index 0000000000..3206418c54 --- /dev/null +++ b/ext/asio/asio/detail/impl/posix_event.ipp @@ -0,0 +1,46 @@ +// +// detail/impl/posix_event.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_EVENT_IPP +#define ASIO_DETAIL_IMPL_POSIX_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/posix_event.hpp" +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_event::posix_event() + : signalled_(false) +{ + int error = ::pthread_cond_init(&cond_, 0); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_EVENT_IPP diff --git a/ext/asio/asio/detail/impl/posix_mutex.ipp b/ext/asio/asio/detail/impl/posix_mutex.ipp new file mode 100644 index 0000000000..12b6659745 --- /dev/null +++ b/ext/asio/asio/detail/impl/posix_mutex.ipp @@ -0,0 +1,46 @@ +// +// detail/impl/posix_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP +#define ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/posix_mutex.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_mutex::posix_mutex() +{ + int error = ::pthread_mutex_init(&mutex_, 0); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "mutex"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_MUTEX_IPP diff --git a/ext/asio/asio/detail/impl/posix_thread.ipp b/ext/asio/asio/detail/impl/posix_thread.ipp new file mode 100644 index 0000000000..ce91c57bbc --- /dev/null +++ b/ext/asio/asio/detail/impl/posix_thread.ipp @@ -0,0 +1,74 @@ +// +// detail/impl/posix_thread.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_THREAD_IPP +#define ASIO_DETAIL_IMPL_POSIX_THREAD_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/posix_thread.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +posix_thread::~posix_thread() +{ + if (!joined_) + ::pthread_detach(thread_); +} + +void posix_thread::join() +{ + if (!joined_) + { + ::pthread_join(thread_, 0); + joined_ = true; + } +} + +void posix_thread::start_thread(func_base* arg) +{ + int error = ::pthread_create(&thread_, 0, + asio_detail_posix_thread_function, arg); + if (error != 0) + { + delete arg; + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } +} + +void* asio_detail_posix_thread_function(void* arg) +{ + posix_thread::auto_func_base_ptr func = { + static_cast(arg) }; + func.ptr->run(); + return 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_THREAD_IPP diff --git a/ext/asio/asio/detail/impl/posix_tss_ptr.ipp b/ext/asio/asio/detail/impl/posix_tss_ptr.ipp new file mode 100644 index 0000000000..89b40f7022 --- /dev/null +++ b/ext/asio/asio/detail/impl/posix_tss_ptr.ipp @@ -0,0 +1,46 @@ +// +// detail/impl/posix_tss_ptr.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP +#define ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#include "asio/detail/posix_tss_ptr.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void posix_tss_ptr_create(pthread_key_t& key) +{ + int error = ::pthread_key_create(&key, 0); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "tss"); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + +#endif // ASIO_DETAIL_IMPL_POSIX_TSS_PTR_IPP diff --git a/ext/asio/asio/detail/impl/reactive_descriptor_service.ipp b/ext/asio/asio/detail/impl/reactive_descriptor_service.ipp new file mode 100644 index 0000000000..706d60f423 --- /dev/null +++ b/ext/asio/asio/detail/impl/reactive_descriptor_service.ipp @@ -0,0 +1,136 @@ +// +// detail/impl/reactive_descriptor_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include "asio/error.hpp" +#include "asio/detail/reactive_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_descriptor_service::reactive_descriptor_service( + asio::io_service& io_service) + : reactor_(asio::use_service(io_service)) +{ + reactor_.init_task(); +} + +void reactive_descriptor_service::shutdown_service() +{ +} + +void reactive_descriptor_service::construct( + reactive_descriptor_service::implementation_type& impl) +{ + impl.descriptor_ = -1; + impl.state_ = 0; +} + +void reactive_descriptor_service::destroy( + reactive_descriptor_service::implementation_type& impl) +{ + if (is_open(impl)) + reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); + + asio::error_code ignored_ec; + descriptor_ops::close(impl.descriptor_, impl.state_, ignored_ec); +} + +asio::error_code reactive_descriptor_service::assign( + reactive_descriptor_service::implementation_type& impl, + const native_type& native_descriptor, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_descriptor, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.descriptor_ = native_descriptor; + impl.state_ = 0; + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_descriptor_service::close( + reactive_descriptor_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); + + if (descriptor_ops::close(impl.descriptor_, impl.state_, ec) == 0) + construct(impl); + + return ec; +} + +asio::error_code reactive_descriptor_service::cancel( + reactive_descriptor_service::implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_); + ec = asio::error_code(); + return ec; +} + +void reactive_descriptor_service::start_op( + reactive_descriptor_service::implementation_type& impl, + int op_type, reactor_op* op, bool non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & descriptor_ops::non_blocking) || + descriptor_ops::set_internal_non_blocking( + impl.descriptor_, impl.state_, op->ec_)) + { + reactor_.start_op(op_type, impl.descriptor_, + impl.reactor_data_, op, non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_DESCRIPTOR_SERVICE_IPP diff --git a/ext/asio/asio/detail/impl/reactive_serial_port_service.ipp b/ext/asio/asio/detail/impl/reactive_serial_port_service.ipp new file mode 100644 index 0000000000..c4df79303c --- /dev/null +++ b/ext/asio/asio/detail/impl/reactive_serial_port_service.ipp @@ -0,0 +1,151 @@ +// +// detail/impl/reactive_serial_port_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include +#include "asio/detail/reactive_serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_serial_port_service::reactive_serial_port_service( + asio::io_service& io_service) + : descriptor_service_(io_service) +{ +} + +void reactive_serial_port_service::shutdown_service() +{ + descriptor_service_.shutdown_service(); +} + +asio::error_code reactive_serial_port_service::open( + reactive_serial_port_service::implementation_type& impl, + const std::string& device, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + descriptor_ops::state_type state = 0; + int fd = descriptor_ops::open(device.c_str(), + O_RDWR | O_NONBLOCK | O_NOCTTY, ec); + if (fd < 0) + return ec; + + int s = descriptor_ops::fcntl(fd, F_GETFL, ec); + if (s >= 0) + s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec); + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + return ec; + } + + // Set up default serial port options. + termios ios; + errno = 0; + s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec); + if (s >= 0) + { +#if defined(_BSD_SOURCE) + ::cfmakeraw(&ios); +#else + ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK + | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + ios.c_oflag &= ~OPOST; + ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + ios.c_cflag &= ~(CSIZE | PARENB); + ios.c_cflag |= CS8; +#endif + ios.c_iflag |= IGNPAR; + ios.c_cflag |= CREAD | CLOCAL; + errno = 0; + s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec); + } + if (s < 0) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + return ec; + } + + // We're done. Take ownership of the serial port descriptor. + if (descriptor_service_.assign(impl, fd, ec)) + { + asio::error_code ignored_ec; + descriptor_ops::close(fd, state, ignored_ec); + } + + return ec; +} + +asio::error_code reactive_serial_port_service::do_set_option( + reactive_serial_port_service::implementation_type& impl, + reactive_serial_port_service::store_function_type store, + const void* option, asio::error_code& ec) +{ + termios ios; + errno = 0; + descriptor_ops::error_wrapper(::tcgetattr( + descriptor_service_.native(impl), &ios), ec); + if (ec) + return ec; + + if (store(option, ios, ec)) + return ec; + + errno = 0; + descriptor_ops::error_wrapper(::tcsetattr( + descriptor_service_.native(impl), TCSANOW, &ios), ec); + return ec; +} + +asio::error_code reactive_serial_port_service::do_get_option( + const reactive_serial_port_service::implementation_type& impl, + reactive_serial_port_service::load_function_type load, + void* option, asio::error_code& ec) const +{ + termios ios; + errno = 0; + descriptor_ops::error_wrapper(::tcgetattr( + descriptor_service_.native(impl), &ios), ec); + if (ec) + return ec; + + return load(option, ios, ec); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#endif // defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_SERIAL_PORT_SERVICE_IPP diff --git a/ext/asio/asio/detail/impl/reactive_socket_service_base.ipp b/ext/asio/asio/detail/impl/reactive_socket_service_base.ipp new file mode 100644 index 0000000000..54e7944875 --- /dev/null +++ b/ext/asio/asio/detail/impl/reactive_socket_service_base.ipp @@ -0,0 +1,212 @@ +// +// detail/reactive_socket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) + +#include "asio/detail/reactive_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +reactive_socket_service_base::reactive_socket_service_base( + asio::io_service& io_service) + : reactor_(use_service(io_service)) +{ + reactor_.init_task(); +} + +void reactive_socket_service_base::shutdown_service() +{ +} + +void reactive_socket_service_base::construct( + reactive_socket_service_base::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; +} + +void reactive_socket_service_base::destroy( + reactive_socket_service_base::base_implementation_type& impl) +{ + if (impl.socket_ != invalid_socket) + { + reactor_.close_descriptor(impl.socket_, impl.reactor_data_); + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + } +} + +asio::error_code reactive_socket_service_base::close( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + reactor_.close_descriptor(impl.socket_, impl.reactor_data_); + + if (socket_ops::close(impl.socket_, impl.state_, true, ec) == 0) + construct(impl); + + return ec; +} + +asio::error_code reactive_socket_service_base::cancel( + reactive_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + + reactor_.cancel_ops(impl.socket_, impl.reactor_data_); + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_socket_service_base::do_open( + reactive_socket_service_base::base_implementation_type& impl, + int af, int type, int protocol, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(af, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + ec = asio::error_code(); + return ec; +} + +asio::error_code reactive_socket_service_base::do_assign( + reactive_socket_service_base::base_implementation_type& impl, int type, + const reactive_socket_service_base::native_type& native_socket, + asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (int err = reactor_.register_descriptor( + native_socket, impl.reactor_data_)) + { + ec = asio::error_code(err, + asio::error::get_system_category()); + return ec; + } + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + ec = asio::error_code(); + return ec; +} + +void reactive_socket_service_base::start_op( + reactive_socket_service_base::base_implementation_type& impl, + int op_type, reactor_op* op, bool non_blocking, bool noop) +{ + if (!noop) + { + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, op->ec_)) + { + reactor_.start_op(op_type, impl.socket_, + impl.reactor_data_, op, non_blocking); + return; + } + } + + reactor_.post_immediate_completion(op); +} + +void reactive_socket_service_base::start_accept_op( + reactive_socket_service_base::base_implementation_type& impl, + reactor_op* op, bool peer_is_open) +{ + if (!peer_is_open) + start_op(impl, reactor::read_op, op, true, false); + else + { + op->ec_ = asio::error::already_open; + reactor_.post_immediate_completion(op); + } +} + +void reactive_socket_service_base::start_connect_op( + reactive_socket_service_base::base_implementation_type& impl, + reactor_op* op, const socket_addr_type* addr, size_t addrlen) +{ + if ((impl.state_ & socket_ops::non_blocking) + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + reactor_.start_op(reactor::connect_op, + impl.socket_, impl.reactor_data_, op, false); + return; + } + } + } + + reactor_.post_immediate_completion(op); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_REACTIVE_SOCKET_SERVICE_BASE_IPP diff --git a/ext/asio/asio/detail/impl/resolver_service_base.ipp b/ext/asio/asio/detail/impl/resolver_service_base.ipp new file mode 100644 index 0000000000..a844a2374e --- /dev/null +++ b/ext/asio/asio/detail/impl/resolver_service_base.ipp @@ -0,0 +1,106 @@ +// +// detail/impl/resolver_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/resolver_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolver_service_base::work_io_service_runner +{ +public: + work_io_service_runner(asio::io_service& io_service) + : io_service_(io_service) {} + void operator()() { io_service_.run(); } +private: + asio::io_service& io_service_; +}; + +resolver_service_base::resolver_service_base( + asio::io_service& io_service) + : io_service_impl_(asio::use_service(io_service)), + work_io_service_(new asio::io_service), + work_io_service_impl_(asio::use_service< + io_service_impl>(*work_io_service_)), + work_(new asio::io_service::work(*work_io_service_)), + work_thread_(0) +{ +} + +resolver_service_base::~resolver_service_base() +{ + shutdown_service(); +} + +void resolver_service_base::shutdown_service() +{ + work_.reset(); + if (work_io_service_) + { + work_io_service_->stop(); + if (work_thread_) + { + work_thread_->join(); + work_thread_.reset(); + } + work_io_service_.reset(); + } +} + +void resolver_service_base::construct( + resolver_service_base::implementation_type& impl) +{ + impl.reset(static_cast(0), socket_ops::noop_deleter()); +} + +void resolver_service_base::destroy( + resolver_service_base::implementation_type&) +{ +} + +void resolver_service_base::cancel( + resolver_service_base::implementation_type& impl) +{ + impl.reset(static_cast(0), socket_ops::noop_deleter()); +} + +void resolver_service_base::start_resolve_op(operation* op) +{ + start_work_thread(); + io_service_impl_.work_started(); + work_io_service_impl_.post_immediate_completion(op); +} + +void resolver_service_base::start_work_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + if (!work_thread_) + { + work_thread_.reset(new asio::detail::thread( + work_io_service_runner(*work_io_service_))); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_RESOLVER_SERVICE_BASE_IPP diff --git a/ext/asio/asio/detail/impl/select_reactor.hpp b/ext/asio/asio/detail/impl/select_reactor.hpp new file mode 100644 index 0000000000..50d3ad42d0 --- /dev/null +++ b/ext/asio/asio/detail/impl/select_reactor.hpp @@ -0,0 +1,84 @@ +// +// detail/impl/select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP +#define ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE)) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void select_reactor::add_timer_queue(timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +// Remove a timer queue from the reactor. +template +void select_reactor::remove_timer_queue(timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void select_reactor::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + io_service_.post_immediate_completion(op); + return; + } + + bool earliest = queue.enqueue_timer(time, timer, op); + io_service_.work_started(); + if (earliest) + interrupter_.interrupt(); +} + +template +std::size_t select_reactor::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops); + lock.unlock(); + io_service_.post_deferred_completions(ops); + return n; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE)) + +#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_HPP diff --git a/ext/asio/asio/detail/impl/select_reactor.ipp b/ext/asio/asio/detail/impl/select_reactor.ipp new file mode 100644 index 0000000000..1a59be37e7 --- /dev/null +++ b/ext/asio/asio/detail/impl/select_reactor.ipp @@ -0,0 +1,273 @@ +// +// detail/impl/select_reactor.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP +#define ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE)) + +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fd_set_adapter.hpp" +#include "asio/detail/select_reactor.hpp" +#include "asio/detail/signal_blocker.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +select_reactor::select_reactor(asio::io_service& io_service) + : asio::detail::service_base(io_service), + io_service_(use_service(io_service)), + mutex_(), + interrupter_(), +#if defined(ASIO_HAS_IOCP) + stop_thread_(false), + thread_(0), +#endif // defined(ASIO_HAS_IOCP) + shutdown_(false) +{ +#if defined(ASIO_HAS_IOCP) + asio::detail::signal_blocker sb; + thread_ = new asio::detail::thread( + bind_handler(&select_reactor::call_run_thread, this)); +#endif // defined(ASIO_HAS_IOCP) +} + +select_reactor::~select_reactor() +{ + shutdown_service(); +} + +void select_reactor::shutdown_service() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + shutdown_ = true; +#if defined(ASIO_HAS_IOCP) + stop_thread_ = true; +#endif // defined(ASIO_HAS_IOCP) + lock.unlock(); + +#if defined(ASIO_HAS_IOCP) + if (thread_) + { + interrupter_.interrupt(); + thread_->join(); + delete thread_; + thread_ = 0; + } +#endif // defined(ASIO_HAS_IOCP) + + op_queue ops; + + for (int i = 0; i < max_ops; ++i) + op_queue_[i].get_all_operations(ops); + + timer_queues_.get_all_timers(ops); +} + +void select_reactor::init_task() +{ + io_service_.init_task(); +} + +int select_reactor::register_descriptor(socket_type, + select_reactor::per_descriptor_data&) +{ + return 0; +} + +void select_reactor::start_op(int op_type, socket_type descriptor, + select_reactor::per_descriptor_data&, reactor_op* op, bool) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + if (shutdown_) + { + post_immediate_completion(op); + return; + } + + bool first = op_queue_[op_type].enqueue_operation(descriptor, op); + io_service_.work_started(); + if (first) + interrupter_.interrupt(); +} + +void select_reactor::cancel_ops(socket_type descriptor, + select_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void select_reactor::close_descriptor(socket_type descriptor, + select_reactor::per_descriptor_data&) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + cancel_ops_unlocked(descriptor, asio::error::operation_aborted); +} + +void select_reactor::run(bool block, op_queue& ops) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + +#if defined(ASIO_HAS_IOCP) + // Check if the thread is supposed to stop. + if (stop_thread_) + return; +#endif // defined(ASIO_HAS_IOCP) + + // Set up the descriptor sets. + fd_set_adapter fds[max_select_ops]; + fds[read_op].set(interrupter_.read_descriptor()); + socket_type max_fd = 0; + bool have_work_to_do = !timer_queues_.all_empty(); + for (int i = 0; i < max_select_ops; ++i) + { + have_work_to_do = have_work_to_do || !op_queue_[i].empty(); + op_queue_[i].get_descriptors(fds[i], ops); + if (fds[i].max_descriptor() > max_fd) + max_fd = fds[i].max_descriptor(); + } + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty(); + op_queue_[connect_op].get_descriptors(fds[write_op], ops); + if (fds[write_op].max_descriptor() > max_fd) + max_fd = fds[write_op].max_descriptor(); + op_queue_[connect_op].get_descriptors(fds[except_op], ops); + if (fds[except_op].max_descriptor() > max_fd) + max_fd = fds[except_op].max_descriptor(); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + + // We can return immediately if there's no work to do and the reactor is + // not supposed to block. + if (!block && !have_work_to_do) + return; + + // Determine how long to block while waiting for events. + timeval tv_buf = { 0, 0 }; + timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; + + lock.unlock(); + + // Block on the select call until descriptors become ready. + asio::error_code ec; + int retval = socket_ops::select(static_cast(max_fd + 1), + fds[read_op], fds[write_op], fds[except_op], tv, ec); + + // Reset the interrupter. + if (retval > 0 && fds[read_op].is_set(interrupter_.read_descriptor())) + interrupter_.reset(); + + lock.lock(); + + // Dispatch all ready operations. + if (retval > 0) + { +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Connection operations on Windows use both except and write fd_sets. + op_queue_[connect_op].perform_operations_for_descriptors( + fds[except_op], ops); + op_queue_[connect_op].perform_operations_for_descriptors( + fds[write_op], ops); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + + // Exception operations must be processed first to ensure that any + // out-of-band data is read before normal data. + for (int i = max_select_ops - 1; i >= 0; --i) + op_queue_[i].perform_operations_for_descriptors(fds[i], ops); + } + timer_queues_.get_ready_timers(ops); +} + +void select_reactor::interrupt() +{ + interrupter_.interrupt(); +} + +#if defined(ASIO_HAS_IOCP) +void select_reactor::run_thread() +{ + asio::detail::mutex::scoped_lock lock(mutex_); + while (!stop_thread_) + { + lock.unlock(); + op_queue ops; + run(true, ops); + io_service_.post_deferred_completions(ops); + lock.lock(); + } +} + +void select_reactor::call_run_thread(select_reactor* reactor) +{ + reactor->run_thread(); +} +#endif // defined(ASIO_HAS_IOCP) + +void select_reactor::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.insert(&queue); +} + +void select_reactor::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(mutex_); + timer_queues_.erase(&queue); +} + +timeval* select_reactor::get_timeout(timeval& tv) +{ + // By default we will wait no longer than 5 minutes. This will ensure that + // any changes to the system clock are detected after no longer than this. + long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + return &tv; +} + +void select_reactor::cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec) +{ + bool need_interrupt = false; + op_queue ops; + for (int i = 0; i < max_ops; ++i) + need_interrupt = op_queue_[i].cancel_operations( + descriptor, ops, ec) || need_interrupt; + io_service_.post_deferred_completions(ops); + if (need_interrupt) + interrupter_.interrupt(); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE)) + +#endif // ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP diff --git a/ext/asio/asio/detail/impl/service_registry.hpp b/ext/asio/asio/detail/impl/service_registry.hpp new file mode 100644 index 0000000000..c2aa9ec4e4 --- /dev/null +++ b/ext/asio/asio/detail/impl/service_registry.hpp @@ -0,0 +1,70 @@ +// +// detail/impl/service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP +#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +Service& service_registry::use_service() +{ + asio::io_service::service::key key; + init_key(key, Service::id); + factory_type factory = &service_registry::create; + return *static_cast(do_use_service(key, factory)); +} + +template +void service_registry::add_service(Service* new_service) +{ + asio::io_service::service::key key; + init_key(key, Service::id); + return do_add_service(key, new_service); +} + +template +bool service_registry::has_service() const +{ + asio::io_service::service::key key; + init_key(key, Service::id); + return do_has_service(key); +} + +#if !defined(ASIO_NO_TYPEID) +template +void service_registry::init_key(asio::io_service::service::key& key, + const asio::detail::service_id& /*id*/) +{ + key.type_info_ = &typeid(typeid_wrapper); + key.id_ = 0; +} +#endif // !defined(ASIO_NO_TYPEID) + +template +asio::io_service::service* service_registry::create( + asio::io_service& owner) +{ + return new Service(owner); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_HPP diff --git a/ext/asio/asio/detail/impl/service_registry.ipp b/ext/asio/asio/detail/impl/service_registry.ipp new file mode 100644 index 0000000000..57c57b88b0 --- /dev/null +++ b/ext/asio/asio/detail/impl/service_registry.ipp @@ -0,0 +1,164 @@ +// +// detail/impl/service_registry.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP +#define ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/service_registry.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +service_registry::service_registry(asio::io_service& o) + : owner_(o), + first_service_(0) +{ +} + +service_registry::~service_registry() +{ + // Shutdown all services. This must be done in a separate loop before the + // services are destroyed since the destructors of user-defined handler + // objects may try to access other service objects. + asio::io_service::service* service = first_service_; + while (service) + { + service->shutdown_service(); + service = service->next_; + } + + // Destroy all services. + while (first_service_) + { + asio::io_service::service* next_service = first_service_->next_; + destroy(first_service_); + first_service_ = next_service; + } +} + +void service_registry::init_key(asio::io_service::service::key& key, + const asio::io_service::id& id) +{ + key.type_info_ = 0; + key.id_ = &id; +} + +bool service_registry::keys_match( + const asio::io_service::service::key& key1, + const asio::io_service::service::key& key2) +{ + if (key1.id_ && key2.id_) + if (key1.id_ == key2.id_) + return true; + if (key1.type_info_ && key2.type_info_) + if (*key1.type_info_ == *key2.type_info_) + return true; + return false; +} + +void service_registry::destroy(asio::io_service::service* service) +{ + delete service; +} + +asio::io_service::service* service_registry::do_use_service( + const asio::io_service::service::key& key, + factory_type factory) +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + // First see if there is an existing service object with the given key. + asio::io_service::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Create a new service object. The service registry's mutex is not locked + // at this time to allow for nested calls into this function from the new + // service's constructor. + lock.unlock(); + auto_service_ptr new_service = { factory(owner_) }; + new_service.ptr_->key_ = key; + lock.lock(); + + // Check that nobody else created another service object of the same type + // while the lock was released. + service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return service; + service = service->next_; + } + + // Service was successfully initialised, pass ownership to registry. + new_service.ptr_->next_ = first_service_; + first_service_ = new_service.ptr_; + new_service.ptr_ = 0; + return first_service_; +} + +void service_registry::do_add_service( + const asio::io_service::service::key& key, + asio::io_service::service* new_service) +{ + if (&owner_ != &new_service->io_service()) + boost::throw_exception(invalid_service_owner()); + + asio::detail::mutex::scoped_lock lock(mutex_); + + // Check if there is an existing service object with the given key. + asio::io_service::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + boost::throw_exception(service_already_exists()); + service = service->next_; + } + + // Take ownership of the service object. + new_service->key_ = key; + new_service->next_ = first_service_; + first_service_ = new_service; +} + +bool service_registry::do_has_service( + const asio::io_service::service::key& key) const +{ + asio::detail::mutex::scoped_lock lock(mutex_); + + asio::io_service::service* service = first_service_; + while (service) + { + if (keys_match(service->key_, key)) + return true; + service = service->next_; + } + + return false; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_SERVICE_REGISTRY_IPP diff --git a/ext/asio/asio/detail/impl/socket_ops.ipp b/ext/asio/asio/detail/impl/socket_ops.ipp new file mode 100644 index 0000000000..66006ea37a --- /dev/null +++ b/ext/asio/asio/detail/impl/socket_ops.ipp @@ -0,0 +1,2917 @@ +// +// detail/impl/socket_ops.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SOCKET_OPS_IPP +#define ASIO_DETAIL_SOCKET_OPS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { +namespace socket_ops { + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +struct msghdr { int msg_namelen; }; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) +// HP-UX doesn't declare these functions extern "C", so they are declared again +// here to avoid linker errors about undefined symbols. +extern "C" char* if_indextoname(unsigned int, char*); +extern "C" unsigned int if_nametoindex(const char*); +#endif // defined(__hpux) + +inline void clear_last_error() +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + WSASetLastError(0); +#else + errno = 0; +#endif +} + +template +inline ReturnType error_wrapper(ReturnType return_value, + asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + ec = asio::error_code(WSAGetLastError(), + asio::error::get_system_category()); +#else + ec = asio::error_code(errno, + asio::error::get_system_category()); +#endif + return return_value; +} + +template +inline socket_type call_accept(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; + socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0); + if (addrlen) + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +ASIO_DECL +socket_type accept(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return invalid_socket; + } + + clear_last_error(); + + socket_type new_s = error_wrapper(call_accept( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (new_s == invalid_socket) + return new_s; + +#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + int optval = 1; + int result = error_wrapper(::setsockopt(new_s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(new_s); + return invalid_socket; + } +#endif + + ec = asio::error_code(); + return new_s; +} + +ASIO_DECL +socket_type sync_accept(socket_type s, state_type state, + socket_addr_type* addr, std::size_t* addrlen, asio::error_code& ec) +{ + // Accept a socket. + for (;;) + { + // Try to complete the operation without blocking. + socket_type new_socket = socket_ops::accept(s, addr, addrlen, ec); + + // Check if operation succeeded. + if (new_socket != invalid_socket) + return new_socket; + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + if (state & user_set_non_blocking) + return invalid_socket; + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (state & enable_connection_aborted) + return invalid_socket; + // Fall through to retry operation. + } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (state & enable_connection_aborted) + return invalid_socket; + // Fall through to retry operation. + } +#endif // defined(EPROTO) + else + return invalid_socket; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, ec) < 0) + return invalid_socket; + } +} + +#if defined(ASIO_HAS_IOCP) + +void complete_iocp_accept(socket_type s, + void* output_buffer, DWORD address_length, + socket_addr_type* addr, std::size_t* addrlen, + socket_type new_socket, asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_aborted; + + if (!ec) + { + // Get the address of the peer. + if (addr && addrlen) + { + LPSOCKADDR local_addr = 0; + int local_addr_length = 0; + LPSOCKADDR remote_addr = 0; + int remote_addr_length = 0; + GetAcceptExSockaddrs(output_buffer, 0, address_length, + address_length, &local_addr, &local_addr_length, + &remote_addr, &remote_addr_length); + if (static_cast(remote_addr_length) > *addrlen) + { + ec = asio::error::invalid_argument; + } + else + { + using namespace std; // For memcpy. + memcpy(addr, remote_addr, remote_addr_length); + *addrlen = static_cast(remote_addr_length); + } + } + + // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname + // and getpeername will work on the accepted socket. + SOCKET update_ctx_param = s; + socket_ops::state_type state = 0; + socket_ops::setsockopt(new_socket, state, + SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + &update_ctx_param, sizeof(SOCKET), ec); + } +} + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL +bool non_blocking_accept(socket_type s, + state_type state, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, socket_type& new_socket) +{ + for (;;) + { + // Accept the waiting connection. + new_socket = socket_ops::accept(s, addr, addrlen, ec); + + // Check if operation succeeded. + if (new_socket != invalid_socket) + return true; + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Operation failed. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + { + if (state & user_set_non_blocking) + return true; + // Fall through to retry operation. + } + else if (ec == asio::error::connection_aborted) + { + if (state & enable_connection_aborted) + return true; + // Fall through to retry operation. + } +#if defined(EPROTO) + else if (ec.value() == EPROTO) + { + if (state & enable_connection_aborted) + return true; + // Fall through to retry operation. + } +#endif // defined(EPROTO) + else + return true; + + return false; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +template +inline int call_bind(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::bind(s, addr, (SockLenType)addrlen); +} + +ASIO_DECL +int bind(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(call_bind( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +int close(socket_type s, state_type& state, + bool destruction, asio::error_code& ec) +{ + int result = 0; + if (s != invalid_socket) + { +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if ((state & non_blocking) && (state & user_set_linger)) + { + ioctl_arg_type arg = 0; + ::ioctlsocket(s, FIONBIO, &arg); + state &= ~non_blocking; + } +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (state & non_blocking) + { +#if defined(__SYMBIAN32__) + int flags = ::fcntl(s, F_GETFL, 0); + if (flags >= 0) + ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); +#else // defined(__SYMBIAN32__) + ioctl_arg_type arg = 0; + ::ioctl(s, FIONBIO, &arg); +#endif // defined(__SYMBIAN32__) + state &= ~non_blocking; + } +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + + if (destruction && (state & user_set_linger)) + { + ::linger opt; + opt.l_onoff = 0; + opt.l_linger = 0; + asio::error_code ignored_ec; + socket_ops::setsockopt(s, state, SOL_SOCKET, + SO_LINGER, &opt, sizeof(opt), ignored_ec); + } + + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + result = error_wrapper(::closesocket(s), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + result = error_wrapper(::close(s), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + } + + if (result == 0) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +bool set_internal_non_blocking(socket_type s, + state_type& state, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = 1; + int result = error_wrapper(::ioctlsocket(s, FIONBIO, &arg), ec); +#elif defined(__SYMBIAN32__) + int result = error_wrapper(::fcntl(s, F_GETFL, 0), ec); + if (result >= 0) + { + clear_last_error(); + result = error_wrapper(::fcntl(s, F_SETFL, result | O_NONBLOCK), ec); + } +#else + ioctl_arg_type arg = 1; + int result = error_wrapper(::ioctl(s, FIONBIO, &arg), ec); +#endif + + if (result >= 0) + { + ec = asio::error_code(); + state |= internal_non_blocking; + return true; + } + + return false; +} + +ASIO_DECL +int shutdown(socket_type s, int what, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(::shutdown(s, what), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +template +inline int call_connect(SockLenType msghdr::*, + socket_type s, const socket_addr_type* addr, std::size_t addrlen) +{ + return ::connect(s, addr, (SockLenType)addrlen); +} + +ASIO_DECL +int connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(call_connect( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +void sync_connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + // Perform the connect operation. + socket_ops::connect(s, addr, addrlen, ec); + if (ec != asio::error::in_progress + && ec != asio::error::would_block) + { + // The connect operation finished immediately. + return; + } + + // Wait for socket to become ready. + if (socket_ops::poll_connect(s, ec) < 0) + return; + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == socket_error_retval) + return; + + // Return the result of the connect operation. + ec = asio::error_code(connect_error, + asio::error::get_system_category()); +} + +ASIO_DECL +bool non_blocking_connect(socket_type s, asio::error_code& ec) +{ + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == 0) + { + if (connect_error) + { + ec = asio::error_code(connect_error, + asio::error::get_system_category()); + } + else + ec = asio::error_code(); + } + + return true; +} + +ASIO_DECL +int socketpair(int af, int type, int protocol, + socket_type sv[2], asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(af); + (void)(type); + (void)(protocol); + (void)(sv); + ec = asio::error::operation_not_supported; + return socket_error_retval; +#else + clear_last_error(); + int result = error_wrapper(::socketpair(af, type, protocol, sv), ec); + if (result == 0) + ec = asio::error_code(); + return result; +#endif +} + +ASIO_DECL +bool sockatmark(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return false; + } + +#if defined(SIOCATMARK) + ioctl_arg_type value = 0; +# if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctlsocket(s, SIOCATMARK, &value), ec); +# else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctl(s, SIOCATMARK, &value), ec); +# endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (result == 0) + ec = asio::error_code(); +# if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +# endif // defined(ENOTTY) +#else // defined(SIOCATMARK) + int value = error_wrapper(::sockatmark(s), ec); + if (value != -1) + ec = asio::error_code(); +#endif // defined(SIOCATMARK) + + return ec ? false : value != 0; +} + +ASIO_DECL +size_t available(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + ioctl_arg_type value = 0; +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctlsocket(s, FIONREAD, &value), ec); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctl(s, FIONREAD, &value), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (result == 0) + ec = asio::error_code(); +#if defined(ENOTTY) + if (ec.value() == ENOTTY) + ec = asio::error::not_socket; +#endif // defined(ENOTTY) + + return ec ? static_cast(0) : static_cast(value); +} + +ASIO_DECL +int listen(socket_type s, int backlog, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(::listen(s, backlog), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +inline void init_buf_iov_base(void*& base, void* addr) +{ + base = addr; +} + +template +inline void init_buf_iov_base(T& base, void* addr) +{ + base = static_cast(addr); +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef WSABUF buf; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +typedef iovec buf; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +ASIO_DECL +void init_buf(buf& b, void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(data); + b.len = static_cast(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, data); + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +ASIO_DECL +void init_buf(buf& b, const void* data, size_t size) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + b.buf = static_cast(const_cast(data)); + b.len = static_cast(size); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + init_buf_iov_base(b.iov_base, const_cast(data)); + b.iov_len = size; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr) +{ + name = addr; +} + +inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) +{ + name = const_cast(addr); +} + +template +inline void init_msghdr_msg_name(T& name, socket_addr_type* addr) +{ + name = reinterpret_cast(addr); +} + +template +inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr) +{ + name = reinterpret_cast(const_cast(addr)); +} + +ASIO_DECL +int recv(socket_type s, buf* bufs, size_t count, int flags, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = error_wrapper(::WSARecv(s, bufs, + recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = bufs; + msg.msg_iovlen = count; + int result = error_wrapper(::recvmsg(s, &msg, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +ASIO_DECL +size_t sync_recv(socket_type s, state_type state, buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream is a no-op. + if (all_empty && (state & stream_oriented)) + { + ec = asio::error_code(); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes = socket_ops::recv(s, bufs, count, flags, ec); + + // Check if operation succeeded. + if (bytes > 0) + return bytes; + + // Check for EOF. + if ((state & stream_oriented) && bytes == 0) + { + ec = asio::error::eof; + return 0; + } + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL +void complete_iocp_recv(state_type state, + const weak_cancel_token_type& cancel_token, bool all_empty, + asio::error_code& ec, size_t bytes_transferred) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Check for connection closed. + else if (!ec && bytes_transferred == 0 + && (state & stream_oriented) != 0 + && !all_empty) + { + ec = asio::error::eof; + } +} + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL +bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + int bytes = socket_ops::recv(s, bufs, count, flags, ec); + + // Check for end of stream. + if (is_stream && bytes == 0) + { + ec = asio::error::eof; + return true; + } + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL +int recvfrom(socket_type s, buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Receive some data. + DWORD recv_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int tmp_addrlen = (int)*addrlen; + int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, + &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec); + *addrlen = (std::size_t)tmp_addrlen; + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = *addrlen; + msg.msg_iov = bufs; + msg.msg_iovlen = count; + int result = error_wrapper(::recvmsg(s, &msg, flags), ec); + *addrlen = msg.msg_namelen; + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +ASIO_DECL +size_t sync_recvfrom(socket_type s, state_type state, buf* bufs, + size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes = socket_ops::recvfrom(s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_read(s, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL +void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } +} + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL +bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Read some data. + int bytes = socket_ops::recvfrom(s, bufs, count, flags, addr, addrlen, ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL +int send(socket_type s, const buf* bufs, size_t count, int flags, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + DWORD send_flags = flags; + int result = error_wrapper(::WSASend(s, const_cast(bufs), + send_buf_count, &bytes_transferred, send_flags, 0, 0), ec); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = count; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + int result = error_wrapper(::sendmsg(s, &msg, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +ASIO_DECL +size_t sync_send(socket_type s, state_type state, const buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes to a stream is a no-op. + if (all_empty && (state & stream_oriented)) + { + ec = asio::error_code(); + return 0; + } + + // Read some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes = socket_ops::send(s, bufs, count, flags, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, ec) < 0) + return 0; + } +} + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL +void complete_iocp_send( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec) +{ + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } +} + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL +bool non_blocking_send(socket_type s, + const buf* bufs, size_t count, int flags, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + int bytes = socket_ops::send(s, bufs, count, flags, ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL +int sendto(socket_type s, const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // Send the data. + DWORD send_buf_count = static_cast(count); + DWORD bytes_transferred = 0; + int result = error_wrapper(::WSASendTo(s, const_cast(bufs), + send_buf_count, &bytes_transferred, flags, addr, + static_cast(addrlen), 0, 0), ec); + if (ec.value() == ERROR_NETNAME_DELETED) + ec = asio::error::connection_reset; + else if (ec.value() == ERROR_PORT_UNREACHABLE) + ec = asio::error::connection_refused; + if (result != 0) + return socket_error_retval; + ec = asio::error_code(); + return bytes_transferred; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + msghdr msg = msghdr(); + init_msghdr_msg_name(msg.msg_name, addr); + msg.msg_namelen = addrlen; + msg.msg_iov = const_cast(bufs); + msg.msg_iovlen = count; +#if defined(__linux__) + flags |= MSG_NOSIGNAL; +#endif // defined(__linux__) + int result = error_wrapper(::sendmsg(s, &msg, flags), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +ASIO_DECL +size_t sync_sendto(socket_type s, state_type state, const buf* bufs, + size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // Write some data. + for (;;) + { + // Try to complete the operation without blocking. + int bytes = socket_ops::sendto(s, bufs, count, flags, addr, addrlen, ec); + + // Check if operation succeeded. + if (bytes >= 0) + return bytes; + + // Operation failed. + if ((state & user_set_non_blocking) + || (ec != asio::error::would_block + && ec != asio::error::try_again)) + return 0; + + // Wait for socket to become ready. + if (socket_ops::poll_write(s, ec) < 0) + return 0; + } +} + +#if !defined(ASIO_HAS_IOCP) + +ASIO_DECL +bool non_blocking_sendto(socket_type s, + const buf* bufs, size_t count, int flags, + const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec, size_t& bytes_transferred) +{ + for (;;) + { + // Write some data. + int bytes = socket_ops::sendto(s, bufs, count, flags, addr, addrlen, ec); + + // Retry operation if interrupted by signal. + if (ec == asio::error::interrupted) + continue; + + // Check if we need to run the operation again. + if (ec == asio::error::would_block + || ec == asio::error::try_again) + return false; + + // Operation is complete. + if (bytes >= 0) + { + ec = asio::error_code(); + bytes_transferred = bytes; + } + else + bytes_transferred = 0; + + return true; + } +} + +#endif // !defined(ASIO_HAS_IOCP) + +ASIO_DECL +socket_type socket(int af, int type, int protocol, + asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + socket_type s = error_wrapper(::WSASocket(af, type, protocol, 0, 0, + WSA_FLAG_OVERLAPPED), ec); + if (s == invalid_socket) + return s; + + if (af == AF_INET6) + { + // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to + // false. This will only succeed on Windows Vista and later versions of + // Windows, where a dual-stack IPv4/v6 implementation is available. + DWORD optval = 0; + ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + reinterpret_cast(&optval), sizeof(optval)); + } + + ec = asio::error_code(); + + return s; +#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) + socket_type s = error_wrapper(::socket(af, type, protocol), ec); + if (s == invalid_socket) + return s; + + int optval = 1; + int result = error_wrapper(::setsockopt(s, + SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); + if (result != 0) + { + ::close(s); + return invalid_socket; + } + + return s; +#else + int s = error_wrapper(::socket(af, type, protocol), ec); + if (s >= 0) + ec = asio::error_code(); + return s; +#endif +} + +template +inline int call_setsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + const void* optval, std::size_t optlen) +{ + return ::setsockopt(s, level, optname, + (const char*)optval, (SockLenType)optlen); +} + +ASIO_DECL +int setsockopt(socket_type s, state_type& state, int level, int optname, + const void* optval, std::size_t optlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (level == custom_socket_option_level + && optname == enable_connection_aborted_option) + { + if (optlen != sizeof(int)) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (*static_cast(optval)) + state |= enable_connection_aborted; + else + state &= ~enable_connection_aborted; + ec = asio::error_code(); + return 0; + } + + if (level == SOL_SOCKET && optname == SO_LINGER) + state |= user_set_linger; + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); + if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) + { + clear_last_error(); + return error_wrapper(sso(s, level, optname, + reinterpret_cast(optval), + static_cast(optlen)), ec); + } + } + ec = asio::error::fault; + return socket_error_retval; +#else // defined(__BORLANDC__) + clear_last_error(); + int result = error_wrapper(call_setsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); + if (result == 0) + { + ec = asio::error_code(); + +#if defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + // To implement portable behaviour for SO_REUSEADDR with UDP sockets we + // need to also set SO_REUSEPORT on BSD-based platforms. + if ((state & datagram_oriented) + && level == SOL_SOCKET && optname == SO_REUSEADDR) + { + call_setsockopt(&msghdr::msg_namelen, s, + SOL_SOCKET, SO_REUSEPORT, optval, optlen); + } +#endif + } + + return result; +#endif // defined(__BORLANDC__) +} + +template +inline int call_getsockopt(SockLenType msghdr::*, + socket_type s, int level, int optname, + void* optval, std::size_t* optlen) +{ + SockLenType tmp_optlen = (SockLenType)*optlen; + int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); + *optlen = (std::size_t)tmp_optlen; + return result; +} + +ASIO_DECL +int getsockopt(socket_type s, state_type state, int level, int optname, + void* optval, size_t* optlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + if (level == custom_socket_option_level && optname == always_fail_option) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + if (level == custom_socket_option_level + && optname == enable_connection_aborted_option) + { + if (*optlen != sizeof(int)) + { + ec = asio::error::invalid_argument; + return socket_error_retval; + } + + *static_cast(optval) = (state & enable_connection_aborted) ? 1 : 0; + ec = asio::error_code(); + return 0; + } + +#if defined(__BORLANDC__) + // Mysteriously, using the getsockopt and setsockopt functions directly with + // Borland C++ results in incorrect values being set and read. The bug can be + // worked around by using function addresses resolved with GetProcAddress. + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); + if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) + { + clear_last_error(); + int tmp_optlen = static_cast(*optlen); + int result = error_wrapper(gso(s, level, optname, + reinterpret_cast(optval), &tmp_optlen), ec); + *optlen = static_cast(tmp_optlen); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are + // only supported on Windows Vista and later. To simplify program logic + // we will fake success of getting this option and specify that the + // value is non-zero (i.e. true). This corresponds to the behavior of + // IPv6 sockets on Windows platforms pre-Vista. + *static_cast(optval) = 1; + ec = asio::error_code(); + } + return result; + } + } + ec = asio::error::fault; + return socket_error_retval; +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) + clear_last_error(); + int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); + if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY + && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) + { + // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only + // supported on Windows Vista and later. To simplify program logic we will + // fake success of getting this option and specify that the value is + // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets + // on Windows platforms pre-Vista. + *static_cast(optval) = 1; + ec = asio::error_code(); + } + if (result == 0) + ec = asio::error_code(); + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + clear_last_error(); + int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, + s, level, optname, optval, optlen), ec); +#if defined(__linux__) + if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int) + && (optname == SO_SNDBUF || optname == SO_RCVBUF)) + { + // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel + // to set the buffer size to N*2. Linux puts additional stuff into the + // buffers so that only about half is actually available to the application. + // The retrieved value is divided by 2 here to make it appear as though the + // correct value has been set. + *static_cast(optval) /= 2; + } +#endif // defined(__linux__) + if (result == 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +template +inline int call_getpeername(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getpeername(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +ASIO_DECL +int getpeername(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, bool cached, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (cached) + { + // Check if socket is still connected. + DWORD connect_time = 0; + size_t connect_time_len = sizeof(connect_time); + if (socket_ops::getsockopt(s, 0, SOL_SOCKET, SO_CONNECT_TIME, + &connect_time, &connect_time_len, ec) == socket_error_retval) + { + return socket_error_retval; + } + if (connect_time == 0xFFFFFFFF) + { + ec = asio::error::not_connected; + return socket_error_retval; + } + + // The cached value is still valid. + ec = asio::error_code(); + return 0; + } +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)cached; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + + clear_last_error(); + int result = error_wrapper(call_getpeername( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +template +inline int call_getsockname(SockLenType msghdr::*, + socket_type s, socket_addr_type* addr, std::size_t* addrlen) +{ + SockLenType tmp_addrlen = (SockLenType)*addrlen; + int result = ::getsockname(s, addr, &tmp_addrlen); + *addrlen = (std::size_t)tmp_addrlen; + return result; +} + +ASIO_DECL +int getsockname(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); + int result = error_wrapper(call_getsockname( + &msghdr::msg_namelen, s, addr, addrlen), ec); + if (result == 0) + ec = asio::error_code(); + return result; +} + +ASIO_DECL +int ioctl(socket_type s, state_type& state, int cmd, + ioctl_arg_type* arg, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::ioctlsocket(s, cmd, arg), ec); +#elif defined(__MACH__) && defined(__APPLE__) \ + || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) + int result = error_wrapper(::ioctl(s, + static_cast(cmd), arg), ec); +#else + int result = error_wrapper(::ioctl(s, cmd, arg), ec); +#endif + if (result >= 0) + { + ec = asio::error_code(); + + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the socket is already in + // the correct state. This ensures that the underlying socket is put into + // the state that has been requested by the user. If the ioctl syscall was + // successful then we need to update the flags to match. + if (cmd == static_cast(FIONBIO)) + { + if (*arg) + { + state |= user_set_non_blocking; + } + else + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + state &= ~(user_set_non_blocking | internal_non_blocking); + } + } + } + + return result; +} + +ASIO_DECL +int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + if (!readfds && !writefds && !exceptfds && timeout) + { + DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + if (milliseconds == 0) + milliseconds = 1; // Force context switch. + ::Sleep(milliseconds); + ec = asio::error_code(); + return 0; + } + + // The select() call allows timeout values measured in microseconds, but the + // system clock (as wrapped by boost::posix_time::microsec_clock) typically + // has a resolution of 10 milliseconds. This can lead to a spinning select + // reactor, meaning increased CPU usage, when waiting for the earliest + // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight + // spin we'll use a minimum timeout of 1 millisecond. + if (timeout && timeout->tv_sec == 0 + && timeout->tv_usec > 0 && timeout->tv_usec < 1000) + timeout->tv_usec = 1000; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#if defined(__hpux) && defined(__SELECT) + timespec ts; + ts.tv_sec = timeout ? timeout->tv_sec : 0; + ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; + return error_wrapper(::pselect(nfds, readfds, + writefds, exceptfds, timeout ? &ts : 0, 0), ec); +#else + int result = error_wrapper(::select(nfds, readfds, + writefds, exceptfds, timeout), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif +} + +ASIO_DECL +int poll_read(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(BOOST_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + clear_last_error(); + int result = error_wrapper(::select(s, &fds, 0, 0, 0), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#else // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLIN; + fds.revents = 0; + clear_last_error(); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) +} + +ASIO_DECL +int poll_write(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(BOOST_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set fds; + FD_ZERO(&fds); + FD_SET(s, &fds); + clear_last_error(); + int result = error_wrapper(::select(s, 0, &fds, 0, 0), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#else // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + clear_last_error(); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) +} + +ASIO_DECL +int poll_connect(socket_type s, asio::error_code& ec) +{ + if (s == invalid_socket) + { + ec = asio::error::bad_descriptor; + return socket_error_retval; + } + +#if defined(BOOST_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + fd_set write_fds; + FD_ZERO(&write_fds); + FD_SET(s, &write_fds); + fd_set except_fds; + FD_ZERO(&except_fds); + FD_SET(s, &except_fds); + clear_last_error(); + int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#else // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + clear_last_error(); + int result = error_wrapper(::poll(&fds, 1, -1), ec); + if (result >= 0) + ec = asio::error_code(); + return result; +#endif // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) +} + +ASIO_DECL +const char* inet_ntop(int af, const void* src, char* dest, size_t length, + unsigned long scope_id, asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy. + + if (af != AF_INET && af != AF_INET6) + { + ec = asio::error::address_family_not_supported; + return 0; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + DWORD address_length; + if (af == AF_INET) + { + address_length = sizeof(sockaddr_in4_type); + address.v4.sin_family = AF_INET; + address.v4.sin_port = 0; + memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); + } + else // AF_INET6 + { + address_length = sizeof(sockaddr_in6_type); + address.v6.sin6_family = AF_INET6; + address.v6.sin6_port = 0; + address.v6.sin6_flowinfo = 0; + address.v6.sin6_scope_id = scope_id; + memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); + } + + DWORD string_length = static_cast(length); +#if defined(BOOST_NO_ANSI_APIS) + LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); + int result = error_wrapper(::WSAAddressToStringW(&address.base, + address_length, 0, string_buffer, &string_length), ec); + ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, dest, length, 0, 0); +#else + int result = error_wrapper(::WSAAddressToStringA( + &address.base, address_length, 0, dest, &string_length), ec); +#endif + + // Windows may set error code on success. + if (result != socket_error_retval) + ec = asio::error_code(); + + // Windows may not set an error code on failure. + else if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + return result == socket_error_retval ? 0 : dest; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + const char* result = error_wrapper(::inet_ntop(af, src, dest, length), ec); + if (result == 0 && !ec) + ec = asio::error::invalid_argument; + if (result != 0 && af == AF_INET6 && scope_id != 0) + { + using namespace std; // For strcat and sprintf. + char if_name[IF_NAMESIZE + 1] = "%"; + const in6_addr_type* ipv6_address = static_cast(src); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0) + sprintf(if_name + 1, "%lu", scope_id); + strcat(dest, if_name); + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +ASIO_DECL +int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + using namespace std; // For memcpy and strcmp. + + if (af != AF_INET && af != AF_INET6) + { + ec = asio::error::address_family_not_supported; + return -1; + } + + union + { + socket_addr_type base; + sockaddr_storage_type storage; + sockaddr_in4_type v4; + sockaddr_in6_type v6; + } address; + int address_length = sizeof(sockaddr_storage_type); +#if defined(BOOST_NO_ANSI_APIS) + int num_wide_chars = strlen(src) + 1; + LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); + ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); + int result = error_wrapper(::WSAStringToAddressW( + wide_buffer, af, 0, &address.base, &address_length), ec); +#else + int result = error_wrapper(::WSAStringToAddressA( + const_cast(src), af, 0, &address.base, &address_length), ec); +#endif + + if (af == AF_INET) + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); + ec = asio::error_code(); + } + else if (strcmp(src, "255.255.255.255") == 0) + { + static_cast(dest)->s_addr = INADDR_NONE; + ec = asio::error_code(); + } + } + else // AF_INET6 + { + if (result != socket_error_retval) + { + memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); + if (scope_id) + *scope_id = address.v6.sin6_scope_id; + ec = asio::error_code(); + } + } + + // Windows may not set an error code on failure. + if (result == socket_error_retval && !ec) + ec = asio::error::invalid_argument; + + if (result != socket_error_retval) + ec = asio::error_code(); + + return result == socket_error_retval ? -1 : 1; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + int result = error_wrapper(::inet_pton(af, src, dest), ec); + if (result <= 0 && !ec) + ec = asio::error::invalid_argument; + if (result > 0 && af == AF_INET6 && scope_id) + { + using namespace std; // For strchr and atoi. + *scope_id = 0; + if (const char* if_name = strchr(src, '%')) + { + in6_addr_type* ipv6_address = static_cast(dest); + bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); + if (is_link_local) + *scope_id = if_nametoindex(if_name + 1); + if (*scope_id == 0) + *scope_id = atoi(if_name + 1); + } + } + return result; +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + +ASIO_DECL +int gethostname(char* name, int namelen, asio::error_code& ec) +{ + clear_last_error(); + int result = error_wrapper(::gethostname(name, namelen), ec); +#if defined(BOOST_WINDOWS) + if (result == 0) + ec = asio::error_code(); +#endif + return result; +} + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \ + || defined(__MACH__) && defined(__APPLE__) + +// The following functions are only needed for emulation of getaddrinfo and +// getnameinfo. + +inline asio::error_code translate_netdb_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case HOST_NOT_FOUND: + return asio::error::host_not_found; + case TRY_AGAIN: + return asio::error::host_not_found_try_again; + case NO_RECOVERY: + return asio::error::no_recovery; + case NO_DATA: + return asio::error::no_data; + default: + BOOST_ASSERT(false); + return asio::error::invalid_argument; + } +} + +inline hostent* gethostbyaddr(const char* addr, int length, int af, + hostent* result, char* buffer, int buflength, asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec); + if (!retval) + return 0; + ec = asio::error_code(); + *result = *retval; + return retval; +#elif defined(__sun) || defined(__QNX__) + int error = 0; + hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result, + buffer, buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyaddr( + addr, length, af, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, + buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline hostent* gethostbyname(const char* name, int af, struct hostent* result, + char* buffer, int buflength, int ai_flags, asio::error_code& ec) +{ + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + (void)(buffer); + (void)(buflength); + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = error_wrapper(::gethostbyname(name), ec); + if (!retval) + return 0; + ec = asio::error_code(); + *result = *retval; + return result; +#elif defined(__sun) || defined(__QNX__) + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + int error = 0; + hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, + buflength, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#elif defined(__MACH__) && defined(__APPLE__) + (void)(buffer); + (void)(buflength); + int error = 0; + hostent* retval = error_wrapper(::getipnodebyname( + name, af, ai_flags, &error), ec); + if (error) + ec = translate_netdb_error(error); + if (!retval) + return 0; + *result = *retval; + return retval; +#else + (void)(ai_flags); + if (af != AF_INET) + { + ec = asio::error::address_family_not_supported; + return 0; + } + hostent* retval = 0; + int error = 0; + error_wrapper(::gethostbyname_r(name, result, + buffer, buflength, &retval, &error), ec); + if (error) + ec = translate_netdb_error(error); + return retval; +#endif +} + +inline void freehostent(hostent* h) +{ +#if defined(__MACH__) && defined(__APPLE__) + if (h) + ::freehostent(h); +#else + (void)(h); +#endif +} + +// Emulation of getaddrinfo based on implementation in: +// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. + +struct gai_search +{ + const char* host; + int family; +}; + +inline int gai_nsearch(const char* host, + const addrinfo_type* hints, gai_search (&search)[2]) +{ + int search_count = 0; + if (host == 0 || host[0] == '\0') + { + if (hints->ai_flags & AI_PASSIVE) + { + // No host and AI_PASSIVE implies wildcard bind. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "0::0"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "0.0.0.0"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + else + { + // No host and not AI_PASSIVE means connect to local host. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = "localhost"; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = "localhost"; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + } + else + { + // Host is specified. + switch (hints->ai_family) + { + case AF_INET: + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + case AF_INET6: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + break; + case AF_UNSPEC: + search[search_count].host = host; + search[search_count].family = AF_INET6; + ++search_count; + search[search_count].host = host; + search[search_count].family = AF_INET; + ++search_count; + break; + default: + break; + } + } + return search_count; +} + +template +inline T* gai_alloc(std::size_t size = sizeof(T)) +{ + using namespace std; + T* p = static_cast(::operator new(size, std::nothrow)); + if (p) + memset(p, 0, size); + return p; +} + +inline void gai_free(void* p) +{ + ::operator delete(p); +} + +inline void gai_strcpy(char* target, const char* source, std::size_t max_size) +{ + using namespace std; +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) + strcpy_s(target, max_size, source); +#else + *target = 0; + strncat(target, source, max_size); +#endif +} + +enum { gai_clone_flag = 1 << 30 }; + +inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, + const void* addr, int family) +{ + using namespace std; + + addrinfo_type* ai = gai_alloc(); + if (ai == 0) + return EAI_MEMORY; + + ai->ai_next = 0; + **next = ai; + *next = &ai->ai_next; + + ai->ai_canonname = 0; + ai->ai_socktype = hints->ai_socktype; + if (ai->ai_socktype == 0) + ai->ai_flags |= gai_clone_flag; + ai->ai_protocol = hints->ai_protocol; + ai->ai_family = family; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = gai_alloc(); + if (sinptr == 0) + return EAI_MEMORY; + sinptr->sin_family = AF_INET; + memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); + ai->ai_addr = reinterpret_cast(sinptr); + ai->ai_addrlen = sizeof(sockaddr_in4_type); + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = gai_alloc(); + if (sin6ptr == 0) + return EAI_MEMORY; + sin6ptr->sin6_family = AF_INET6; + memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); + ai->ai_addr = reinterpret_cast(sin6ptr); + ai->ai_addrlen = sizeof(sockaddr_in6_type); + break; + } + default: + break; + } + + return 0; +} + +inline addrinfo_type* gai_clone(addrinfo_type* ai) +{ + using namespace std; + + addrinfo_type* new_ai = gai_alloc(); + if (new_ai == 0) + return new_ai; + + new_ai->ai_next = ai->ai_next; + ai->ai_next = new_ai; + + new_ai->ai_flags = 0; + new_ai->ai_family = ai->ai_family; + new_ai->ai_socktype = ai->ai_socktype; + new_ai->ai_protocol = ai->ai_protocol; + new_ai->ai_canonname = 0; + new_ai->ai_addrlen = ai->ai_addrlen; + new_ai->ai_addr = gai_alloc(ai->ai_addrlen); + memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); + + return new_ai; +} + +inline int gai_port(addrinfo_type* aihead, int port, int socktype) +{ + int num_found = 0; + + for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) + { + if (ai->ai_flags & gai_clone_flag) + { + if (ai->ai_socktype != 0) + { + ai = gai_clone(ai); + if (ai == 0) + return -1; + // ai now points to newly cloned entry. + } + } + else if (ai->ai_socktype != socktype) + { + // Ignore if mismatch on socket type. + continue; + } + + ai->ai_socktype = socktype; + + switch (ai->ai_family) + { + case AF_INET: + { + sockaddr_in4_type* sinptr = + reinterpret_cast(ai->ai_addr); + sinptr->sin_port = port; + ++num_found; + break; + } + case AF_INET6: + { + sockaddr_in6_type* sin6ptr = + reinterpret_cast(ai->ai_addr); + sin6ptr->sin6_port = port; + ++num_found; + break; + } + default: + break; + } + } + + return num_found; +} + +inline int gai_serv(addrinfo_type* aihead, + const addrinfo_type* hints, const char* serv) +{ + using namespace std; + + int num_found = 0; + + if ( +#if defined(AI_NUMERICSERV) + (hints->ai_flags & AI_NUMERICSERV) || +#endif + isdigit(static_cast(serv[0]))) + { + int port = htons(atoi(serv)); + if (hints->ai_socktype) + { + // Caller specifies socket type. + int rc = gai_port(aihead, port, hints->ai_socktype); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + else + { + // Caller does not specify socket type. + int rc = gai_port(aihead, port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + rc = gai_port(aihead, port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + else + { + // Try service name with TCP first, then UDP. + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) + { + servent* sptr = getservbyname(serv, "tcp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) + { + servent* sptr = getservbyname(serv, "udp"); + if (sptr != 0) + { + int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); + if (rc < 0) + return EAI_MEMORY; + num_found += rc; + } + } + } + + if (num_found == 0) + { + if (hints->ai_socktype == 0) + { + // All calls to getservbyname() failed. + return EAI_NONAME; + } + else + { + // Service not supported for socket type. + return EAI_SERVICE; + } + } + + return 0; +} + +inline int gai_echeck(const char* host, const char* service, + int flags, int family, int socktype, int protocol) +{ + (void)(flags); + (void)(protocol); + + // Host or service must be specified. + if (host == 0 || host[0] == '\0') + if (service == 0 || service[0] == '\0') + return EAI_NONAME; + + // Check combination of family and socket type. + switch (family) + { + case AF_UNSPEC: + break; + case AF_INET: + case AF_INET6: + if (service != 0 && service[0] != '\0') + if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return EAI_SOCKTYPE; + break; + default: + return EAI_FAMILY; + } + + return 0; +} + +inline void freeaddrinfo_emulation(addrinfo_type* aihead) +{ + addrinfo_type* ai = aihead; + while (ai) + { + gai_free(ai->ai_addr); + gai_free(ai->ai_canonname); + addrinfo_type* ainext = ai->ai_next; + gai_free(ai); + ai = ainext; + } +} + +inline int getaddrinfo_emulation(const char* host, const char* service, + const addrinfo_type* hintsp, addrinfo_type** result) +{ + // Set up linked list of addrinfo structures. + addrinfo_type* aihead = 0; + addrinfo_type** ainext = &aihead; + char* canon = 0; + + // Supply default hints if not specified by caller. + addrinfo_type hints = addrinfo_type(); + hints.ai_family = AF_UNSPEC; + if (hintsp) + hints = *hintsp; + + // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED + // and AI_ALL flags. +#if defined(AI_V4MAPPED) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_V4MAPPED; +#endif +#if defined(AI_ALL) + if (hints.ai_family != AF_INET6) + hints.ai_flags &= ~AI_ALL; +#endif + + // Basic error checking. + int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, + hints.ai_socktype, hints.ai_protocol); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + + gai_search search[2]; + int search_count = gai_nsearch(host, &hints, search); + for (gai_search* sptr = search; sptr < search + search_count; ++sptr) + { + // Check for IPv4 dotted decimal string. + in4_addr_type inaddr; + asio::error_code ec; + if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr, 0, ec) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET) + { + rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Check for IPv6 hex string. + in6_addr_type in6addr; + if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr, 0, ec) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return EAI_FAMILY; + } + if (sptr->family == AF_INET6) + { + rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + return rc; + } + } + continue; + } + + // Look up hostname. + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyname(sptr->host, + sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); + if (hptr == 0) + { + if (search_count == 2) + { + // Failure is OK if there are multiple searches. + continue; + } + freeaddrinfo_emulation(aihead); + gai_free(canon); + if (ec == asio::error::host_not_found) + return EAI_NONAME; + if (ec == asio::error::host_not_found_try_again) + return EAI_AGAIN; + if (ec == asio::error::no_recovery) + return EAI_FAIL; + if (ec == asio::error::no_data) + return EAI_NONAME; + return EAI_NONAME; + } + + // Check for address family mismatch if one was specified. + if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + + // Save canonical name first time. + if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] + && (hints.ai_flags & AI_CANONNAME) && canon == 0) + { + std::size_t canon_len = strlen(hptr->h_name) + 1; + canon = gai_alloc(canon_len); + if (canon == 0) + { + freeaddrinfo_emulation(aihead); + socket_ops::freehostent(hptr); + return EAI_MEMORY; + } + gai_strcpy(canon, hptr->h_name, canon_len); + } + + // Create an addrinfo structure for each returned address. + for (char** ap = hptr->h_addr_list; *ap; ++ap) + { + rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + gai_free(canon); + socket_ops::freehostent(hptr); + return EAI_FAMILY; + } + } + + socket_ops::freehostent(hptr); + } + + // Check if we found anything. + if (aihead == 0) + { + gai_free(canon); + return EAI_NONAME; + } + + // Return canonical name in first entry. + if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) + { + if (canon) + { + aihead->ai_canonname = canon; + canon = 0; + } + else + { + std::size_t canonname_len = strlen(search[0].host) + 1; + aihead->ai_canonname = gai_alloc(canonname_len); + if (aihead->ai_canonname == 0) + { + freeaddrinfo_emulation(aihead); + return EAI_MEMORY; + } + gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); + } + } + gai_free(canon); + + // Process the service name. + if (service != 0 && service[0] != '\0') + { + rc = gai_serv(aihead, &hints, service); + if (rc != 0) + { + freeaddrinfo_emulation(aihead); + return rc; + } + } + + // Return result to caller. + *result = aihead; + return 0; +} + +inline asio::error_code getnameinfo_emulation( + const socket_addr_type* sa, std::size_t salen, char* host, + std::size_t hostlen, char* serv, std::size_t servlen, int flags, + asio::error_code& ec) +{ + using namespace std; + + const char* addr; + size_t addr_len; + unsigned short port; + switch (sa->sa_family) + { + case AF_INET: + if (salen != sizeof(sockaddr_in4_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin_addr); + addr_len = sizeof(in4_addr_type); + port = reinterpret_cast(sa)->sin_port; + break; + case AF_INET6: + if (salen != sizeof(sockaddr_in6_type)) + { + return ec = asio::error::invalid_argument; + } + addr = reinterpret_cast( + &reinterpret_cast(sa)->sin6_addr); + addr_len = sizeof(in6_addr_type); + port = reinterpret_cast(sa)->sin6_port; + break; + default: + return ec = asio::error::address_family_not_supported; + } + + if (host && hostlen > 0) + { + if (flags & NI_NUMERICHOST) + { + if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + else + { + hostent hent; + char hbuf[8192] = ""; + hostent* hptr = socket_ops::gethostbyaddr(addr, + static_cast(addr_len), sa->sa_family, + &hent, hbuf, sizeof(hbuf), ec); + if (hptr && hptr->h_name && hptr->h_name[0] != '\0') + { + if (flags & NI_NOFQDN) + { + char* dot = strchr(hptr->h_name, '.'); + if (dot) + { + *dot = 0; + } + } + gai_strcpy(host, hptr->h_name, hostlen); + socket_ops::freehostent(hptr); + } + else + { + socket_ops::freehostent(hptr); + if (flags & NI_NAMEREQD) + { + return ec = asio::error::host_not_found; + } + if (socket_ops::inet_ntop(sa->sa_family, + addr, host, hostlen, 0, ec) == 0) + { + return ec; + } + } + } + } + + if (serv && servlen > 0) + { + if (flags & NI_NUMERICSERV) + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else + sprintf(serv, "%u", ntohs(port)); +#endif + } + else + { +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) \ + && !defined(ASIO_DISABLE_THREADS) + static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + ::pthread_mutex_lock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + // && !defined(ASIO_DISABLE_THREADS) + servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); + if (sptr && sptr->s_name && sptr->s_name[0] != '\0') + { + gai_strcpy(serv, sptr->s_name, servlen); + } + else + { + if (servlen < 6) + { + return ec = asio::error::no_buffer_space; + } +#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) + sprintf_s(serv, servlen, "%u", ntohs(port)); +#else + sprintf(serv, "%u", ntohs(port)); +#endif + } +#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) \ + && !defined(ASIO_DISABLE_THREADS) + ::pthread_mutex_unlock(&mutex); +#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) + // && !defined(ASIO_DISABLE_THREADS) + } + } + + ec = asio::error_code(); + return ec; +} + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + // || defined(__MACH__) && defined(__APPLE__) + +inline asio::error_code translate_addrinfo_error(int error) +{ + switch (error) + { + case 0: + return asio::error_code(); + case EAI_AGAIN: + return asio::error::host_not_found_try_again; + case EAI_BADFLAGS: + return asio::error::invalid_argument; + case EAI_FAIL: + return asio::error::no_recovery; + case EAI_FAMILY: + return asio::error::address_family_not_supported; + case EAI_MEMORY: + return asio::error::no_memory; + case EAI_NONAME: +#if defined(EAI_ADDRFAMILY) + case EAI_ADDRFAMILY: +#endif +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) + case EAI_NODATA: +#endif + return asio::error::host_not_found; + case EAI_SERVICE: + return asio::error::service_not_found; + case EAI_SOCKTYPE: + return asio::error::socket_type_not_supported; + default: // Possibly the non-portable EAI_SYSTEM. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + return asio::error_code( + WSAGetLastError(), asio::error::get_system_category()); +#else + return asio::error_code( + errno, asio::error::get_system_category()); +#endif + } +} + +ASIO_DECL +asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec) +{ + host = (host && *host) ? host : 0; + service = (service && *service) ? service : 0; + clear_last_error(); +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) + // Building for Windows XP, Windows Server 2003, or later. + int error = ::getaddrinfo(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gai_t)(const char*, + const char*, const addrinfo_type*, addrinfo_type**); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) + { + int error = gai(host, service, &hints, result); + return ec = translate_addrinfo_error(error); + } + } + int error = getaddrinfo_emulation(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + int error = getaddrinfo_emulation(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +#else + int error = ::getaddrinfo(host, service, &hints, result); + return ec = translate_addrinfo_error(error); +#endif +} + +ASIO_DECL +asio::error_code background_getaddrinfo( + const weak_cancel_token_type& cancel_token, const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec) +{ + if (cancel_token.expired()) + ec = asio::error::operation_aborted; + else + socket_ops::getaddrinfo(host, service, hints, result, ec); + return ec; +} + +ASIO_DECL +void freeaddrinfo(addrinfo_type* ai) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) + // Building for Windows XP, Windows Server 2003, or later. + ::freeaddrinfo(ai); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *fai_t)(addrinfo_type*); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) + { + fai(ai); + return; + } + } + freeaddrinfo_emulation(ai); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + freeaddrinfo_emulation(ai); +#else + ::freeaddrinfo(ai); +#endif +} + +ASIO_DECL +asio::error_code getnameinfo(const socket_addr_type* addr, + std::size_t addrlen, char* host, std::size_t hostlen, + char* serv, std::size_t servlen, int flags, asio::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) + // Building for Windows XP, Windows Server 2003, or later. + clear_last_error(); + int error = ::getnameinfo(addr, static_cast(addrlen), + host, static_cast(hostlen), + serv, static_cast(servlen), flags); + return ec = translate_addrinfo_error(error); +# else + // Building for Windows 2000 or earlier. + typedef int (WSAAPI *gni_t)(const socket_addr_type*, + int, char*, DWORD, char*, DWORD, int); + if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) + { + if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) + { + clear_last_error(); + int error = gni(addr, static_cast(addrlen), + host, static_cast(hostlen), + serv, static_cast(servlen), flags); + return ec = translate_addrinfo_error(error); + } + } + clear_last_error(); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +# endif +#elif defined(__MACH__) && defined(__APPLE__) + using namespace std; // For memcpy. + sockaddr_storage_type tmp_addr; + memcpy(&tmp_addr, addr, addrlen); + tmp_addr.ss_len = addrlen; + addr = reinterpret_cast(&tmp_addr); + clear_last_error(); + return getnameinfo_emulation(addr, addrlen, + host, hostlen, serv, servlen, flags, ec); +#else + clear_last_error(); + int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); + return ec = translate_addrinfo_error(error); +#endif +} + +ASIO_DECL +asio::error_code sync_getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec) +{ + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; + socket_ops::getnameinfo(addr, addrlen, host, + hostlen, serv, servlen, flags, ec); + if (ec) + { + socket_ops::getnameinfo(addr, addrlen, host, hostlen, + serv, servlen, flags | NI_NUMERICSERV, ec); + } + + return ec; +} + +ASIO_DECL +asio::error_code background_getnameinfo( + const weak_cancel_token_type& cancel_token, + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec) +{ + if (cancel_token.expired()) + { + ec = asio::error::operation_aborted; + } + else + { + // First try resolving with the service name. If that fails try resolving + // but allow the service to be returned as a number. + int flags = (sock_type == SOCK_DGRAM) ? NI_DGRAM : 0; + socket_ops::getnameinfo(addr, addrlen, host, + hostlen, serv, servlen, flags, ec); + if (ec) + { + socket_ops::getnameinfo(addr, addrlen, host, hostlen, + serv, servlen, flags | NI_NUMERICSERV, ec); + } + } + + return ec; +} + +ASIO_DECL +u_long_type network_to_host_long(u_long_type value) +{ + return ntohl(value); +} + +ASIO_DECL +u_long_type host_to_network_long(u_long_type value) +{ + return htonl(value); +} + +ASIO_DECL +u_short_type network_to_host_short(u_short_type value) +{ + return ntohs(value); +} + +ASIO_DECL +u_short_type host_to_network_short(u_short_type value) +{ + return htons(value); +} + +} // namespace socket_ops +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_SOCKET_OPS_IPP diff --git a/ext/asio/asio/detail/impl/socket_select_interrupter.ipp b/ext/asio/asio/detail/impl/socket_select_interrupter.ipp new file mode 100644 index 0000000000..181c543754 --- /dev/null +++ b/ext/asio/asio/detail/impl/socket_select_interrupter.ipp @@ -0,0 +1,151 @@ +// +// detail/impl/socket_select_interrupter.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP +#define ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) + +#include +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_select_interrupter.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +socket_select_interrupter::socket_select_interrupter() +{ + asio::error_code ec; + socket_holder acceptor(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (acceptor.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + int opt = 1; + socket_ops::state_type acceptor_state = 0; + socket_ops::setsockopt(acceptor.get(), acceptor_state, + SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); + + using namespace std; // For memset. + sockaddr_in4_type addr; + std::size_t addr_len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 0; + if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, + &addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + // Some broken firewalls on Windows will intermittently cause getsockname to + // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We + // explicitly specify the target address here to work around this problem. + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + if (socket_ops::listen(acceptor.get(), + SOMAXCONN, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + socket_holder client(socket_ops::socket( + AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); + if (client.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, + addr_len, ec) == socket_error_retval) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); + if (server.get() == invalid_socket) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + ioctl_arg_type non_blocking = 1; + socket_ops::state_type client_state = 0; + if (socket_ops::ioctl(client.get(), client_state, + FIONBIO, &non_blocking, ec)) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + opt = 1; + socket_ops::setsockopt(client.get(), client_state, + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + non_blocking = 1; + socket_ops::state_type server_state = 0; + if (socket_ops::ioctl(server.get(), server_state, + FIONBIO, &non_blocking, ec)) + asio::detail::throw_error(ec, "socket_select_interrupter"); + + opt = 1; + socket_ops::setsockopt(server.get(), server_state, + IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); + + read_descriptor_ = server.release(); + write_descriptor_ = client.release(); +} + +socket_select_interrupter::~socket_select_interrupter() +{ + asio::error_code ec; + socket_ops::state_type state = socket_ops::internal_non_blocking; + if (read_descriptor_ != invalid_socket) + socket_ops::close(read_descriptor_, state, true, ec); + if (write_descriptor_ != invalid_socket) + socket_ops::close(write_descriptor_, state, true, ec); +} + +void socket_select_interrupter::interrupt() +{ + char byte = 0; + socket_ops::buf b; + socket_ops::init_buf(b, &byte, 1); + asio::error_code ec; + socket_ops::send(write_descriptor_, &b, 1, 0, ec); +} + +bool socket_select_interrupter::reset() +{ + char data[1024]; + socket_ops::buf b; + socket_ops::init_buf(b, data, sizeof(data)); + asio::error_code ec; + int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + bool was_interrupted = (bytes_read > 0); + while (bytes_read == sizeof(data)) + bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); + return was_interrupted; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + +#endif // ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP diff --git a/ext/asio/asio/detail/impl/strand_service.hpp b/ext/asio/asio/detail/impl/strand_service.hpp new file mode 100644 index 0000000000..81f438a988 --- /dev/null +++ b/ext/asio/asio/detail/impl/strand_service.hpp @@ -0,0 +1,140 @@ +// +// detail/impl/strand_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP +#define ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/call_stack.hpp" +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +inline strand_service::strand_impl::strand_impl() + : operation(&strand_service::do_complete), + count_(0) +{ +} + +struct strand_service::on_dispatch_exit +{ + io_service_impl* io_service_; + strand_impl* impl_; + + ~on_dispatch_exit() + { + impl_->mutex_.lock(); + bool more_handlers = (--impl_->count_ > 0); + impl_->mutex_.unlock(); + + if (more_handlers) + io_service_->post_immediate_completion(impl_); + } +}; + +inline void strand_service::destroy(strand_service::implementation_type& impl) +{ + impl = 0; +} + +template +void strand_service::dispatch(strand_service::implementation_type& impl, + Handler handler) +{ + // If we are already in the strand then the handler can run immediately. + if (call_stack::contains(impl)) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + return; + } + + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); + + // If we are running inside the io_service, and no other handler is queued + // or running, then the handler can run immediately. + bool can_dispatch = call_stack::contains(&io_service_); + impl->mutex_.lock(); + bool first = (++impl->count_ == 1); + if (can_dispatch && first) + { + // Immediate invocation is allowed. + impl->mutex_.unlock(); + + // Memory must be releaesed before any upcall is made. + p.reset(); + + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_dispatch_exit on_exit = { &io_service_, impl }; + (void)on_exit; + + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + return; + } + + // Immediate invocation is not allowed, so enqueue for later. + impl->queue_.push(p.p); + impl->mutex_.unlock(); + p.v = p.p = 0; + + // The first handler to be enqueued is responsible for scheduling the + // strand. + if (first) + io_service_.post_immediate_completion(impl); +} + +// Request the io_service to invoke the given handler and return immediately. +template +void strand_service::post(strand_service::implementation_type& impl, + Handler handler) +{ + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); + + // Add the handler to the queue. + impl->mutex_.lock(); + bool first = (++impl->count_ == 1); + impl->queue_.push(p.p); + impl->mutex_.unlock(); + p.v = p.p = 0; + + // The first handler to be enqueue is responsible for scheduling the strand. + if (first) + io_service_.post_immediate_completion(impl); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_HPP diff --git a/ext/asio/asio/detail/impl/strand_service.ipp b/ext/asio/asio/detail/impl/strand_service.ipp new file mode 100644 index 0000000000..84b806a553 --- /dev/null +++ b/ext/asio/asio/detail/impl/strand_service.ipp @@ -0,0 +1,106 @@ +// +// detail/impl/strand_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP +#define ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/call_stack.hpp" +#include "asio/detail/strand_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct strand_service::on_do_complete_exit +{ + io_service_impl* owner_; + strand_impl* impl_; + + ~on_do_complete_exit() + { + impl_->mutex_.lock(); + bool more_handlers = (--impl_->count_ > 0); + impl_->mutex_.unlock(); + + if (more_handlers) + owner_->post_immediate_completion(impl_); + } +}; + +strand_service::strand_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + io_service_(asio::use_service(io_service)), + mutex_(), + salt_(0) +{ +} + +void strand_service::shutdown_service() +{ + op_queue ops; + + asio::detail::mutex::scoped_lock lock(mutex_); + + for (std::size_t i = 0; i < num_implementations; ++i) + if (strand_impl* impl = implementations_[i].get()) + ops.push(impl->queue_); +} + +void strand_service::construct(strand_service::implementation_type& impl) +{ + std::size_t salt = salt_++; + std::size_t index = reinterpret_cast(&impl); + index += (reinterpret_cast(&impl) >> 3); + index ^= salt + 0x9e3779b9 + (index << 6) + (index >> 2); + index = index % num_implementations; + + asio::detail::mutex::scoped_lock lock(mutex_); + + if (!implementations_[index]) + implementations_[index].reset(new strand_impl); + impl = implementations_[index].get(); +} + +void strand_service::do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) +{ + if (owner) + { + strand_impl* impl = static_cast(base); + + // Get the next handler to be executed. + impl->mutex_.lock(); + operation* o = impl->queue_.front(); + impl->queue_.pop(); + impl->mutex_.unlock(); + + // Indicate that this strand is executing on the current thread. + call_stack::context ctx(impl); + + // Ensure the next handler, if any, is scheduled on block exit. + on_do_complete_exit on_exit = { owner, impl }; + (void)on_exit; + + o->complete(*owner); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP diff --git a/ext/asio/asio/detail/impl/task_io_service.hpp b/ext/asio/asio/detail/impl/task_io_service.hpp new file mode 100644 index 0000000000..20ffd61c06 --- /dev/null +++ b/ext/asio/asio/detail/impl/task_io_service.hpp @@ -0,0 +1,60 @@ +// +// detail/impl/task_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP +#define ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/call_stack.hpp" +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void task_io_service::dispatch(Handler handler) +{ + if (call_stack::contains(this)) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + else + post(handler); +} + +template +void task_io_service::post(Handler handler) +{ + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); + + post_immediate_completion(p.p); + p.v = p.p = 0; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_TASK_IO_SERVICE_HPP diff --git a/ext/asio/asio/detail/impl/task_io_service.ipp b/ext/asio/asio/detail/impl/task_io_service.ipp new file mode 100644 index 0000000000..12a22be8cc --- /dev/null +++ b/ext/asio/asio/detail/impl/task_io_service.ipp @@ -0,0 +1,354 @@ +// +// detail/impl/task_io_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP +#define ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) + +#include +#include "asio/detail/call_stack.hpp" +#include "asio/detail/event.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/task_io_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct task_io_service::task_cleanup +{ + ~task_cleanup() + { + // Enqueue the completed operations and reinsert the task at the end of + // the operation queue. + lock_->lock(); + task_io_service_->task_interrupted_ = true; + task_io_service_->op_queue_.push(*ops_); + task_io_service_->op_queue_.push(&task_io_service_->task_operation_); + } + + task_io_service* task_io_service_; + mutex::scoped_lock* lock_; + op_queue* ops_; +}; + +struct task_io_service::work_finished_on_block_exit +{ + ~work_finished_on_block_exit() + { + task_io_service_->work_finished(); + } + + task_io_service* task_io_service_; +}; + +struct task_io_service::idle_thread_info +{ + event wakeup_event; + idle_thread_info* next; +}; + +task_io_service::task_io_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + mutex_(), + task_(0), + task_interrupted_(true), + outstanding_work_(0), + stopped_(false), + shutdown_(false), + first_idle_thread_(0) +{ +} + +void task_io_service::init(std::size_t /*concurrency_hint*/) +{ +} + +void task_io_service::shutdown_service() +{ + mutex::scoped_lock lock(mutex_); + shutdown_ = true; + lock.unlock(); + + // Destroy handler objects. + while (!op_queue_.empty()) + { + operation* o = op_queue_.front(); + op_queue_.pop(); + if (o != &task_operation_) + o->destroy(); + } + + // Reset to initial state. + task_ = 0; +} + +void task_io_service::init_task() +{ + mutex::scoped_lock lock(mutex_); + if (!shutdown_ && !task_) + { + task_ = &use_service(this->get_io_service()); + op_queue_.push(&task_operation_); + wake_one_thread_and_unlock(lock); + } +} + +std::size_t task_io_service::run(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.next = 0; + + mutex::scoped_lock lock(mutex_); + + std::size_t n = 0; + for (; do_one(lock, &this_idle_thread); lock.lock()) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +std::size_t task_io_service::run_one(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + call_stack::context ctx(this); + + idle_thread_info this_idle_thread; + this_idle_thread.next = 0; + + mutex::scoped_lock lock(mutex_); + + return do_one(lock, &this_idle_thread); +} + +std::size_t task_io_service::poll(asio::error_code& ec) +{ + if (outstanding_work_ == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + mutex::scoped_lock lock(mutex_); + + std::size_t n = 0; + for (; do_one(lock, 0); lock.lock()) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +std::size_t task_io_service::poll_one(asio::error_code& ec) +{ + ec = asio::error_code(); + if (outstanding_work_ == 0) + { + stop(); + return 0; + } + + call_stack::context ctx(this); + + mutex::scoped_lock lock(mutex_); + + return do_one(lock, 0); +} + +void task_io_service::stop() +{ + mutex::scoped_lock lock(mutex_); + stop_all_threads(lock); +} + +void task_io_service::reset() +{ + mutex::scoped_lock lock(mutex_); + stopped_ = false; +} + +void task_io_service::post_immediate_completion(task_io_service::operation* op) +{ + work_started(); + post_deferred_completion(op); +} + +void task_io_service::post_deferred_completion(task_io_service::operation* op) +{ + mutex::scoped_lock lock(mutex_); + op_queue_.push(op); + wake_one_thread_and_unlock(lock); +} + +void task_io_service::post_deferred_completions( + op_queue& ops) +{ + if (!ops.empty()) + { + mutex::scoped_lock lock(mutex_); + op_queue_.push(ops); + wake_one_thread_and_unlock(lock); + } +} + +std::size_t task_io_service::do_one(mutex::scoped_lock& lock, + task_io_service::idle_thread_info* this_idle_thread) +{ + bool polling = !this_idle_thread; + bool task_has_run = false; + while (!stopped_) + { + if (!op_queue_.empty()) + { + // Prepare to execute first handler from queue. + operation* o = op_queue_.front(); + op_queue_.pop(); + bool more_handlers = (!op_queue_.empty()); + + if (o == &task_operation_) + { + task_interrupted_ = more_handlers || polling; + + // If the task has already run and we're polling then we're done. + if (task_has_run && polling) + { + task_interrupted_ = true; + op_queue_.push(&task_operation_); + return 0; + } + task_has_run = true; + + if (!more_handlers || !wake_one_idle_thread_and_unlock(lock)) + lock.unlock(); + + op_queue completed_ops; + task_cleanup c = { this, &lock, &completed_ops }; + (void)c; + + // Run the task. May throw an exception. Only block if the operation + // queue is empty and we're not polling, otherwise we want to return + // as soon as possible. + task_->run(!more_handlers && !polling, completed_ops); + } + else + { + if (more_handlers) + wake_one_thread_and_unlock(lock); + else + lock.unlock(); + + // Ensure the count of outstanding work is decremented on block exit. + work_finished_on_block_exit on_exit = { this }; + (void)on_exit; + + // Complete the operation. May throw an exception. + o->complete(*this); // deletes the operation object + + return 1; + } + } + else if (this_idle_thread) + { + // Nothing to run right now, so just wait for work to do. + this_idle_thread->next = first_idle_thread_; + first_idle_thread_ = this_idle_thread; + this_idle_thread->wakeup_event.clear(lock); + this_idle_thread->wakeup_event.wait(lock); + } + else + { + return 0; + } + } + + return 0; +} + +void task_io_service::stop_all_threads( + mutex::scoped_lock& lock) +{ + stopped_ = true; + + while (first_idle_thread_) + { + idle_thread_info* idle_thread = first_idle_thread_; + first_idle_thread_ = idle_thread->next; + idle_thread->next = 0; + idle_thread->wakeup_event.signal(lock); + } + + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } +} + +bool task_io_service::wake_one_idle_thread_and_unlock( + mutex::scoped_lock& lock) +{ + if (first_idle_thread_) + { + idle_thread_info* idle_thread = first_idle_thread_; + first_idle_thread_ = idle_thread->next; + idle_thread->next = 0; + idle_thread->wakeup_event.signal_and_unlock(lock); + return true; + } + return false; +} + +void task_io_service::wake_one_thread_and_unlock( + mutex::scoped_lock& lock) +{ + if (!wake_one_idle_thread_and_unlock(lock)) + { + if (!task_interrupted_ && task_) + { + task_interrupted_ = true; + task_->interrupt(); + } + lock.unlock(); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_TASK_IO_SERVICE_IPP diff --git a/ext/asio/asio/detail/impl/throw_error.ipp b/ext/asio/asio/detail/impl/throw_error.ipp new file mode 100644 index 0000000000..a04fbf4ef3 --- /dev/null +++ b/ext/asio/asio/detail/impl/throw_error.ipp @@ -0,0 +1,47 @@ +// +// detail/impl/throw_error.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_THROW_ERROR_IPP +#define ASIO_DETAIL_IMPL_THROW_ERROR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/throw_error.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +ASIO_DECL +void do_throw_error(const asio::error_code& err) +{ + asio::system_error e(err); + boost::throw_exception(e); +} + +ASIO_DECL +void do_throw_error(const asio::error_code& err, const char* location) +{ + asio::system_error e(err, location); + boost::throw_exception(e); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_THROW_ERROR_IPP diff --git a/ext/asio/asio/detail/impl/timer_queue.ipp b/ext/asio/asio/detail/impl/timer_queue.ipp new file mode 100644 index 0000000000..63fbd7e90a --- /dev/null +++ b/ext/asio/asio/detail/impl/timer_queue.ipp @@ -0,0 +1,85 @@ +// +// detail/impl/timer_queue.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP +#define ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HEADER_ONLY) + +#include "asio/detail/timer_queue.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +timer_queue >::timer_queue() +{ +} + +timer_queue >::~timer_queue() +{ +} + +bool timer_queue >::enqueue_timer( + const time_type& time, per_timer_data& timer, timer_op* op) +{ + return impl_.enqueue_timer(time, timer, op); +} + +bool timer_queue >::empty() const +{ + return impl_.empty(); +} + +long timer_queue >::wait_duration_msec( + long max_duration) const +{ + return impl_.wait_duration_msec(max_duration); +} + +long timer_queue >::wait_duration_usec( + long max_duration) const +{ + return impl_.wait_duration_usec(max_duration); +} + +void timer_queue >::get_ready_timers( + op_queue& ops) +{ + impl_.get_ready_timers(ops); +} + +void timer_queue >::get_all_timers( + op_queue& ops) +{ + impl_.get_all_timers(ops); +} + +std::size_t timer_queue >::cancel_timer( + per_timer_data& timer, op_queue& ops) +{ + return impl_.cancel_timer(timer, ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_IPP diff --git a/ext/asio/asio/detail/impl/timer_queue_set.ipp b/ext/asio/asio/detail/impl/timer_queue_set.ipp new file mode 100644 index 0000000000..81ab5a9a3c --- /dev/null +++ b/ext/asio/asio/detail/impl/timer_queue_set.ipp @@ -0,0 +1,101 @@ +// +// detail/impl/timer_queue_set.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP +#define ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/timer_queue_set.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +timer_queue_set::timer_queue_set() + : first_(0) +{ +} + +void timer_queue_set::insert(timer_queue_base* q) +{ + q->next_ = first_; + first_ = q; +} + +void timer_queue_set::erase(timer_queue_base* q) +{ + if (first_) + { + if (q == first_) + { + first_ = q->next_; + q->next_ = 0; + return; + } + + for (timer_queue_base* p = first_; p->next_; p = p->next_) + { + if (p->next_ == q) + { + p->next_ = q->next_; + q->next_ = 0; + return; + } + } + } +} + +bool timer_queue_set::all_empty() const +{ + for (timer_queue_base* p = first_; p; p = p->next_) + if (!p->empty()) + return false; + return true; +} + +long timer_queue_set::wait_duration_msec(long max_duration) const +{ + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_msec(min_duration); + return min_duration; +} + +long timer_queue_set::wait_duration_usec(long max_duration) const +{ + long min_duration = max_duration; + for (timer_queue_base* p = first_; p; p = p->next_) + min_duration = p->wait_duration_usec(min_duration); + return min_duration; +} + +void timer_queue_set::get_ready_timers(op_queue& ops) +{ + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_ready_timers(ops); +} + +void timer_queue_set::get_all_timers(op_queue& ops) +{ + for (timer_queue_base* p = first_; p; p = p->next_) + p->get_all_timers(ops); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_IMPL_TIMER_QUEUE_SET_IPP diff --git a/ext/asio/asio/detail/impl/win_event.ipp b/ext/asio/asio/detail/impl/win_event.ipp new file mode 100644 index 0000000000..2b72f6ecda --- /dev/null +++ b/ext/asio/asio/detail/impl/win_event.ipp @@ -0,0 +1,50 @@ +// +// detail/win_event.ipp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_EVENT_IPP +#define ASIO_DETAIL_IMPL_WIN_EVENT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_event.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_event::win_event() + : event_(::CreateEvent(0, true, false, 0)) +{ + if (!event_) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "event"); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_EVENT_IPP diff --git a/ext/asio/asio/detail/impl/win_iocp_handle_service.ipp b/ext/asio/asio/detail/impl/win_iocp_handle_service.ipp new file mode 100644 index 0000000000..3b30b1d5df --- /dev/null +++ b/ext/asio/asio/detail/impl/win_iocp_handle_service.ipp @@ -0,0 +1,452 @@ +// +// detail/impl/win_iocp_handle_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/win_iocp_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_handle_service::overlapped_wrapper + : public OVERLAPPED +{ +public: + explicit overlapped_wrapper(asio::error_code& ec) + { + Internal = 0; + InternalHigh = 0; + Offset = 0; + OffsetHigh = 0; + + // Create a non-signalled manual-reset event, for GetOverlappedResult. + hEvent = ::CreateEvent(0, TRUE, FALSE, 0); + if (hEvent) + { + // As documented in GetQueuedCompletionStatus, setting the low order + // bit of this event prevents our synchronous writes from being treated + // as completion port events. + *reinterpret_cast(&hEvent) |= 1; + } + else + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + + ~overlapped_wrapper() + { + if (hEvent) + { + ::CloseHandle(hEvent); + } + } +}; + +win_iocp_handle_service::win_iocp_handle_service( + asio::io_service& io_service) + : iocp_service_(asio::use_service(io_service)), + mutex_(), + impl_list_(0) +{ +} + +void win_iocp_handle_service::shutdown_service() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + implementation_type* impl = impl_list_; + while (impl) + { + close_for_destruction(*impl); + impl = impl->next_; + } +} + +void win_iocp_handle_service::construct( + win_iocp_handle_service::implementation_type& impl) +{ + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_handle_service::destroy( + win_iocp_handle_service::implementation_type& impl) +{ + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code win_iocp_handle_service::assign( + win_iocp_handle_service::implementation_type& impl, + const native_type& native_handle, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + if (iocp_service_.register_handle(native_handle, ec)) + return ec; + + impl.handle_ = native_handle; + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_handle_service::close( + win_iocp_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + if (!::CloseHandle(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_handle_service::cancel( + win_iocp_handle_service::implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + } + else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; + if (!cancel_io_ex(impl.handle_, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + if (!::CancelIo(impl.handle_)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } + + return ec; +} + +size_t win_iocp_handle_service::do_write( + win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, + const asio::const_buffer& buffer, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to write 0 bytes on a handle is a no-op. + if (asio::buffer_size(buffer) == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Write the data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, + asio::buffer_cast(buffer), + static_cast(asio::buffer_size(buffer)), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING) + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; +} + +void win_iocp_handle_service::start_write_op( + win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, + const asio::const_buffer& buffer, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (asio::buffer_size(buffer) == 0) + { + // A request to write 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::WriteFile(impl.handle_, + asio::buffer_cast(buffer), + static_cast(asio::buffer_size(buffer)), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } +} + +size_t win_iocp_handle_service::do_read( + win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, + const asio::mutable_buffer& buffer, asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return 0; + } + + // A request to read 0 bytes on a stream handle is a no-op. + if (asio::buffer_size(buffer) == 0) + { + ec = asio::error_code(); + return 0; + } + + overlapped_wrapper overlapped(ec); + if (ec) + { + return 0; + } + + // Read some data. + overlapped.Offset = offset & 0xFFFFFFFF; + overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, + asio::buffer_cast(buffer), + static_cast(asio::buffer_size(buffer)), 0, &overlapped); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) + { + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return 0; + } + } + + // Wait for the operation to complete. + DWORD bytes_transferred = 0; + ok = ::GetOverlappedResult(impl.handle_, + &overlapped, &bytes_transferred, TRUE); + if (!ok) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_HANDLE_EOF) + { + ec = asio::error::eof; + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + return 0; + } + + ec = asio::error_code(); + return bytes_transferred; +} + +void win_iocp_handle_service::start_read_op( + win_iocp_handle_service::implementation_type& impl, boost::uint64_t offset, + const asio::mutable_buffer& buffer, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + { + iocp_service_.on_completion(op, asio::error::bad_descriptor); + } + else if (asio::buffer_size(buffer) == 0) + { + // A request to read 0 bytes on a handle is a no-op. + iocp_service_.on_completion(op); + } + else + { + DWORD bytes_transferred = 0; + op->Offset = offset & 0xFFFFFFFF; + op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; + BOOL ok = ::ReadFile(impl.handle_, + asio::buffer_cast(buffer), + static_cast(asio::buffer_size(buffer)), + &bytes_transferred, op); + DWORD last_error = ::GetLastError(); + if (!ok && last_error != ERROR_IO_PENDING + && last_error != ERROR_MORE_DATA) + { + iocp_service_.on_completion(op, last_error, bytes_transferred); + } + else + { + iocp_service_.on_pending(op); + } + } +} + +void win_iocp_handle_service::update_cancellation_thread_id( + win_iocp_handle_service::implementation_type& impl) +{ + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +} + +void win_iocp_handle_service::close_for_destruction(implementation_type& impl) +{ + if (is_open(impl)) + { + ::CloseHandle(impl.handle_); + impl.handle_ = INVALID_HANDLE_VALUE; + impl.safe_cancellation_thread_id_ = 0; + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_HANDLE_SERVICE_IPP diff --git a/ext/asio/asio/detail/impl/win_iocp_io_service.hpp b/ext/asio/asio/detail/impl/win_iocp_io_service.hpp new file mode 100644 index 0000000000..ab235471e8 --- /dev/null +++ b/ext/asio/asio/detail/impl/win_iocp_io_service.hpp @@ -0,0 +1,115 @@ +// +// detail/impl/win_iocp_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/call_stack.hpp" +#include "asio/detail/completion_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +void win_iocp_io_service::dispatch(Handler handler) +{ + if (call_stack::contains(this)) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + else + post(handler); +} + +template +void win_iocp_io_service::post(Handler handler) +{ + // Allocate and construct an operation to wrap the handler. + typedef completion_handler op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); + + post_immediate_completion(p.p); + p.v = p.p = 0; +} + +template +void win_iocp_io_service::add_timer_queue( + timer_queue& queue) +{ + do_add_timer_queue(queue); +} + +template +void win_iocp_io_service::remove_timer_queue( + timer_queue& queue) +{ + do_remove_timer_queue(queue); +} + +template +void win_iocp_io_service::schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op) +{ + // If the service has been shut down we silently discard the timer. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + { + post_immediate_completion(op); + return; + } + + mutex::scoped_lock lock(dispatch_mutex_); + + bool earliest = queue.enqueue_timer(time, timer, op); + work_started(); + if (earliest) + update_timeout(); +} + +template +std::size_t win_iocp_io_service::cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer) +{ + // If the service has been shut down we silently ignore the cancellation. + if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) + return 0; + + mutex::scoped_lock lock(dispatch_mutex_); + op_queue ops; + std::size_t n = queue.cancel_timer(timer, ops); + post_deferred_completions(ops); + return n; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_HPP diff --git a/ext/asio/asio/detail/impl/win_iocp_io_service.ipp b/ext/asio/asio/detail/impl/win_iocp_io_service.ipp new file mode 100644 index 0000000000..890e32f534 --- /dev/null +++ b/ext/asio/asio/detail/impl/win_iocp_io_service.ipp @@ -0,0 +1,496 @@ +// +// detail/impl/win_iocp_io_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_iocp_io_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +struct win_iocp_io_service::work_finished_on_block_exit +{ + ~work_finished_on_block_exit() + { + io_service_->work_finished(); + } + + win_iocp_io_service* io_service_; +}; + +struct win_iocp_io_service::timer_thread_function +{ + void operator()() + { + while (::InterlockedExchangeAdd(&io_service_->shutdown_, 0) == 0) + { + if (::WaitForSingleObject(io_service_->waitable_timer_.handle, + INFINITE) == WAIT_OBJECT_0) + { + ::InterlockedExchange(&io_service_->dispatch_required_, 1); + ::PostQueuedCompletionStatus(io_service_->iocp_.handle, + 0, wake_for_dispatch, 0); + } + } + } + + win_iocp_io_service* io_service_; +}; + +win_iocp_io_service::win_iocp_io_service(asio::io_service& io_service) + : asio::detail::service_base(io_service), + iocp_(), + outstanding_work_(0), + stopped_(0), + shutdown_(0), + dispatch_required_(0) +{ +} + +void win_iocp_io_service::init(size_t concurrency_hint) +{ + iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, + static_cast((std::min)(concurrency_hint, DWORD(~0)))); + if (!iocp_.handle) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "iocp"); + } +} + +void win_iocp_io_service::shutdown_service() +{ + ::InterlockedExchange(&shutdown_, 1); + + if (timer_thread_) + { + LARGE_INTEGER timeout; + timeout.QuadPart = 1; + ::SetWaitableTimer(waitable_timer_.handle, &timeout, 1, 0, 0, FALSE); + } + + while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0) + { + op_queue ops; + timer_queues_.get_all_timers(ops); + ops.push(completed_ops_); + if (!ops.empty()) + { + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + ::InterlockedDecrement(&outstanding_work_); + op->destroy(); + } + } + else + { + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, gqcs_timeout); + if (overlapped) + { + ::InterlockedDecrement(&outstanding_work_); + static_cast(overlapped)->destroy(); + } + } + } + + if (timer_thread_) + timer_thread_->join(); +} + +asio::error_code win_iocp_io_service::register_handle( + HANDLE handle, asio::error_code& ec) +{ + if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + return ec; +} + +size_t win_iocp_io_service::run(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + size_t n = 0; + while (do_one(true, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +size_t win_iocp_io_service::run_one(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + return do_one(true, ec); +} + +size_t win_iocp_io_service::poll(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + size_t n = 0; + while (do_one(false, ec)) + if (n != (std::numeric_limits::max)()) + ++n; + return n; +} + +size_t win_iocp_io_service::poll_one(asio::error_code& ec) +{ + if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) + { + stop(); + ec = asio::error_code(); + return 0; + } + + call_stack::context ctx(this); + + return do_one(false, ec); +} + +void win_iocp_io_service::stop() +{ + if (::InterlockedExchange(&stopped_, 1) == 0) + { + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "pqcs"); + } + } +} + +void win_iocp_io_service::post_deferred_completion(win_iocp_operation* op) +{ + // Flag the operation as ready. + op->ready_ = 1; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +void win_iocp_io_service::post_deferred_completions( + op_queue& ops) +{ + while (win_iocp_operation* op = ops.front()) + { + ops.pop(); + + // Flag the operation as ready. + op->ready_ = 1; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + completed_ops_.push(ops); + ::InterlockedExchange(&dispatch_required_, 1); + } + } +} + +void win_iocp_io_service::on_pending(win_iocp_operation* op) +{ + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } + } +} + +void win_iocp_io_service::on_completion(win_iocp_operation* op, + DWORD last_error, DWORD bytes_transferred) +{ + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = asio::error::get_system_category(); + op->Offset = last_error; + op->OffsetHigh = bytes_transferred; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +void win_iocp_io_service::on_completion(win_iocp_operation* op, + const asio::error_code& ec, DWORD bytes_transferred) +{ + // Flag that the operation is ready for invocation. + op->ready_ = 1; + + // Store results in the OVERLAPPED structure. + op->Internal = ec.category(); + op->Offset = ec.value(); + op->OffsetHigh = bytes_transferred; + + // Enqueue the operation on the I/O completion port. + if (!::PostQueuedCompletionStatus(iocp_.handle, + 0, overlapped_contains_result, op)) + { + // Out of resources. Put on completed queue instead. + mutex::scoped_lock lock(dispatch_mutex_); + completed_ops_.push(op); + ::InterlockedExchange(&dispatch_required_, 1); + } +} + +size_t win_iocp_io_service::do_one(bool block, asio::error_code& ec) +{ + for (;;) + { + // Try to acquire responsibility for dispatching timers and completed ops. + if (::InterlockedCompareExchange(&dispatch_required_, 0, 1) == 1) + { + mutex::scoped_lock lock(dispatch_mutex_); + + // Dispatch pending timers and operations. + op_queue ops; + ops.push(completed_ops_); + timer_queues_.get_ready_timers(ops); + post_deferred_completions(ops); + update_timeout(); + } + + // Get the next operation from the queue. + DWORD bytes_transferred = 0; + dword_ptr_t completion_key = 0; + LPOVERLAPPED overlapped = 0; + ::SetLastError(0); + BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, + &completion_key, &overlapped, block ? gqcs_timeout : 0); + DWORD last_error = ::GetLastError(); + + if (overlapped) + { + win_iocp_operation* op = static_cast(overlapped); + asio::error_code result_ec(last_error, + asio::error::get_system_category()); + + // We may have been passed the last_error and bytes_transferred in the + // OVERLAPPED structure itself. + if (completion_key == overlapped_contains_result) + { + result_ec = asio::error_code(static_cast(op->Offset), + static_cast(op->Internal)); + bytes_transferred = op->OffsetHigh; + } + + // Otherwise ensure any result has been saved into the OVERLAPPED + // structure. + else + { + op->Internal = result_ec.category(); + op->Offset = result_ec.value(); + op->OffsetHigh = bytes_transferred; + } + + // Dispatch the operation only if ready. The operation may not be ready + // if the initiating function (e.g. a call to WSARecv) has not yet + // returned. This is because the initiating function still wants access + // to the operation's OVERLAPPED structure. + if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) + { + // Ensure the count of outstanding work is decremented on block exit. + work_finished_on_block_exit on_exit = { this }; + (void)on_exit; + + op->complete(*this, result_ec, bytes_transferred); + ec = asio::error_code(); + return 1; + } + } + else if (!ok) + { + if (last_error != WAIT_TIMEOUT) + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + // If we're not polling we need to keep going until we get a real handler. + if (block) + continue; + + ec = asio::error_code(); + return 0; + } + else if (completion_key == wake_for_dispatch) + { + // We have been woken up to try to acquire responsibility for dispatching + // timers and completed operations. + } + else + { + // The stopped_ flag is always checked to ensure that any leftover + // interrupts from a previous run invocation are ignored. + if (::InterlockedExchangeAdd(&stopped_, 0) != 0) + { + // Wake up next thread that is blocked on GetQueuedCompletionStatus. + if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) + { + last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return 0; + } + + ec = asio::error_code(); + return 0; + } + } + } +} + +void win_iocp_io_service::do_add_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(dispatch_mutex_); + + timer_queues_.insert(&queue); + + if (!waitable_timer_.handle) + { + waitable_timer_.handle = ::CreateWaitableTimer(0, FALSE, 0); + if (waitable_timer_.handle == 0) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "timer"); + } + + LARGE_INTEGER timeout; + timeout.QuadPart = -max_timeout_usec; + timeout.QuadPart *= 10; + ::SetWaitableTimer(waitable_timer_.handle, + &timeout, max_timeout_msec, 0, 0, FALSE); + } + + if (!timer_thread_) + { + timer_thread_function thread_function = { this }; + timer_thread_.reset(new thread(thread_function, 65536)); + } +} + +void win_iocp_io_service::do_remove_timer_queue(timer_queue_base& queue) +{ + mutex::scoped_lock lock(dispatch_mutex_); + + timer_queues_.erase(&queue); +} + +void win_iocp_io_service::update_timeout() +{ + if (timer_thread_) + { + // There's no point updating the waitable timer if the new timeout period + // exceeds the maximum timeout. In that case, we might as well wait for the + // existing period of the timer to expire. + long timeout_usec = timer_queues_.wait_duration_usec(max_timeout_usec); + if (timeout_usec < max_timeout_usec) + { + LARGE_INTEGER timeout; + timeout.QuadPart = -timeout_usec; + timeout.QuadPart *= 10; + ::SetWaitableTimer(waitable_timer_.handle, + &timeout, max_timeout_msec, 0, 0, FALSE); + } + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_IO_SERVICE_IPP diff --git a/ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp b/ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp new file mode 100644 index 0000000000..763151434c --- /dev/null +++ b/ext/asio/asio/detail/impl/win_iocp_serial_port_service.ipp @@ -0,0 +1,180 @@ +// +// detail/impl/win_iocp_serial_port_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#include +#include "asio/detail/win_iocp_serial_port_service.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_iocp_serial_port_service::win_iocp_serial_port_service( + asio::io_service& io_service) + : handle_service_(io_service) +{ +} + +void win_iocp_serial_port_service::shutdown_service() +{ +} + +asio::error_code win_iocp_serial_port_service::open( + win_iocp_serial_port_service::implementation_type& impl, + const std::string& device, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + // For convenience, add a leading \\.\ sequence if not already present. + std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device; + + // Open a handle to the serial port. + ::HANDLE handle = ::CreateFileA(name.c_str(), + GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (handle == INVALID_HANDLE_VALUE) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Determine the initial serial port parameters. + using namespace std; // For memset. + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set some default serial port parameters. This implementation does not + // support changing these, so they might as well be in a known state. + dcb.fBinary = TRUE; // Win32 only supports binary mode. + dcb.fDsrSensitivity = FALSE; + dcb.fNull = FALSE; // Do not ignore NULL characters. + dcb.fAbortOnError = FALSE; // Ignore serial framing errors. + if (!::SetCommState(handle, &dcb)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // Set up timeouts so that the serial port will behave similarly to a + // network socket. Reads wait for at least one byte, then return with + // whatever they have. Writes return once everything is out the door. + ::COMMTIMEOUTS timeouts; + timeouts.ReadIntervalTimeout = 1; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + if (!::SetCommTimeouts(handle, &timeouts)) + { + DWORD last_error = ::GetLastError(); + ::CloseHandle(handle); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + // We're done. Take ownership of the serial port handle. + if (handle_service_.assign(impl, handle, ec)) + ::CloseHandle(handle); + return ec; +} + +asio::error_code win_iocp_serial_port_service::do_set_option( + win_iocp_serial_port_service::implementation_type& impl, + win_iocp_serial_port_service::store_function_type store, + const void* option, asio::error_code& ec) +{ + using namespace std; // For memcpy. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + if (store(option, dcb, ec)) + return ec; + + if (!::SetCommState(handle_service_.native(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_serial_port_service::do_get_option( + const win_iocp_serial_port_service::implementation_type& impl, + win_iocp_serial_port_service::load_function_type load, + void* option, asio::error_code& ec) const +{ + using namespace std; // For memset. + + ::DCB dcb; + memset(&dcb, 0, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + if (!::GetCommState(handle_service_.native(impl), &dcb)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + return ec; + } + + return load(option, dcb, ec); +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SERIAL_PORT_SERVICE_IPP diff --git a/ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp b/ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp new file mode 100644 index 0000000000..37c6f63379 --- /dev/null +++ b/ext/asio/asio/detail/impl/win_iocp_socket_service_base.ipp @@ -0,0 +1,575 @@ +// +// detail/impl/win_iocp_socket_service_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP +#define ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/detail/win_iocp_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_iocp_socket_service_base::win_iocp_socket_service_base( + asio::io_service& io_service) + : io_service_(io_service), + iocp_service_(use_service(io_service)), + reactor_(0), + mutex_(), + impl_list_(0) +{ +} + +void win_iocp_socket_service_base::shutdown_service() +{ + // Close all implementations, causing all operations to complete. + asio::detail::mutex::scoped_lock lock(mutex_); + base_implementation_type* impl = impl_list_; + while (impl) + { + asio::error_code ignored_ec; + close_for_destruction(*impl); + impl = impl->next_; + } +} + +void win_iocp_socket_service_base::construct( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Insert implementation into linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + impl.next_ = impl_list_; + impl.prev_ = 0; + if (impl_list_) + impl_list_->prev_ = &impl; + impl_list_ = &impl; +} + +void win_iocp_socket_service_base::destroy( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + close_for_destruction(impl); + + // Remove implementation from linked list of all implementations. + asio::detail::mutex::scoped_lock lock(mutex_); + if (impl_list_ == &impl) + impl_list_ = impl.next_; + if (impl.prev_) + impl.prev_->next_ = impl.next_; + if (impl.next_) + impl.next_->prev_= impl.prev_; + impl.next_ = 0; + impl.prev_ = 0; +} + +asio::error_code win_iocp_socket_service_base::close( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (is_open(impl)) + { + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->close_descriptor(impl.socket_, impl.reactor_data_); + } + + if (socket_ops::close(impl.socket_, impl.state_, false, ec) == 0) + { + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) + } + + return ec; +} + +asio::error_code win_iocp_socket_service_base::cancel( + win_iocp_socket_service_base::base_implementation_type& impl, + asio::error_code& ec) +{ + if (!is_open(impl)) + { + ec = asio::error::bad_descriptor; + return ec; + } + else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( + ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) + { + // The version of Windows supports cancellation from any thread. + typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); + cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!cancel_io_ex(sock_as_handle, 0)) + { + DWORD last_error = ::GetLastError(); + if (last_error == ERROR_NOT_FOUND) + { + // ERROR_NOT_FOUND means that there were no operations to be + // cancelled. We swallow this error to match the behaviour on other + // platforms. + ec = asio::error_code(); + } + else + { + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + } + else + { + ec = asio::error_code(); + } + } +#if defined(ASIO_ENABLE_CANCELIO) + else if (impl.safe_cancellation_thread_id_ == 0) + { + // No operations have been started, so there's nothing to cancel. + ec = asio::error_code(); + } + else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) + { + // Asynchronous operations have been started from the current thread only, + // so it is safe to try to cancel them using CancelIo. + socket_type sock = impl.socket_; + HANDLE sock_as_handle = reinterpret_cast(sock); + if (!::CancelIo(sock_as_handle)) + { + DWORD last_error = ::GetLastError(); + ec = asio::error_code(last_error, + asio::error::get_system_category()); + } + else + { + ec = asio::error_code(); + } + } + else + { + // Asynchronous operations have been started from more than one thread, + // so cancellation is not safe. + ec = asio::error::operation_not_supported; + } +#else // defined(ASIO_ENABLE_CANCELIO) + else + { + // Cancellation is not supported as CancelIo may not be used. + ec = asio::error::operation_not_supported; + } +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Cancel any operations started via the reactor. + if (!ec) + { + reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->cancel_ops(impl.socket_, impl.reactor_data_); + } + + return ec; +} + +asio::error_code win_iocp_socket_service_base::do_open( + win_iocp_socket_service_base::base_implementation_type& impl, + int family, int type, int protocol, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + socket_holder sock(socket_ops::socket(family, type, protocol, ec)); + if (sock.get() == invalid_socket) + return ec; + + HANDLE sock_as_handle = reinterpret_cast(sock.get()); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; + + impl.socket_ = sock.release(); + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); + ec = asio::error_code(); + return ec; +} + +asio::error_code win_iocp_socket_service_base::do_assign( + win_iocp_socket_service_base::base_implementation_type& impl, + int type, socket_type native_socket, asio::error_code& ec) +{ + if (is_open(impl)) + { + ec = asio::error::already_open; + return ec; + } + + HANDLE sock_as_handle = reinterpret_cast(native_socket); + if (iocp_service_.register_handle(sock_as_handle, ec)) + return ec; + + impl.socket_ = native_socket; + switch (type) + { + case SOCK_STREAM: impl.state_ = socket_ops::stream_oriented; break; + case SOCK_DGRAM: impl.state_ = socket_ops::datagram_oriented; break; + default: impl.state_ = 0; break; + } + impl.cancel_token_.reset(static_cast(0), socket_ops::noop_deleter()); + ec = asio::error_code(); + return ec; +} + +void win_iocp_socket_service_base::start_send_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + int result = ::WSASend(impl.socket_, buffers, + static_cast(buffer_count), &bytes_transferred, flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_send_to_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + const socket_addr_type* addr, int addrlen, + socket_base::message_flags flags, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + int result = ::WSASendTo(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, flags, addr, addrlen, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_receive_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (noop) + iocp_service_.on_completion(op); + else if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecv(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, &recv_flags, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_NETNAME_DELETED) + last_error = WSAECONNRESET; + else if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_null_buffers_receive_op( + win_iocp_socket_service_base::base_implementation_type& impl, + socket_base::message_flags flags, reactor_op* op) +{ + if ((impl.state_ & socket_ops::stream_oriented) != 0) + { + // For stream sockets on Windows, we may issue a 0-byte overlapped + // WSARecv to wait until there is data available on the socket. + ::WSABUF buf = { 0, 0 }; + start_receive_op(impl, &buf, 1, flags, false, op); + } + else + { + start_reactor_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + op); + } +} + +void win_iocp_socket_service_base::start_receive_from_op( + win_iocp_socket_service_base::base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, + socket_base::message_flags flags, int* addrlen, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else + { + DWORD bytes_transferred = 0; + DWORD recv_flags = flags; + int result = ::WSARecvFrom(impl.socket_, buffers, + static_cast(buffer_count), + &bytes_transferred, &recv_flags, addr, addrlen, op, 0); + DWORD last_error = ::WSAGetLastError(); + if (last_error == ERROR_PORT_UNREACHABLE) + last_error = WSAECONNREFUSED; + if (result != 0 && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error, bytes_transferred); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_accept_op( + win_iocp_socket_service_base::base_implementation_type& impl, + bool peer_is_open, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op) +{ + update_cancellation_thread_id(impl); + iocp_service_.work_started(); + + if (!is_open(impl)) + iocp_service_.on_completion(op, asio::error::bad_descriptor); + else if (peer_is_open) + iocp_service_.on_completion(op, asio::error::already_open); + else + { + asio::error_code ec; + new_socket.reset(socket_ops::socket(family, type, protocol, ec)); + if (new_socket.get() == invalid_socket) + iocp_service_.on_completion(op, ec); + else + { + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, + 0, address_length, address_length, &bytes_read, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + } + } +} + +void win_iocp_socket_service_base::restart_accept_op( + socket_type s, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op) +{ + new_socket.reset(); + iocp_service_.work_started(); + + asio::error_code ec; + new_socket.reset(socket_ops::socket(family, type, protocol, ec)); + if (new_socket.get() == invalid_socket) + iocp_service_.on_completion(op, ec); + else + { + DWORD bytes_read = 0; + BOOL result = ::AcceptEx(s, new_socket.get(), output_buffer, + 0, address_length, address_length, &bytes_read, op); + DWORD last_error = ::WSAGetLastError(); + if (!result && last_error != WSA_IO_PENDING) + iocp_service_.on_completion(op, last_error); + else + iocp_service_.on_pending(op); + } +} + +void win_iocp_socket_service_base::start_reactor_op( + win_iocp_socket_service_base::base_implementation_type& impl, + int op_type, reactor_op* op) +{ + reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if (is_open(impl)) + { + r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false); + return; + } + else + op->ec_ = asio::error::bad_descriptor; + + iocp_service_.post_immediate_completion(op); +} + +void win_iocp_socket_service_base::start_connect_op( + win_iocp_socket_service_base::base_implementation_type& impl, + reactor_op* op, const socket_addr_type* addr, std::size_t addrlen) +{ + reactor& r = get_reactor(); + update_cancellation_thread_id(impl); + + if ((impl.state_ & socket_ops::non_blocking) != 0 + || socket_ops::set_internal_non_blocking( + impl.socket_, impl.state_, op->ec_)) + { + if (socket_ops::connect(impl.socket_, addr, addrlen, op->ec_) != 0) + { + if (op->ec_ == asio::error::in_progress + || op->ec_ == asio::error::would_block) + { + op->ec_ = asio::error_code(); + r.start_op(reactor::connect_op, impl.socket_, + impl.reactor_data_, op, false); + return; + } + } + } + + r.post_immediate_completion(op); +} + +void win_iocp_socket_service_base::close_for_destruction( + win_iocp_socket_service_base::base_implementation_type& impl) +{ + if (is_open(impl)) + { + // Check if the reactor was created, in which case we need to close the + // socket on the reactor as well to cancel any operations that might be + // running there. + reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (r) + r->close_descriptor(impl.socket_, impl.reactor_data_); + } + + asio::error_code ignored_ec; + socket_ops::close(impl.socket_, impl.state_, true, ignored_ec); + impl.socket_ = invalid_socket; + impl.state_ = 0; + impl.cancel_token_.reset(); +#if defined(ASIO_ENABLE_CANCELIO) + impl.safe_cancellation_thread_id_ = 0; +#endif // defined(ASIO_ENABLE_CANCELIO) +} + +void win_iocp_socket_service_base::update_cancellation_thread_id( + win_iocp_socket_service_base::base_implementation_type& impl) +{ +#if defined(ASIO_ENABLE_CANCELIO) + if (impl.safe_cancellation_thread_id_ == 0) + impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); + else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) + impl.safe_cancellation_thread_id_ = ~DWORD(0); +#else // defined(ASIO_ENABLE_CANCELIO) + (void)impl; +#endif // defined(ASIO_ENABLE_CANCELIO) +} + +reactor& win_iocp_socket_service_base::get_reactor() +{ + reactor* r = static_cast( + interlocked_compare_exchange_pointer( + reinterpret_cast(&reactor_), 0, 0)); + if (!r) + { + r = &(use_service(io_service_)); + interlocked_exchange_pointer(reinterpret_cast(&reactor_), r); + } + return *r; +} + +void* win_iocp_socket_service_base::interlocked_compare_exchange_pointer( + void** dest, void* exch, void* cmp) +{ +#if defined(_M_IX86) + return reinterpret_cast(InterlockedCompareExchange( + reinterpret_cast(dest), reinterpret_cast(exch), + reinterpret_cast(cmp))); +#else + return InterlockedCompareExchangePointer(dest, exch, cmp); +#endif +} + +void* win_iocp_socket_service_base::interlocked_exchange_pointer( + void** dest, void* val) +{ +#if defined(_M_IX86) + return reinterpret_cast(InterlockedExchange( + reinterpret_cast(dest), reinterpret_cast(val))); +#else + return InterlockedExchangePointer(dest, val); +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_IMPL_WIN_IOCP_SOCKET_SERVICE_BASE_IPP diff --git a/ext/asio/asio/detail/impl/win_mutex.ipp b/ext/asio/asio/detail/impl/win_mutex.ipp new file mode 100644 index 0000000000..910cc23372 --- /dev/null +++ b/ext/asio/asio/detail/impl/win_mutex.ipp @@ -0,0 +1,78 @@ +// +// detail/impl/win_mutex.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_MUTEX_IPP +#define ASIO_DETAIL_IMPL_WIN_MUTEX_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_mutex.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_mutex::win_mutex() +{ + int error = do_init(); + asio::error_code ec(error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "mutex"); +} + +int win_mutex::do_init() +{ +#if defined(__MINGW32__) + // Not sure if MinGW supports structured exception handling, so for now + // we'll just call the Windows API and hope. +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + return ::GetLastError(); +# endif + return 0; +#else + __try + { +# if defined(UNDER_CE) + ::InitializeCriticalSection(&crit_section_); +# else + if (!::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000)) + return ::GetLastError(); +# endif + } + __except(GetExceptionCode() == STATUS_NO_MEMORY + ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + return ERROR_OUTOFMEMORY; + } + + return 0; +#endif +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_MUTEX_IPP diff --git a/ext/asio/asio/detail/impl/win_thread.ipp b/ext/asio/asio/detail/impl/win_thread.ipp new file mode 100644 index 0000000000..65844c8866 --- /dev/null +++ b/ext/asio/asio/detail/impl/win_thread.ipp @@ -0,0 +1,138 @@ +// +// detail/impl/win_thread.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_THREAD_IPP +#define ASIO_DETAIL_IMPL_WIN_THREAD_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#include +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_thread.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +win_thread::~win_thread() +{ + ::CloseHandle(thread_); + + // The exit_event_ handle is deliberately allowed to leak here since it + // is an error for the owner of an internal thread not to join() it. +} + +void win_thread::join() +{ + HANDLE handles[2] = { exit_event_, thread_ }; + ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + ::CloseHandle(exit_event_); + if (terminate_threads()) + { + ::TerminateThread(thread_, 0); + } + else + { + ::QueueUserAPC(apc_function, thread_, 0); + ::WaitForSingleObject(thread_, INFINITE); + } +} + +void win_thread::start_thread(func_base* arg, unsigned int stack_size) +{ + ::HANDLE entry_event = 0; + arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0); + if (!entry_event) + { + DWORD last_error = ::GetLastError(); + delete arg; + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread.entry_event"); + } + + arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0); + if (!exit_event_) + { + DWORD last_error = ::GetLastError(); + delete arg; + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread.exit_event"); + } + + unsigned int thread_id = 0; + thread_ = reinterpret_cast(::_beginthreadex(0, + stack_size, win_thread_function, arg, 0, &thread_id)); + if (!thread_) + { + DWORD last_error = ::GetLastError(); + delete arg; + if (entry_event) + ::CloseHandle(entry_event); + if (exit_event_) + ::CloseHandle(exit_event_); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); + } + + if (entry_event) + { + ::WaitForSingleObject(entry_event, INFINITE); + ::CloseHandle(entry_event); + } +} + +unsigned int __stdcall win_thread_function(void* arg) +{ + std::auto_ptr func( + static_cast(arg)); + + ::SetEvent(func->entry_event_); + + func->run(); + + // Signal that the thread has finished its work, but rather than returning go + // to sleep to put the thread into a well known state. If the thread is being + // joined during global object destruction then it may be killed using + // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx + // call will be interrupted using QueueUserAPC and the thread will shut down + // cleanly. + HANDLE exit_event = func->exit_event_; + func.reset(); + ::SetEvent(exit_event); + ::SleepEx(INFINITE, TRUE); + + return 0; +} + +#if defined(WINVER) && (WINVER < 0x0500) +void __stdcall apc_function(ULONG) {} +#else +void __stdcall apc_function(ULONG_PTR) {} +#endif + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) + +#endif // ASIO_DETAIL_IMPL_WIN_THREAD_IPP diff --git a/ext/asio/asio/detail/impl/win_tss_ptr.ipp b/ext/asio/asio/detail/impl/win_tss_ptr.ipp new file mode 100644 index 0000000000..de3ea3c7e0 --- /dev/null +++ b/ext/asio/asio/detail/impl/win_tss_ptr.ipp @@ -0,0 +1,57 @@ +// +// detail/impl/win_tss_ptr.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP +#define ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_WINDOWS) + +#include "asio/detail/throw_error.hpp" +#include "asio/detail/win_tss_ptr.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +DWORD win_tss_ptr_create() +{ +#if defined(UNDER_CE) + enum { out_of_indexes = 0xFFFFFFFF }; +#else + enum { out_of_indexes = TLS_OUT_OF_INDEXES }; +#endif + + DWORD tss_key = ::TlsAlloc(); + if (tss_key == out_of_indexes) + { + DWORD last_error = ::GetLastError(); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "tss"); + } + return tss_key; +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_WINDOWS) + +#endif // ASIO_DETAIL_IMPL_WIN_TSS_PTR_IPP diff --git a/ext/asio/asio/detail/impl/winsock_init.ipp b/ext/asio/asio/detail/impl/winsock_init.ipp new file mode 100644 index 0000000000..3b20c98e27 --- /dev/null +++ b/ext/asio/asio/detail/impl/winsock_init.ipp @@ -0,0 +1,69 @@ +// +// detail/impl/winsock_init.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP +#define ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +void winsock_init_base::startup(data& d, + unsigned char major, unsigned char minor) +{ + if (::InterlockedIncrement(&d.init_count_) == 1) + { + WSADATA wsa_data; + long result = ::WSAStartup(MAKEWORD(major, minor), &wsa_data); + ::InterlockedExchange(&d.result_, result); + } +} + +void winsock_init_base::cleanup(data& d) +{ + if (::InterlockedDecrement(&d.init_count_) == 0) + { + ::WSACleanup(); + } +} + +void winsock_init_base::throw_on_error(data& d) +{ + long result = ::InterlockedExchangeAdd(&d.result_, 0); + if (result != 0) + { + asio::error_code ec(result, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "winsock"); + } +} + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + +#endif // ASIO_DETAIL_IMPL_WINSOCK_INIT_IPP diff --git a/ext/asio/asio/detail/io_control.hpp b/ext/asio/asio/detail/io_control.hpp index df7171cbe2..b3ef844958 100644 --- a/ext/asio/asio/detail/io_control.hpp +++ b/ext/asio/asio/detail/io_control.hpp @@ -1,8 +1,8 @@ // -// io_control.hpp -// ~~~~~~~~~~~~~~ +// detail/io_control.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { namespace io_control { diff --git a/ext/asio/asio/detail/kqueue_reactor.hpp b/ext/asio/asio/detail/kqueue_reactor.hpp index bfa004d0da..07087ddd6e 100644 --- a/ext/asio/asio/detail/kqueue_reactor.hpp +++ b/ext/asio/asio/detail/kqueue_reactor.hpp @@ -1,8 +1,8 @@ // -// kqueue_reactor.hpp -// ~~~~~~~~~~~~~~~~~~ +// detail/kqueue_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,41 +16,35 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/kqueue_reactor_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_KQUEUE) -#include "asio/detail/push_options.hpp" #include #include #include #include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/io_service.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/hash_map.hpp" +#include "asio/detail/kqueue_reactor_fwd.hpp" #include "asio/detail/mutex.hpp" +#include "asio/detail/object_pool.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/select_interrupter.hpp" -#include "asio/detail/service_base.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" // Older versions of Mac OS X may not define EV_OOBAND. #if !defined(EV_OOBAND) # define EV_OOBAND EV_FLAG1 #endif // !defined(EV_OOBAND) +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -64,384 +58,98 @@ public: // Per-descriptor queues. struct descriptor_state { - descriptor_state() {} - descriptor_state(const descriptor_state&) {} - void operator=(const descriptor_state&) {} - + friend class kqueue_reactor; + friend class object_pool_access; mutex mutex_; op_queue op_queue_[max_ops]; bool shutdown_; + descriptor_state* next_; + descriptor_state* prev_; }; // Per-descriptor data. typedef descriptor_state* per_descriptor_data; // Constructor. - kqueue_reactor(asio::io_service& io_service) - : asio::detail::service_base(io_service), - io_service_(use_service(io_service)), - mutex_(), - kqueue_fd_(do_kqueue_create()), - interrupter_(), - shutdown_(false) - { - // The interrupter is put into a permanently readable state. Whenever we - // want to interrupt the blocked kevent call we register a one-shot read - // operation against the descriptor. - interrupter_.interrupt(); - } + ASIO_DECL kqueue_reactor(asio::io_service& io_service); // Destructor. - ~kqueue_reactor() - { - close(kqueue_fd_); - } + ASIO_DECL ~kqueue_reactor(); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - mutex::scoped_lock lock(mutex_); - shutdown_ = true; - lock.unlock(); - - op_queue ops; - - descriptor_map::iterator iter = registered_descriptors_.begin(); - descriptor_map::iterator end = registered_descriptors_.end(); - while (iter != end) - { - for (int i = 0; i < max_ops; ++i) - ops.push(iter->second.op_queue_[i]); - iter->second.shutdown_ = true; - ++iter; - } - - timer_queues_.get_all_timers(ops); - } + ASIO_DECL void shutdown_service(); // Initialise the task. - void init_task() - { - io_service_.init_task(); - } + ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type descriptor, - per_descriptor_data& descriptor_data) + ASIO_DECL int register_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op) { - mutex::scoped_lock lock(registered_descriptors_mutex_); - - descriptor_map::iterator new_entry = registered_descriptors_.insert( - std::make_pair(descriptor, descriptor_state())).first; - descriptor_data = &new_entry->second; - - descriptor_data->shutdown_ = false; - - return 0; + io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. - void start_op(int op_type, socket_type descriptor, + ASIO_DECL void start_op(int op_type, socket_type descriptor, per_descriptor_data& descriptor_data, - reactor_op* op, bool allow_speculative) - { - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - if (descriptor_data->shutdown_) - return; - - bool first = descriptor_data->op_queue_[op_type].empty(); - if (first) - { - if (allow_speculative) - { - if (op_type != read_op || descriptor_data->op_queue_[except_op].empty()) - { - if (op->perform()) - { - descriptor_lock.unlock(); - io_service_.post_immediate_completion(op); - return; - } - } - } - } - - descriptor_data->op_queue_[op_type].push(op); - io_service_.work_started(); - - if (first) - { - struct kevent event; - switch (op_type) - { - case read_op: - EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); - break; - case write_op: - EV_SET(&event, descriptor, EVFILT_WRITE, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); - break; - case except_op: - if (!descriptor_data->op_queue_[read_op].empty()) - return; // Already registered for read events. - EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); - break; - } - - if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) - { - op->ec_ = asio::error_code(errno, - asio::error::get_system_category()); - descriptor_data->op_queue_[op_type].pop(); - io_service_.post_deferred_completion(op); - } - } - } + reactor_op* op, bool allow_speculative); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. - void cancel_ops(socket_type , per_descriptor_data& descriptor_data) - { - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - - op_queue ops; - for (int i = 0; i < max_ops; ++i) - { - while (reactor_op* op = descriptor_data->op_queue_[i].front()) - { - op->ec_ = asio::error::operation_aborted; - descriptor_data->op_queue_[i].pop(); - ops.push(op); - } - } - - descriptor_lock.unlock(); - - io_service_.post_deferred_completions(ops); - } + ASIO_DECL void cancel_ops(socket_type descriptor, + per_descriptor_data& descriptor_data); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor, - per_descriptor_data& descriptor_data) - { - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); - - // Remove the descriptor from the set of known descriptors. The descriptor - // will be automatically removed from the kqueue set when it is closed. - descriptor_data->shutdown_ = true; - - op_queue ops; - for (int i = 0; i < max_ops; ++i) - { - while (reactor_op* op = descriptor_data->op_queue_[i].front()) - { - op->ec_ = asio::error::operation_aborted; - descriptor_data->op_queue_[i].pop(); - ops.push(op); - } - } - - descriptor_lock.unlock(); - - registered_descriptors_.erase(descriptor); - - descriptors_lock.unlock(); - - io_service_.post_deferred_completions(ops); - } + ASIO_DECL void close_descriptor(socket_type descriptor, + per_descriptor_data& descriptor_data); // Add a new timer queue to the reactor. template - void add_timer_queue(timer_queue& timer_queue) - { - mutex::scoped_lock lock(mutex_); - timer_queues_.insert(&timer_queue); - } + void add_timer_queue(timer_queue& queue); // Remove a timer queue from the reactor. template - void remove_timer_queue(timer_queue& timer_queue) - { - mutex::scoped_lock lock(mutex_); - timer_queues_.erase(&timer_queue); - } + void remove_timer_queue(timer_queue& queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template - void schedule_timer(timer_queue& timer_queue, - const typename Time_Traits::time_type& time, timer_op* op, void* token) - { - mutex::scoped_lock lock(mutex_); - if (!shutdown_) - { - bool earliest = timer_queue.enqueue_timer(time, op, token); - io_service_.work_started(); - if (earliest) - interrupt(); - } - } + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template - std::size_t cancel_timer(timer_queue& timer_queue, void* token) - { - mutex::scoped_lock lock(mutex_); - op_queue ops; - std::size_t n = timer_queue.cancel_timer(token, ops); - lock.unlock(); - io_service_.post_deferred_completions(ops); - return n; - } + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer); // Run the kqueue loop. - void run(bool block, op_queue& ops) - { - mutex::scoped_lock lock(mutex_); - - // Determine how long to block while waiting for events. - timespec timeout_buf = { 0, 0 }; - timespec* timeout = block ? get_timeout(timeout_buf) : &timeout_buf; - - lock.unlock(); - - // Block on the kqueue descriptor. - struct kevent events[128]; - int num_events = kevent(kqueue_fd_, 0, 0, events, 128, timeout); - - // Dispatch the waiting events. - for (int i = 0; i < num_events; ++i) - { - int descriptor = events[i].ident; - void* ptr = events[i].udata; - if (ptr == &interrupter_) - { - // No need to reset the interrupter since we're leaving the descriptor - // in a ready-to-read state and relying on one-shot notifications. - } - else - { - descriptor_state* descriptor_data = static_cast(ptr); - mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); - - // Exception operations must be processed first to ensure that any - // out-of-band data is read before normal data. - static const int filter[max_ops] = - { EVFILT_READ, EVFILT_WRITE, EVFILT_READ }; - for (int j = max_ops - 1; j >= 0; --j) - { - if (events[i].filter == filter[j]) - { - if (j != except_op || events[i].flags & EV_OOBAND) - { - while (reactor_op* op = descriptor_data->op_queue_[j].front()) - { - if (events[i].flags & EV_ERROR) - { - op->ec_ = asio::error_code(events[i].data, - asio::error::get_system_category()); - descriptor_data->op_queue_[j].pop(); - ops.push(op); - } - if (op->perform()) - { - descriptor_data->op_queue_[j].pop(); - ops.push(op); - } - else - break; - } - } - } - } - - // Renew registration for event notifications. - struct kevent event; - switch (events[i].filter) - { - case EVFILT_READ: - if (!descriptor_data->op_queue_[read_op].empty()) - EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); - else if (!descriptor_data->op_queue_[except_op].empty()) - EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); - else - continue; - case EVFILT_WRITE: - if (!descriptor_data->op_queue_[write_op].empty()) - EV_SET(&event, descriptor, EVFILT_WRITE, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); - else - continue; - default: - break; - } - if (::kevent(kqueue_fd_, &event, 1, 0, 0, 0) == -1) - { - asio::error_code error(errno, - asio::error::get_system_category()); - for (int j = 0; j < max_ops; ++j) - { - while (reactor_op* op = descriptor_data->op_queue_[j].front()) - { - op->ec_ = error; - descriptor_data->op_queue_[j].pop(); - ops.push(op); - } - } - } - } - } - - lock.lock(); - timer_queues_.get_ready_timers(ops); - } + ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the kqueue loop. - void interrupt() - { - struct kevent event; - EV_SET(&event, interrupter_.read_descriptor(), - EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &interrupter_); - ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); - } + ASIO_DECL void interrupt(); private: // Create the kqueue file descriptor. Throws an exception if the descriptor // cannot be created. - static int do_kqueue_create() - { - int fd = kqueue(); - if (fd == -1) - { - boost::throw_exception( - asio::system_error( - asio::error_code(errno, - asio::error::get_system_category()), - "kqueue")); - } - return fd; - } + ASIO_DECL static int do_kqueue_create(); + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Get the timeout value for the kevent call. - timespec* get_timeout(timespec& ts) - { - // By default we will wait no longer than 5 minutes. This will ensure that - // any changes to the system clock are detected after no longer than this. - long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); - ts.tv_sec = usec / 1000000; - ts.tv_nsec = (usec % 1000000) * 1000; - return &ts; - } + ASIO_DECL timespec* get_timeout(timespec& ts); // The io_service implementation used to post completions. io_service_impl& io_service_; @@ -464,22 +172,20 @@ private: // Mutex to protect access to the registered descriptors. mutex registered_descriptors_mutex_; - // Keep track of all registered descriptors. This code relies on the fact that - // the hash_map implementation pools deleted nodes, meaning that we can assume - // our descriptor_state pointer remains valid even after the entry is removed. - // Technically this is not true for C++98, as that standard says that spliced - // elements in a list are invalidated. However, C++0x fixes this shortcoming - // so we'll just assume that C++98 std::list implementations will do the right - // thing anyway. - typedef detail::hash_map descriptor_map; - descriptor_map registered_descriptors_; + // Keep track of all registered descriptors. + object_pool registered_descriptors_; }; } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_KQUEUE) - #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/kqueue_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/kqueue_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_KQUEUE) + #endif // ASIO_DETAIL_KQUEUE_REACTOR_HPP diff --git a/ext/asio/asio/detail/kqueue_reactor_fwd.hpp b/ext/asio/asio/detail/kqueue_reactor_fwd.hpp index abbc0c7d7b..fda59ce8ec 100644 --- a/ext/asio/asio/detail/kqueue_reactor_fwd.hpp +++ b/ext/asio/asio/detail/kqueue_reactor_fwd.hpp @@ -1,8 +1,8 @@ // -// kqueue_reactor_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/kqueue_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2005 Stefan Arentz (stefan at soze dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,15 +16,9 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#if !defined(ASIO_DISABLE_KQUEUE) - -#if (defined(__MACH__) && defined(__APPLE__)) \ - || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) - -// Define this to indicate that kqueue is supported on the target platform. -#define ASIO_HAS_KQUEUE 1 +#if defined(ASIO_HAS_KQUEUE) namespace asio { namespace detail { @@ -34,11 +28,6 @@ class kqueue_reactor; } // namespace detail } // namespace asio -#endif // (defined(__MACH__) && defined(__APPLE__)) - // || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) - -#endif // !defined(ASIO_DISABLE_KQUEUE) - -#include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_KQUEUE) #endif // ASIO_DETAIL_KQUEUE_REACTOR_FWD_HPP diff --git a/ext/asio/asio/detail/local_free_on_block_exit.hpp b/ext/asio/asio/detail/local_free_on_block_exit.hpp index 554943c817..a55bd4b34d 100644 --- a/ext/asio/asio/detail/local_free_on_block_exit.hpp +++ b/ext/asio/asio/detail/local_free_on_block_exit.hpp @@ -1,8 +1,8 @@ // -// local_free_on_block_exit.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/local_free_on_block_exit.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -52,8 +50,8 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + #endif // ASIO_DETAIL_LOCAL_FREE_ON_BLOCK_EXIT_HPP diff --git a/ext/asio/asio/detail/macos_fenced_block.hpp b/ext/asio/asio/detail/macos_fenced_block.hpp index 3c303d62bc..fcb8bfc61f 100644 --- a/ext/asio/asio/detail/macos_fenced_block.hpp +++ b/ext/asio/asio/detail/macos_fenced_block.hpp @@ -1,8 +1,8 @@ // -// macos_fenced_block.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/macos_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(__MACH__) && defined(__APPLE__) -#include "asio/detail/push_options.hpp" #include -#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -50,8 +46,8 @@ public: } // namespace detail } // namespace asio -#endif // defined(__MACH__) && defined(__APPLE__) - #include "asio/detail/pop_options.hpp" +#endif // defined(__MACH__) && defined(__APPLE__) + #endif // ASIO_DETAIL_MACOS_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/mutex.hpp b/ext/asio/asio/detail/mutex.hpp index 024ec7f43f..dc478ff8c3 100644 --- a/ext/asio/asio/detail/mutex.hpp +++ b/ext/asio/asio/detail/mutex.hpp @@ -1,8 +1,8 @@ // -// mutex.hpp -// ~~~~~~~~~ +// detail/mutex.hpp +// ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_mutex.hpp" @@ -45,6 +41,4 @@ typedef posix_mutex mutex; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_MUTEX_HPP diff --git a/ext/asio/asio/detail/noncopyable.hpp b/ext/asio/asio/detail/noncopyable.hpp index 8b73ff0407..e7f6437d7c 100644 --- a/ext/asio/asio/detail/noncopyable.hpp +++ b/ext/asio/asio/detail/noncopyable.hpp @@ -1,8 +1,8 @@ // -// noncopyable.hpp -// ~~~~~~~~~~~~~~~ +// detail/noncopyable.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include #include -#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/null_event.hpp b/ext/asio/asio/detail/null_event.hpp index bcea31b804..db7b7471fb 100644 --- a/ext/asio/asio/detail/null_event.hpp +++ b/ext/asio/asio/detail/null_event.hpp @@ -1,8 +1,8 @@ // -// null_event.hpp -// ~~~~~~~~~~~~~~ +// detail/null_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -70,8 +68,8 @@ public: } // namespace detail } // namespace asio -#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) - #include "asio/detail/pop_options.hpp" +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_NULL_EVENT_HPP diff --git a/ext/asio/asio/detail/null_fenced_block.hpp b/ext/asio/asio/detail/null_fenced_block.hpp index dd9a095a72..fbd025f18b 100644 --- a/ext/asio/asio/detail/null_fenced_block.hpp +++ b/ext/asio/asio/detail/null_fenced_block.hpp @@ -1,8 +1,8 @@ // -// null_fenced_block.hpp -// ~~~~~~~~~~~~~~~~~~~~~ +// detail/null_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/ext/asio/asio/detail/null_mutex.hpp b/ext/asio/asio/detail/null_mutex.hpp index 6661ef8324..bbceb7c2a5 100644 --- a/ext/asio/asio/detail/null_mutex.hpp +++ b/ext/asio/asio/detail/null_mutex.hpp @@ -1,8 +1,8 @@ // -// null_mutex.hpp -// ~~~~~~~~~~~~~~ +// detail/null_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #include "asio/detail/noncopyable.hpp" #include "asio/detail/scoped_lock.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -59,8 +57,8 @@ public: } // namespace detail } // namespace asio -#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) - #include "asio/detail/pop_options.hpp" +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_NULL_MUTEX_HPP diff --git a/ext/asio/asio/detail/null_signal_blocker.hpp b/ext/asio/asio/detail/null_signal_blocker.hpp index a5db315acb..e0d996f736 100644 --- a/ext/asio/asio/detail/null_signal_blocker.hpp +++ b/ext/asio/asio/detail/null_signal_blocker.hpp @@ -1,8 +1,8 @@ // -// null_signal_blocker.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~ +// detail/null_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +#if !defined(BOOST_HAS_THREADS) \ + || defined(ASIO_DISABLE_THREADS) \ + || defined(BOOST_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -56,8 +58,12 @@ public: } // namespace detail } // namespace asio -#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) - #include "asio/detail/pop_options.hpp" +#endif // !defined(BOOST_HAS_THREADS) + // || defined(ASIO_DISABLE_THREADS) + // || defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + #endif // ASIO_DETAIL_NULL_SIGNAL_BLOCKER_HPP diff --git a/ext/asio/asio/detail/null_thread.hpp b/ext/asio/asio/detail/null_thread.hpp index ce3d470ad9..bfe918e289 100644 --- a/ext/asio/asio/detail/null_thread.hpp +++ b/ext/asio/asio/detail/null_thread.hpp @@ -1,8 +1,8 @@ // -// null_thread.hpp -// ~~~~~~~~~~~~~~~ +// detail/null_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,21 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -40,11 +34,10 @@ class null_thread public: // Constructor. template - null_thread(Function ) + null_thread(Function, unsigned int = 0) { - asio::system_error e( + asio::detail::throw_error( asio::error::operation_not_supported, "thread"); - boost::throw_exception(e); } // Destructor. @@ -61,8 +54,8 @@ public: } // namespace detail } // namespace asio -#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) - #include "asio/detail/pop_options.hpp" +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_NULL_THREAD_HPP diff --git a/ext/asio/asio/detail/null_tss_ptr.hpp b/ext/asio/asio/detail/null_tss_ptr.hpp index 112b4761c6..d3456e6157 100644 --- a/ext/asio/asio/detail/null_tss_ptr.hpp +++ b/ext/asio/asio/detail/null_tss_ptr.hpp @@ -1,8 +1,8 @@ // -// null_tss_ptr.hpp -// ~~~~~~~~~~~~~~~~ +// detail/null_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -63,8 +61,8 @@ private: } // namespace detail } // namespace asio -#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) - #include "asio/detail/pop_options.hpp" +#endif // !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_NULL_TSS_PTR_HPP diff --git a/ext/asio/asio/detail/object_pool.hpp b/ext/asio/asio/detail/object_pool.hpp new file mode 100644 index 0000000000..dc50febb16 --- /dev/null +++ b/ext/asio/asio/detail/object_pool.hpp @@ -0,0 +1,146 @@ +// +// detail/object_pool.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_OBJECT_POOL_HPP +#define ASIO_DETAIL_OBJECT_POOL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/noncopyable.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class object_pool; + +class object_pool_access +{ +public: + template + static Object* create() + { + return new Object; + } + + template + static void destroy(Object* o) + { + delete o; + } + + template + static Object*& next(Object* o) + { + return o->next_; + } + + template + static Object*& prev(Object* o) + { + return o->prev_; + } +}; + +template +class object_pool + : private noncopyable +{ +public: + // Constructor. + object_pool() + : live_list_(0), + free_list_(0) + { + } + + // Destructor destroys all objects. + ~object_pool() + { + destroy_list(live_list_); + destroy_list(free_list_); + } + + // Get the object at the start of the live list. + Object* first() + { + return live_list_; + } + + // Allocate a new object. + Object* alloc() + { + Object* o = free_list_; + if (o) + free_list_ = object_pool_access::next(free_list_); + else + o = object_pool_access::create(); + + object_pool_access::next(o) = live_list_; + object_pool_access::prev(o) = 0; + if (live_list_) + object_pool_access::prev(live_list_) = o; + live_list_ = o; + + return o; + } + + // Free an object. Moves it to the free list. No destructors are run. + void free(Object* o) + { + if (live_list_ == o) + live_list_ = object_pool_access::next(o); + + if (object_pool_access::prev(o)) + { + object_pool_access::next(object_pool_access::prev(o)) + = object_pool_access::next(o); + } + + if (object_pool_access::next(o)) + { + object_pool_access::prev(object_pool_access::next(o)) + = object_pool_access::prev(o); + } + + object_pool_access::next(o) = free_list_; + object_pool_access::prev(o) = 0; + free_list_ = o; + } + +private: + // Helper function to destroy all elements in a list. + void destroy_list(Object* list) + { + while (list) + { + Object* o = list; + list = object_pool_access::next(o); + object_pool_access::destroy(o); + } + } + + // The list of live objects. + Object* live_list_; + + // The free list. + Object* free_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_OBJECT_POOL_HPP diff --git a/ext/asio/asio/detail/old_win_sdk_compat.hpp b/ext/asio/asio/detail/old_win_sdk_compat.hpp index 70e5916d6a..fb755791d3 100644 --- a/ext/asio/asio/detail/old_win_sdk_compat.hpp +++ b/ext/asio/asio/detail/old_win_sdk_compat.hpp @@ -1,8 +1,8 @@ // -// old_win_sdk_compat.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/old_win_sdk_compat.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -36,6 +32,8 @@ // a recent (i.e. Vista or later) SDK, as the SDK does not provide IPv6 support // in that case. +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -321,6 +319,8 @@ inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a) } // namespace detail } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_OLD_WIN_SDK) // Even newer Platform SDKs that support IPv6 may not define IPV6_V6ONLY. @@ -335,6 +335,4 @@ inline int IN6_IS_ADDR_MC_GLOBAL(const in6_addr_emulation* a) #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_OLD_WIN_SDK_COMPAT_HPP diff --git a/ext/asio/asio/detail/op_queue.hpp b/ext/asio/asio/detail/op_queue.hpp index ccf8b9a5c2..38278eafac 100644 --- a/ext/asio/asio/detail/op_queue.hpp +++ b/ext/asio/asio/detail/op_queue.hpp @@ -1,8 +1,8 @@ // -// op_queue.hpp -// ~~~~~~~~~~~~ +// detail/op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/operation.hpp b/ext/asio/asio/detail/operation.hpp index 6aba361218..3435c057db 100644 --- a/ext/asio/asio/detail/operation.hpp +++ b/ext/asio/asio/detail/operation.hpp @@ -1,8 +1,8 @@ // -// operation.hpp -// ~~~~~~~~~~~~~ +// detail/operation.hpp +// ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_operation.hpp" #else -# include "asio/detail/reactor_fwd.hpp" # include "asio/detail/task_io_service_operation.hpp" #endif @@ -32,12 +29,10 @@ namespace detail { #if defined(ASIO_HAS_IOCP) typedef win_iocp_operation operation; #else -typedef task_io_service_operation operation; +typedef task_io_service_operation operation; #endif } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_OPERATION_HPP diff --git a/ext/asio/asio/detail/pipe_select_interrupter.hpp b/ext/asio/asio/detail/pipe_select_interrupter.hpp index 74695994dc..bf3942684c 100644 --- a/ext/asio/asio/detail/pipe_select_interrupter.hpp +++ b/ext/asio/asio/detail/pipe_select_interrupter.hpp @@ -1,8 +1,8 @@ // -// pipe_select_interrupter.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/pipe_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,22 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" + +#if !defined(BOOST_WINDOWS) +#if !defined(__CYGWIN__) +#if !defined(__SYMBIAN32__) +#if !defined(ASIO_HAS_EVENTFD) #include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/socket_types.hpp" namespace asio { namespace detail { @@ -39,57 +31,16 @@ class pipe_select_interrupter { public: // Constructor. - pipe_select_interrupter() - { - int pipe_fds[2]; - if (pipe(pipe_fds) == 0) - { - read_descriptor_ = pipe_fds[0]; - ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); - write_descriptor_ = pipe_fds[1]; - ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK); - } - else - { - asio::error_code ec(errno, - asio::error::get_system_category()); - asio::system_error e(ec, "pipe_select_interrupter"); - boost::throw_exception(e); - } - } + ASIO_DECL pipe_select_interrupter(); // Destructor. - ~pipe_select_interrupter() - { - if (read_descriptor_ != -1) - ::close(read_descriptor_); - if (write_descriptor_ != -1) - ::close(write_descriptor_); - } + ASIO_DECL ~pipe_select_interrupter(); // Interrupt the select call. - void interrupt() - { - char byte = 0; - int result = ::write(write_descriptor_, &byte, 1); - (void)result; - } + ASIO_DECL void interrupt(); // Reset the select interrupt. Returns true if the call was interrupted. - bool reset() - { - for (;;) - { - char data[1024]; - int bytes_read = ::read(read_descriptor_, data, sizeof(data)); - if (bytes_read < 0 && errno == EINTR) - continue; - bool was_interrupted = (bytes_read > 0); - while (bytes_read == sizeof(data)) - bytes_read = ::read(read_descriptor_, data, sizeof(data)); - return was_interrupted; - } - } + ASIO_DECL bool reset(); // Get the read descriptor to be passed to select. int read_descriptor() const @@ -113,8 +64,15 @@ private: } // namespace detail } // namespace asio -#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/pipe_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_HAS_EVENTFD) +#endif // !defined(__SYMBIAN32__) +#endif // !defined(__CYGWIN__) +#endif // !defined(BOOST_WINDOWS) + #endif // ASIO_DETAIL_PIPE_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/asio/detail/pop_options.hpp b/ext/asio/asio/detail/pop_options.hpp index a26b2039c0..4e193363ab 100644 --- a/ext/asio/asio/detail/pop_options.hpp +++ b/ext/asio/asio/detail/pop_options.hpp @@ -1,8 +1,8 @@ // -// pop_options.hpp -// ~~~~~~~~~~~~~~~ +// detail/pop_options.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -31,6 +31,16 @@ # pragma pack (pop) # endif +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if defined(ASIO_OBJC_WORKAROUND) +# undef Protocol +# undef id +# undef ASIO_OBJC_WORKAROUND +# endif +# endif +# endif + #elif defined(__KCC) // Kai C++ diff --git a/ext/asio/asio/detail/posix_event.hpp b/ext/asio/asio/detail/posix_event.hpp index 49c15aa5a0..d0ec8b0012 100644 --- a/ext/asio/asio/detail/posix_event.hpp +++ b/ext/asio/asio/detail/posix_event.hpp @@ -1,8 +1,8 @@ // -// posix_event.hpp -// ~~~~~~~~~~~~~~~ +// detail/posix_event.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,24 +15,16 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) -#if defined(BOOST_HAS_PTHREADS) - -#include "asio/detail/push_options.hpp" #include -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -41,19 +33,7 @@ class posix_event { public: // Constructor. - posix_event() - : signalled_(false) - { - int error = ::pthread_cond_init(&cond_, 0); - if (error != 0) - { - asio::system_error e( - asio::error_code(error, - asio::error::get_system_category()), - "event"); - boost::throw_exception(e); - } - } + ASIO_DECL posix_event(); // Destructor. ~posix_event() @@ -107,8 +87,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_HAS_PTHREADS) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_POSIX_EVENT_HPP diff --git a/ext/asio/asio/detail/posix_fd_set_adapter.hpp b/ext/asio/asio/detail/posix_fd_set_adapter.hpp index 17ef269cee..92d6bba7f2 100644 --- a/ext/asio/asio/detail/posix_fd_set_adapter.hpp +++ b/ext/asio/asio/detail/posix_fd_set_adapter.hpp @@ -1,8 +1,8 @@ // -// posix_fd_set_adapter.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/posix_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/detail/socket_types.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#include +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -74,8 +73,8 @@ private: } // namespace detail } // namespace asio -#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + #endif // ASIO_DETAIL_POSIX_FD_SET_ADAPTER_HPP diff --git a/ext/asio/asio/detail/posix_mutex.hpp b/ext/asio/asio/detail/posix_mutex.hpp index 230b83a356..bcdf6b27d4 100644 --- a/ext/asio/asio/detail/posix_mutex.hpp +++ b/ext/asio/asio/detail/posix_mutex.hpp @@ -1,8 +1,8 @@ // -// posix_mutex.hpp -// ~~~~~~~~~~~~~~~ +// detail/posix_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,24 +15,16 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) -#if defined(BOOST_HAS_PTHREADS) - -#include "asio/detail/push_options.hpp" -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/scoped_lock.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -45,18 +37,7 @@ public: typedef asio::detail::scoped_lock scoped_lock; // Constructor. - posix_mutex() - { - int error = ::pthread_mutex_init(&mutex_, 0); - if (error != 0) - { - asio::system_error e( - asio::error_code(error, - asio::error::get_system_category()), - "mutex"); - boost::throw_exception(e); - } - } + ASIO_DECL posix_mutex(); // Destructor. ~posix_mutex() @@ -84,8 +65,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_HAS_PTHREADS) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_POSIX_MUTEX_HPP diff --git a/ext/asio/asio/detail/posix_signal_blocker.hpp b/ext/asio/asio/detail/posix_signal_blocker.hpp index 135ca41b7e..d41f128476 100644 --- a/ext/asio/asio/detail/posix_signal_blocker.hpp +++ b/ext/asio/asio/detail/posix_signal_blocker.hpp @@ -1,8 +1,8 @@ // -// posix_signal_blocker.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/posix_signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,22 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) -#if defined(BOOST_HAS_PTHREADS) - -#include "asio/detail/push_options.hpp" #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -83,8 +78,8 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_HAS_PTHREADS) - #include "asio/detail/pop_options.hpp" +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_POSIX_SIGNAL_BLOCKER_HPP diff --git a/ext/asio/asio/detail/posix_thread.hpp b/ext/asio/asio/detail/posix_thread.hpp index e0fea75129..743417cd62 100644 --- a/ext/asio/asio/detail/posix_thread.hpp +++ b/ext/asio/asio/detail/posix_thread.hpp @@ -1,8 +1,8 @@ // -// posix_thread.hpp -// ~~~~~~~~~~~~~~~~ +// detail/posix_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,28 +15,22 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) -#if defined(BOOST_HAS_PTHREADS) - -#include "asio/detail/push_options.hpp" -#include -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { -extern "C" void* asio_detail_posix_thread_function(void* arg); +extern "C" +{ + ASIO_DECL void* asio_detail_posix_thread_function(void* arg); +} class posix_thread : private noncopyable @@ -47,36 +41,14 @@ public: posix_thread(Function f) : joined_(false) { - std::auto_ptr arg(new func(f)); - int error = ::pthread_create(&thread_, 0, - asio_detail_posix_thread_function, arg.get()); - if (error != 0) - { - asio::system_error e( - asio::error_code(error, - asio::error::get_system_category()), - "thread"); - boost::throw_exception(e); - } - arg.release(); + start_thread(new func(f)); } // Destructor. - ~posix_thread() - { - if (!joined_) - ::pthread_detach(thread_); - } + ASIO_DECL ~posix_thread(); // Wait for the thread to exit. - void join() - { - if (!joined_) - { - ::pthread_join(thread_, 0); - joined_ = true; - } - } + ASIO_DECL void join(); private: friend void* asio_detail_posix_thread_function(void* arg); @@ -88,6 +60,12 @@ private: virtual void run() = 0; }; + struct auto_func_base_ptr + { + func_base* ptr; + ~auto_func_base_ptr() { delete ptr; } + }; + template class func : public func_base @@ -107,23 +85,21 @@ private: Function f_; }; + ASIO_DECL void start_thread(func_base* arg); + ::pthread_t thread_; bool joined_; }; -inline void* asio_detail_posix_thread_function(void* arg) -{ - std::auto_ptr f( - static_cast(arg)); - f->run(); - return 0; -} - } // namespace detail } // namespace asio -#endif // defined(BOOST_HAS_PTHREADS) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_thread.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_POSIX_THREAD_HPP diff --git a/ext/asio/asio/detail/posix_tss_ptr.hpp b/ext/asio/asio/detail/posix_tss_ptr.hpp index 3b4ba07b44..30d9705e7a 100644 --- a/ext/asio/asio/detail/posix_tss_ptr.hpp +++ b/ext/asio/asio/detail/posix_tss_ptr.hpp @@ -1,8 +1,8 @@ // -// posix_tss_ptr.hpp -// ~~~~~~~~~~~~~~~~~ +// detail/posix_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,26 +15,21 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#if defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) -#if defined(BOOST_HAS_PTHREADS) - -#include "asio/detail/push_options.hpp" -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { +// Helper function to create thread-specific storage. +ASIO_DECL void posix_tss_ptr_create(pthread_key_t& key); + template class posix_tss_ptr : private noncopyable @@ -43,15 +38,7 @@ public: // Constructor. posix_tss_ptr() { - int error = ::pthread_key_create(&tss_key_, 0); - if (error != 0) - { - asio::system_error e( - asio::error_code(error, - asio::error::get_system_category()), - "tss"); - boost::throw_exception(e); - } + posix_tss_ptr_create(tss_key_); } // Destructor. @@ -76,13 +63,18 @@ private: // Thread-specific storage to allow unlocked access to determine whether a // thread is a member of the pool. pthread_key_t tss_key_; + }; } // namespace detail } // namespace asio -#endif // defined(BOOST_HAS_PTHREADS) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/posix_tss_ptr.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_HAS_PTHREADS) && !defined(ASIO_DISABLE_THREADS) + #endif // ASIO_DETAIL_POSIX_TSS_PTR_HPP diff --git a/ext/asio/asio/detail/push_options.hpp b/ext/asio/asio/detail/push_options.hpp index cb0e90242e..ebb3276052 100644 --- a/ext/asio/asio/detail/push_options.hpp +++ b/ext/asio/asio/detail/push_options.hpp @@ -1,8 +1,8 @@ // -// push_options.hpp -// ~~~~~~~~~~~~~~~~ +// detail/push_options.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -31,6 +31,18 @@ # pragma pack (push, 8) # endif +# if defined(__OBJC__) +# if !defined(__APPLE_CC__) || (__APPLE_CC__ <= 1) +# if !defined(ASIO_DISABLE_OBJC_WORKAROUND) +# if !defined(Protocol) && !defined(id) +# define Protocol cpp_Protocol +# define id cpp_id +# define ASIO_OBJC_WORKAROUND +# endif +# endif +# endif +# endif + #elif defined(__KCC) // Kai C++ diff --git a/ext/asio/asio/detail/reactive_descriptor_service.hpp b/ext/asio/asio/detail/reactive_descriptor_service.hpp index 7ad368d7de..f7e8bf53a5 100644 --- a/ext/asio/asio/detail/reactive_descriptor_service.hpp +++ b/ext/asio/asio/detail/reactive_descriptor_service.hpp @@ -1,8 +1,8 @@ // -// reactive_descriptor_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/reactive_descriptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,21 +15,24 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + +#include #include "asio/buffer.hpp" -#include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/descriptor_ops.hpp" +#include "asio/detail/descriptor_read_op.hpp" +#include "asio/detail/descriptor_write_op.hpp" #include "asio/detail/fenced_block.hpp" #include "asio/detail/noncopyable.hpp" -#include "asio/detail/null_buffers_op.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" #include "asio/detail/reactor.hpp" -#include "asio/detail/reactor_op.hpp" -#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -48,7 +51,7 @@ public: // Default constructor. implementation_type() : descriptor_(-1), - flags_(0) + state_(0) { } @@ -59,94 +62,29 @@ public: // The native descriptor representation. int descriptor_; - enum - { - // The user wants a non-blocking descriptor. - user_set_non_blocking = 1, - - // The descriptor has been set non-blocking. - internal_non_blocking = 2, - - // Helper "flag" used to determine whether the descriptor is non-blocking. - non_blocking = user_set_non_blocking | internal_non_blocking - }; - - // Flags indicating the current state of the descriptor. - unsigned char flags_; + // The current state of the descriptor. + descriptor_ops::state_type state_; // Per-descriptor data used by the reactor. reactor::per_descriptor_data reactor_data_; }; - // The maximum number of buffers to support in a single operation. - enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len }; - // Constructor. - reactive_descriptor_service(asio::io_service& io_service) - : io_service_impl_(asio::use_service(io_service)), - reactor_(asio::use_service(io_service)) - { - reactor_.init_task(); - } + ASIO_DECL reactive_descriptor_service( + asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - } + ASIO_DECL void shutdown_service(); // Construct a new descriptor implementation. - void construct(implementation_type& impl) - { - impl.descriptor_ = -1; - impl.flags_ = 0; - } + ASIO_DECL void construct(implementation_type& impl); // Destroy a descriptor implementation. - void destroy(implementation_type& impl) - { - if (impl.descriptor_ != -1) - { - reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); - - if (impl.flags_ & implementation_type::internal_non_blocking) - { - ioctl_arg_type non_blocking = 0; - asio::error_code ignored_ec; - descriptor_ops::ioctl(impl.descriptor_, - FIONBIO, &non_blocking, ignored_ec); - impl.flags_ &= ~implementation_type::internal_non_blocking; - } - - asio::error_code ignored_ec; - descriptor_ops::close(impl.descriptor_, ignored_ec); - - impl.descriptor_ = -1; - } - } + ASIO_DECL void destroy(implementation_type& impl); // Assign a native descriptor to a descriptor implementation. - asio::error_code assign(implementation_type& impl, - const native_type& native_descriptor, asio::error_code& ec) - { - if (is_open(impl)) - { - ec = asio::error::already_open; - return ec; - } - - if (int err = reactor_.register_descriptor( - native_descriptor, impl.reactor_data_)) - { - ec = asio::error_code(err, - asio::error::get_system_category()); - return ec; - } - - impl.descriptor_ = native_descriptor; - impl.flags_ = 0; - ec = asio::error_code(); - return ec; - } + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_type& native_descriptor, asio::error_code& ec); // Determine whether the descriptor is open. bool is_open(const implementation_type& impl) const @@ -155,31 +93,8 @@ public: } // Destroy a descriptor implementation. - asio::error_code close(implementation_type& impl, - asio::error_code& ec) - { - if (is_open(impl)) - { - reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_); - - if (impl.flags_ & implementation_type::internal_non_blocking) - { - ioctl_arg_type non_blocking = 0; - asio::error_code ignored_ec; - descriptor_ops::ioctl(impl.descriptor_, - FIONBIO, &non_blocking, ignored_ec); - impl.flags_ &= ~implementation_type::internal_non_blocking; - } - - if (descriptor_ops::close(impl.descriptor_, ec) == -1) - return ec; - - impl.descriptor_ = -1; - } - - ec = asio::error_code(); - return ec; - } + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); // Get the native descriptor representation. native_type native(const implementation_type& impl) const @@ -188,56 +103,16 @@ public: } // Cancel all operations associated with the descriptor. - asio::error_code cancel(implementation_type& impl, - asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_); - ec = asio::error_code(); - return ec; - } + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); // Perform an IO control command on the descriptor. template asio::error_code io_control(implementation_type& impl, IO_Control_Command& command, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - descriptor_ops::ioctl(impl.descriptor_, command.name(), - static_cast(command.data()), ec); - - // When updating the non-blocking mode we always perform the ioctl syscall, - // even if the flags would otherwise indicate that the descriptor is - // already in the correct state. This ensures that the underlying - // descriptor is put into the state that has been requested by the user. If - // the ioctl syscall was successful then we need to update the flags to - // match. - if (!ec && command.name() == static_cast(FIONBIO)) - { - if (*static_cast(command.data())) - { - impl.flags_ |= implementation_type::user_set_non_blocking; - } - else - { - // Clearing the non-blocking mode always overrides any internally-set - // non-blocking flag. Any subsequent asynchronous operations will need - // to re-enable non-blocking I/O. - impl.flags_ &= ~(implementation_type::user_set_non_blocking - | implementation_type::internal_non_blocking); - } - } - + descriptor_ops::ioctl(impl.descriptor_, impl.state_, + command.name(), static_cast(command.data()), ec); return ec; } @@ -246,148 +121,23 @@ public: size_t write_some(implementation_type& impl, const ConstBufferSequence& buffers, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - buffer_sequence_adapter bufs(buffers); - // A request to read_some 0 bytes on a stream is a no-op. - if (bufs.all_empty()) - { - ec = asio::error_code(); - return 0; - } - - // Send the data. - for (;;) - { - // Try to complete the operation without blocking. - int bytes_sent = descriptor_ops::gather_write( - impl.descriptor_, bufs.buffers(), bufs.count(), ec); - - // Check if operation succeeded. - if (bytes_sent >= 0) - return bytes_sent; - - // Operation failed. - if ((impl.flags_ & implementation_type::user_set_non_blocking) - || (ec != asio::error::would_block - && ec != asio::error::try_again)) - return 0; - - // Wait for descriptor to become ready. - if (descriptor_ops::poll_write(impl.descriptor_, ec) < 0) - return 0; - } + return descriptor_ops::sync_write(impl.descriptor_, impl.state_, + bufs.buffers(), bufs.count(), bufs.all_empty(), ec); } // Wait until data can be written without blocking. size_t write_some(implementation_type& impl, const null_buffers&, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - // Wait for descriptor to become ready. descriptor_ops::poll_write(impl.descriptor_, ec); return 0; } - template - class write_op_base : public reactor_op - { - public: - write_op_base(int descriptor, - const ConstBufferSequence& buffers, func_type complete_func) - : reactor_op(&write_op_base::do_perform, complete_func), - descriptor_(descriptor), - buffers_(buffers) - { - } - - static bool do_perform(reactor_op* base) - { - write_op_base* o(static_cast(base)); - - buffer_sequence_adapter bufs(o->buffers_); - - for (;;) - { - // Write the data. - asio::error_code ec; - int bytes = descriptor_ops::gather_write( - o->descriptor_, bufs.buffers(), bufs.count(), ec); - - // Retry operation if interrupted by signal. - if (ec == asio::error::interrupted) - continue; - - // Check if we need to run the operation again. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - return false; - - o->ec_ = ec; - o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); - return true; - } - } - - private: - int descriptor_; - ConstBufferSequence buffers_; - }; - - template - class write_op : public write_op_base - { - public: - write_op(int descriptor, - const ConstBufferSequence& buffers, Handler handler) - : write_op_base( - descriptor, buffers, &write_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - write_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->bytes_transferred_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous write. The data being sent must be valid for the // lifetime of the asynchronous operation. template @@ -395,15 +145,16 @@ public: const ConstBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef write_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, impl.descriptor_, buffers, handler); + typedef descriptor_write_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.descriptor_, buffers, handler); - start_op(impl, reactor::write_op, ptr.get(), true, + start_op(impl, reactor::write_op, p.p, true, buffer_sequence_adapter::all_empty(buffers)); - ptr.release(); + p.v = p.p = 0; } // Start an asynchronous wait until data can be written without blocking. @@ -412,13 +163,14 @@ public: const null_buffers&, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef reactive_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); - start_op(impl, reactor::write_op, ptr.get(), false, false); - ptr.release(); + start_op(impl, reactor::write_op, p.p, false, false); + p.v = p.p = 0; } // Read some data from the stream. Returns the number of bytes read. @@ -426,157 +178,23 @@ public: size_t read_some(implementation_type& impl, const MutableBufferSequence& buffers, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - buffer_sequence_adapter bufs(buffers); - // A request to read_some 0 bytes on a stream is a no-op. - if (bufs.all_empty()) - { - ec = asio::error_code(); - return 0; - } - - // Read some data. - for (;;) - { - // Try to complete the operation without blocking. - int bytes_read = descriptor_ops::scatter_read( - impl.descriptor_, bufs.buffers(), bufs.count(), ec); - - // Check if operation succeeded. - if (bytes_read > 0) - return bytes_read; - - // Check for EOF. - if (bytes_read == 0) - { - ec = asio::error::eof; - return 0; - } - - // Operation failed. - if ((impl.flags_ & implementation_type::user_set_non_blocking) - || (ec != asio::error::would_block - && ec != asio::error::try_again)) - return 0; - - // Wait for descriptor to become ready. - if (descriptor_ops::poll_read(impl.descriptor_, ec) < 0) - return 0; - } + return descriptor_ops::sync_read(impl.descriptor_, impl.state_, + bufs.buffers(), bufs.count(), bufs.all_empty(), ec); } // Wait until data can be read without blocking. size_t read_some(implementation_type& impl, const null_buffers&, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - // Wait for descriptor to become ready. descriptor_ops::poll_read(impl.descriptor_, ec); return 0; } - template - class read_op_base : public reactor_op - { - public: - read_op_base(int descriptor, - const MutableBufferSequence& buffers, func_type complete_func) - : reactor_op(&read_op_base::do_perform, complete_func), - descriptor_(descriptor), - buffers_(buffers) - { - } - - static bool do_perform(reactor_op* base) - { - read_op_base* o(static_cast(base)); - - buffer_sequence_adapter bufs(o->buffers_); - - for (;;) - { - // Read some data. - asio::error_code ec; - int bytes = descriptor_ops::scatter_read( - o->descriptor_, bufs.buffers(), bufs.count(), ec); - if (bytes == 0) - ec = asio::error::eof; - - // Retry operation if interrupted by signal. - if (ec == asio::error::interrupted) - continue; - - // Check if we need to run the operation again. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - return false; - - o->ec_ = ec; - o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); - return true; - } - } - - private: - int descriptor_; - MutableBufferSequence buffers_; - }; - - template - class read_op : public read_op_base - { - public: - read_op(int descriptor, - const MutableBufferSequence& buffers, Handler handler) - : read_op_base( - descriptor, buffers, &read_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - read_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->bytes_transferred_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous read. The buffer for the data being read must be // valid for the lifetime of the asynchronous operation. template @@ -584,16 +202,16 @@ public: const MutableBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef read_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, - impl.descriptor_, buffers, handler); + typedef descriptor_read_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.descriptor_, buffers, handler); - start_op(impl, reactor::read_op, ptr.get(), true, + start_op(impl, reactor::read_op, p.p, true, buffer_sequence_adapter::all_empty(buffers)); - ptr.release(); + p.v = p.p = 0; } // Wait until data can be read without blocking. @@ -602,57 +220,20 @@ public: const null_buffers&, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef reactive_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); - start_op(impl, reactor::read_op, ptr.get(), false, false); - ptr.release(); + start_op(impl, reactor::read_op, p.p, false, false); + p.v = p.p = 0; } private: // Start the asynchronous operation. - void start_op(implementation_type& impl, int op_type, - reactor_op* op, bool non_blocking, bool noop) - { - if (!noop) - { - if (is_open(impl)) - { - if (is_non_blocking(impl) || set_non_blocking(impl, op->ec_)) - { - reactor_.start_op(op_type, impl.descriptor_, - impl.reactor_data_, op, non_blocking); - return; - } - } - else - op->ec_ = asio::error::bad_descriptor; - } - - io_service_impl_.post_immediate_completion(op); - } - - // Determine whether the descriptor has been set non-blocking. - bool is_non_blocking(implementation_type& impl) const - { - return (impl.flags_ & implementation_type::non_blocking); - } - - // Set the internal non-blocking flag. - bool set_non_blocking(implementation_type& impl, - asio::error_code& ec) - { - ioctl_arg_type non_blocking = 1; - if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec)) - return false; - impl.flags_ |= implementation_type::internal_non_blocking; - return true; - } - - // The io_service implementation used to post completions. - io_service_impl& io_service_impl_; + ASIO_DECL void start_op(implementation_type& impl, int op_type, + reactor_op* op, bool non_blocking, bool noop); // The selector that performs event demultiplexing for the service. reactor& reactor_; @@ -661,8 +242,12 @@ private: } // namespace detail } // namespace asio -#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_descriptor_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + #endif // ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP diff --git a/ext/asio/asio/detail/reactive_null_buffers_op.hpp b/ext/asio/asio/detail/reactive_null_buffers_op.hpp new file mode 100644 index 0000000000..0c83c612ab --- /dev/null +++ b/ext/asio/asio/detail/reactive_null_buffers_op.hpp @@ -0,0 +1,83 @@ +// +// detail/reactive_null_buffers_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP +#define ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/reactor_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_null_buffers_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_null_buffers_op); + + reactive_null_buffers_op(Handler handler) + : reactor_op(&reactive_null_buffers_op::do_perform, + &reactive_null_buffers_op::do_complete), + handler_(handler) + { + } + + static bool do_perform(reactor_op*) + { + return true; + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_null_buffers_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_NULL_BUFFERS_OP_HPP diff --git a/ext/asio/asio/detail/reactive_serial_port_service.hpp b/ext/asio/asio/detail/reactive_serial_port_service.hpp index 186460fef6..556ea06d87 100644 --- a/ext/asio/asio/detail/reactive_serial_port_service.hpp +++ b/ext/asio/asio/detail/reactive_serial_port_service.hpp @@ -1,8 +1,8 @@ // -// reactive_serial_port_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/reactive_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,23 +16,20 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -#include "asio/detail/push_options.hpp" -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/serial_port_base.hpp" - -#if defined(ASIO_HAS_SERIAL_PORT) \ - && !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - #include "asio/error.hpp" #include "asio/io_service.hpp" +#include "asio/serial_port_base.hpp" #include "asio/detail/descriptor_ops.hpp" #include "asio/detail/reactive_descriptor_service.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -46,119 +43,55 @@ public: // The implementation type of the serial port. typedef reactive_descriptor_service::implementation_type implementation_type; - reactive_serial_port_service(asio::io_service& io_service) - : descriptor_service_(io_service) - { - } + ASIO_DECL reactive_serial_port_service( + asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - descriptor_service_.shutdown_service(); - } + ASIO_DECL void shutdown_service(); - // Construct a new handle implementation. + // Construct a new serial port implementation. void construct(implementation_type& impl) { descriptor_service_.construct(impl); } - // Destroy a handle implementation. + // Destroy a serial port implementation. void destroy(implementation_type& impl) { descriptor_service_.destroy(impl); } // Open the serial port using the specified device name. - asio::error_code open(implementation_type& impl, - const std::string& device, asio::error_code& ec) - { - if (is_open(impl)) - { - ec = asio::error::already_open; - return ec; - } + ASIO_DECL asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec); - int fd = descriptor_ops::open(device.c_str(), - O_RDWR | O_NONBLOCK | O_NOCTTY, ec); - if (fd < 0) - return ec; - - int s = descriptor_ops::fcntl(fd, F_GETFL, ec); - if (s >= 0) - s = descriptor_ops::fcntl(fd, F_SETFL, s | O_NONBLOCK, ec); - if (s < 0) - { - asio::error_code ignored_ec; - descriptor_ops::close(fd, ignored_ec); - return ec; - } - - // Set up default serial port options. - termios ios; - descriptor_ops::clear_error(ec); - s = descriptor_ops::error_wrapper(::tcgetattr(fd, &ios), ec); - if (s >= 0) - { -#if defined(_BSD_SOURCE) - ::cfmakeraw(&ios); -#else - ios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK - | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - ios.c_oflag &= ~OPOST; - ios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - ios.c_cflag &= ~(CSIZE | PARENB); - ios.c_cflag |= CS8; -#endif - ios.c_iflag |= IGNPAR; - ios.c_cflag |= CREAD | CLOCAL; - descriptor_ops::clear_error(ec); - s = descriptor_ops::error_wrapper(::tcsetattr(fd, TCSANOW, &ios), ec); - } - if (s < 0) - { - asio::error_code ignored_ec; - descriptor_ops::close(fd, ignored_ec); - return ec; - } - - // We're done. Take ownership of the serial port descriptor. - if (descriptor_service_.assign(impl, fd, ec)) - { - asio::error_code ignored_ec; - descriptor_ops::close(fd, ignored_ec); - } - - return ec; - } - - // Assign a native handle to a handle implementation. + // Assign a native descriptor to a serial port implementation. asio::error_code assign(implementation_type& impl, const native_type& native_descriptor, asio::error_code& ec) { return descriptor_service_.assign(impl, native_descriptor, ec); } - // Determine whether the handle is open. + // Determine whether the serial port is open. bool is_open(const implementation_type& impl) const { return descriptor_service_.is_open(impl); } - // Destroy a handle implementation. + // Destroy a serial port implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return descriptor_service_.close(impl, ec); } - // Get the native handle representation. + // Get the native serial port representation. native_type native(implementation_type& impl) { return descriptor_service_.native(impl); } - // Cancel all operations associated with the handle. + // Cancel all operations associated with the serial port. asio::error_code cancel(implementation_type& impl, asio::error_code& ec) { @@ -170,20 +103,9 @@ public: asio::error_code set_option(implementation_type& impl, const SettableSerialPortOption& option, asio::error_code& ec) { - termios ios; - descriptor_ops::clear_error(ec); - descriptor_ops::error_wrapper(::tcgetattr( - descriptor_service_.native(impl), &ios), ec); - if (ec) - return ec; - - if (option.store(ios, ec)) - return ec; - - descriptor_ops::clear_error(ec); - descriptor_ops::error_wrapper(::tcsetattr( - descriptor_service_.native(impl), TCSANOW, &ios), ec); - return ec; + return do_set_option(impl, + &reactive_serial_port_service::store_option, + &option, ec); } // Get an option from the serial port. @@ -191,21 +113,16 @@ public: asio::error_code get_option(const implementation_type& impl, GettableSerialPortOption& option, asio::error_code& ec) const { - termios ios; - descriptor_ops::clear_error(ec); - descriptor_ops::error_wrapper(::tcgetattr( - descriptor_service_.native(impl), &ios), ec); - if (ec) - return ec; - - return option.load(ios, ec); + return do_get_option(impl, + &reactive_serial_port_service::load_option, + &option, ec); } // Send a break sequence to the serial port. asio::error_code send_break(implementation_type& impl, asio::error_code& ec) { - descriptor_ops::clear_error(ec); + errno = 0; descriptor_ops::error_wrapper(::tcsendbreak( descriptor_service_.native(impl), 0), ec); return ec; @@ -246,6 +163,41 @@ public: } private: + // Function pointer type for storing a serial port option. + typedef asio::error_code (*store_function_type)( + const void*, termios&, asio::error_code&); + + // Helper function template to store a serial port option. + template + static asio::error_code store_option(const void* option, + termios& storage, asio::error_code& ec) + { + return static_cast(option)->store( + storage, ec); + } + + // Helper function to set a serial port option. + ASIO_DECL asio::error_code do_set_option( + implementation_type& impl, store_function_type store, + const void* option, asio::error_code& ec); + + // Function pointer type for loading a serial port option. + typedef asio::error_code (*load_function_type)( + void*, const termios&, asio::error_code&); + + // Helper function template to load a serial port option. + template + static asio::error_code load_option(void* option, + const termios& storage, asio::error_code& ec) + { + return static_cast(option)->load(storage, ec); + } + + // Helper function to get a serial port option. + ASIO_DECL asio::error_code do_get_option( + const implementation_type& impl, load_function_type load, + void* option, asio::error_code& ec) const; + // The implementation used for initiating asynchronous operations. reactive_descriptor_service descriptor_service_; }; @@ -253,9 +205,13 @@ private: } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_SERIAL_PORT) - // && !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_serial_port_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#endif // defined(ASIO_HAS_SERIAL_PORT) + #endif // ASIO_DETAIL_REACTIVE_SERIAL_PORT_SERVICE_HPP diff --git a/ext/asio/asio/detail/reactive_socket_accept_op.hpp b/ext/asio/asio/detail/reactive_socket_accept_op.hpp new file mode 100644 index 0000000000..76c6741180 --- /dev/null +++ b/ext/asio/asio/detail/reactive_socket_accept_op.hpp @@ -0,0 +1,131 @@ +// +// detail/reactive_socket_accept_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_accept_op_base : public reactor_op +{ +public: + reactive_socket_accept_op_base(socket_type socket, + socket_ops::state_type state, Socket& peer, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, func_type complete_func) + : reactor_op(&reactive_socket_accept_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint) + { + } + + static bool do_perform(reactor_op* base) + { + reactive_socket_accept_op_base* o( + static_cast(base)); + + std::size_t addrlen = o->peer_endpoint_ ? o->peer_endpoint_->capacity() : 0; + socket_type new_socket = invalid_socket; + bool result = socket_ops::non_blocking_accept(o->socket_, + o->state_, o->peer_endpoint_ ? o->peer_endpoint_->data() : 0, + o->peer_endpoint_ ? &addrlen : 0, o->ec_, new_socket); + + // On success, assign new connection to peer socket object. + if (new_socket >= 0) + { + socket_holder new_socket_holder(new_socket); + if (o->peer_endpoint_) + o->peer_endpoint_->resize(addrlen); + if (!o->peer_.assign(o->protocol_, new_socket, o->ec_)) + new_socket_holder.release(); + } + + return result; + } + +private: + socket_type socket_; + socket_ops::state_type state_; + Socket& peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; +}; + +template +class reactive_socket_accept_op : + public reactive_socket_accept_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_accept_op); + + reactive_socket_accept_op(socket_type socket, + socket_ops::state_type state, Socket& peer, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, Handler handler) + : reactive_socket_accept_op_base(socket, state, peer, + protocol, peer_endpoint, &reactive_socket_accept_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_accept_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_ACCEPT_OP_HPP diff --git a/ext/asio/asio/detail/reactive_socket_connect_op.hpp b/ext/asio/asio/detail/reactive_socket_connect_op.hpp new file mode 100644 index 0000000000..8267091b21 --- /dev/null +++ b/ext/asio/asio/detail/reactive_socket_connect_op.hpp @@ -0,0 +1,101 @@ +// +// detail/reactive_socket_connect_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_socket_connect_op_base : public reactor_op +{ +public: + reactive_socket_connect_op_base(socket_type socket, func_type complete_func) + : reactor_op(&reactive_socket_connect_op_base::do_perform, complete_func), + socket_(socket) + { + } + + static bool do_perform(reactor_op* base) + { + reactive_socket_connect_op_base* o( + static_cast(base)); + + return socket_ops::non_blocking_connect(o->socket_, o->ec_); + } + +private: + socket_type socket_; +}; + +template +class reactive_socket_connect_op : public reactive_socket_connect_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_connect_op); + + reactive_socket_connect_op(socket_type socket, Handler handler) + : reactive_socket_connect_op_base(socket, + &reactive_socket_connect_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_connect_op* o + (static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, o->ec_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_CONNECT_OP_HPP diff --git a/ext/asio/asio/detail/reactive_socket_recv_op.hpp b/ext/asio/asio/detail/reactive_socket_recv_op.hpp new file mode 100644 index 0000000000..91a30a849e --- /dev/null +++ b/ext/asio/asio/detail/reactive_socket_recv_op.hpp @@ -0,0 +1,118 @@ +// +// detail/reactive_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recv_op_base : public reactor_op +{ +public: + reactive_socket_recv_op_base(socket_type socket, + socket_ops::state_type state, const MutableBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_recv_op_base::do_perform, complete_func), + socket_(socket), + state_(state), + buffers_(buffers), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + reactive_socket_recv_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + return socket_ops::non_blocking_recv(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + (o->state_ & socket_ops::stream_oriented), + o->ec_, o->bytes_transferred_); + } + +private: + socket_type socket_; + socket_ops::state_type state_; + MutableBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_recv_op : + public reactive_socket_recv_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recv_op); + + reactive_socket_recv_op(socket_type socket, + socket_ops::state_type state, const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + : reactive_socket_recv_op_base(socket, state, + buffers, flags, &reactive_socket_recv_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recv_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECV_OP_HPP diff --git a/ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp b/ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp new file mode 100644 index 0000000000..121d6ecbd8 --- /dev/null +++ b/ext/asio/asio/detail/reactive_socket_recvfrom_op.hpp @@ -0,0 +1,128 @@ +// +// detail/reactive_socket_recvfrom_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_recvfrom_op_base : public reactor_op +{ +public: + reactive_socket_recvfrom_op_base(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_recvfrom_op_base::do_perform, complete_func), + socket_(socket), + protocol_type_(protocol_type), + buffers_(buffers), + sender_endpoint_(endpoint), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + reactive_socket_recvfrom_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + std::size_t addr_len = o->sender_endpoint_.capacity(); + bool result = socket_ops::non_blocking_recvfrom(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->sender_endpoint_.data(), &addr_len, + o->ec_, o->bytes_transferred_); + + if (result && !o->ec_) + o->sender_endpoint_.resize(addr_len); + + return result; + } + +private: + socket_type socket_; + int protocol_type_; + MutableBufferSequence buffers_; + Endpoint& sender_endpoint_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_recvfrom_op : + public reactive_socket_recvfrom_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_recvfrom_op); + + reactive_socket_recvfrom_op(socket_type socket, int protocol_type, + const MutableBufferSequence& buffers, Endpoint& endpoint, + socket_base::message_flags flags, Handler handler) + : reactive_socket_recvfrom_op_base( + socket, protocol_type, buffers, endpoint, flags, + &reactive_socket_recvfrom_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_recvfrom_op* o( + static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_RECVFROM_OP_HPP diff --git a/ext/asio/asio/detail/reactive_socket_send_op.hpp b/ext/asio/asio/detail/reactive_socket_send_op.hpp new file mode 100644 index 0000000000..9a68dacccb --- /dev/null +++ b/ext/asio/asio/detail/reactive_socket_send_op.hpp @@ -0,0 +1,115 @@ +// +// detail/reactive_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_send_op_base : public reactor_op +{ +public: + reactive_socket_send_op_base(socket_type socket, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_send_op_base::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + reactive_socket_send_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + return socket_ops::non_blocking_send(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->ec_, o->bytes_transferred_); + } + +private: + socket_type socket_; + ConstBufferSequence buffers_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_send_op : + public reactive_socket_send_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_send_op); + + reactive_socket_send_op(socket_type socket, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + : reactive_socket_send_op_base(socket, + buffers, flags, &reactive_socket_send_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_send_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SEND_OP_HPP diff --git a/ext/asio/asio/detail/reactive_socket_sendto_op.hpp b/ext/asio/asio/detail/reactive_socket_sendto_op.hpp new file mode 100644 index 0000000000..e466a2591e --- /dev/null +++ b/ext/asio/asio/detail/reactive_socket_sendto_op.hpp @@ -0,0 +1,118 @@ +// +// detail/reactive_socket_sendto_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class reactive_socket_sendto_op_base : public reactor_op +{ +public: + reactive_socket_sendto_op_base(socket_type socket, + const ConstBufferSequence& buffers, const Endpoint& endpoint, + socket_base::message_flags flags, func_type complete_func) + : reactor_op(&reactive_socket_sendto_op_base::do_perform, complete_func), + socket_(socket), + buffers_(buffers), + destination_(endpoint), + flags_(flags) + { + } + + static bool do_perform(reactor_op* base) + { + reactive_socket_sendto_op_base* o( + static_cast(base)); + + buffer_sequence_adapter bufs(o->buffers_); + + return socket_ops::non_blocking_sendto(o->socket_, + bufs.buffers(), bufs.count(), o->flags_, + o->destination_.data(), o->destination_.size(), + o->ec_, o->bytes_transferred_); + } + +private: + socket_type socket_; + ConstBufferSequence buffers_; + Endpoint destination_; + socket_base::message_flags flags_; +}; + +template +class reactive_socket_sendto_op : + public reactive_socket_sendto_op_base +{ +public: + ASIO_DEFINE_HANDLER_PTR(reactive_socket_sendto_op); + + reactive_socket_sendto_op(socket_type socket, + const ConstBufferSequence& buffers, const Endpoint& endpoint, + socket_base::message_flags flags, Handler handler) + : reactive_socket_sendto_op_base(socket, + buffers, endpoint, flags, &reactive_socket_sendto_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + reactive_socket_sendto_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->bytes_transferred_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SENDTO_OP_HPP diff --git a/ext/asio/asio/detail/reactive_socket_service.hpp b/ext/asio/asio/detail/reactive_socket_service.hpp index 20bd512ef6..daa9f607ad 100644 --- a/ext/asio/asio/detail/reactive_socket_service.hpp +++ b/ext/asio/asio/detail/reactive_socket_service.hpp @@ -1,8 +1,8 @@ // -// reactive_socket_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/reactive_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,28 +15,37 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#if !defined(ASIO_HAS_IOCP) + +#include #include "asio/buffer.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/socket_base.hpp" -#include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" -#include "asio/detail/fenced_block.hpp" #include "asio/detail/noncopyable.hpp" -#include "asio/detail/null_buffers_op.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_socket_accept_op.hpp" +#include "asio/detail/reactive_socket_connect_op.hpp" +#include "asio/detail/reactive_socket_recvfrom_op.hpp" +#include "asio/detail/reactive_socket_sendto_op.hpp" +#include "asio/detail/reactive_socket_service_base.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { template -class reactive_socket_service +class reactive_socket_service : + public reactive_socket_service_base { public: // The protocol type. @@ -49,132 +58,32 @@ public: typedef socket_type native_type; // The implementation type of the socket. - class implementation_type - : private asio::detail::noncopyable + struct implementation_type : + reactive_socket_service_base::base_implementation_type { - public: // Default constructor. implementation_type() - : socket_(invalid_socket), - flags_(0), - protocol_(endpoint_type().protocol()) + : protocol_(endpoint_type().protocol()) { } - private: - // Only this service will have access to the internal values. - friend class reactive_socket_service; - - // The native socket representation. - socket_type socket_; - - enum - { - // The user wants a non-blocking socket. - user_set_non_blocking = 1, - - // The implementation wants a non-blocking socket (in order to be able to - // perform asynchronous read and write operations). - internal_non_blocking = 2, - - // Helper "flag" used to determine whether the socket is non-blocking. - non_blocking = user_set_non_blocking | internal_non_blocking, - - // User wants connection_aborted errors, which are disabled by default. - enable_connection_aborted = 4, - - // The user set the linger option. Needs to be checked when closing. - user_set_linger = 8 - }; - - // Flags indicating the current state of the socket. - unsigned char flags_; - // The protocol associated with the socket. protocol_type protocol_; - - // Per-descriptor data used by the reactor. - reactor::per_descriptor_data reactor_data_; }; // Constructor. reactive_socket_service(asio::io_service& io_service) - : io_service_impl_(use_service(io_service)), - reactor_(use_service(io_service)) + : reactive_socket_service_base(io_service) { - reactor_.init_task(); - } - - // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - } - - // Construct a new socket implementation. - void construct(implementation_type& impl) - { - impl.socket_ = invalid_socket; - impl.flags_ = 0; - } - - // Destroy a socket implementation. - void destroy(implementation_type& impl) - { - if (impl.socket_ != invalid_socket) - { - reactor_.close_descriptor(impl.socket_, impl.reactor_data_); - - if (impl.flags_ & implementation_type::non_blocking) - { - ioctl_arg_type non_blocking = 0; - asio::error_code ignored_ec; - socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); - impl.flags_ &= ~implementation_type::non_blocking; - } - - if (impl.flags_ & implementation_type::user_set_linger) - { - ::linger opt; - opt.l_onoff = 0; - opt.l_linger = 0; - asio::error_code ignored_ec; - socket_ops::setsockopt(impl.socket_, - SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec); - } - - asio::error_code ignored_ec; - socket_ops::close(impl.socket_, ignored_ec); - - impl.socket_ = invalid_socket; - } } // Open a new socket implementation. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { - if (is_open(impl)) - { - ec = asio::error::already_open; - return ec; - } - - socket_holder sock(socket_ops::socket(protocol.family(), - protocol.type(), protocol.protocol(), ec)); - if (sock.get() == invalid_socket) - return ec; - - if (int err = reactor_.register_descriptor(sock.get(), impl.reactor_data_)) - { - ec = asio::error_code(err, - asio::error::get_system_category()); - return ec; - } - - impl.socket_ = sock.release(); - impl.flags_ = 0; - impl.protocol_ = protocol; - ec = asio::error_code(); + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) + impl.protocol_ = protocol; return ec; } @@ -183,56 +92,8 @@ public: const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { - if (is_open(impl)) - { - ec = asio::error::already_open; - return ec; - } - - if (int err = reactor_.register_descriptor( - native_socket, impl.reactor_data_)) - { - ec = asio::error_code(err, - asio::error::get_system_category()); - return ec; - } - - impl.socket_ = native_socket; - impl.flags_ = 0; - impl.protocol_ = protocol; - ec = asio::error_code(); - return ec; - } - - // Determine whether the socket is open. - bool is_open(const implementation_type& impl) const - { - return impl.socket_ != invalid_socket; - } - - // Destroy a socket implementation. - asio::error_code close(implementation_type& impl, - asio::error_code& ec) - { - if (is_open(impl)) - { - reactor_.close_descriptor(impl.socket_, impl.reactor_data_); - - if (impl.flags_ & implementation_type::non_blocking) - { - ioctl_arg_type non_blocking = 0; - asio::error_code ignored_ec; - socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); - impl.flags_ &= ~implementation_type::non_blocking; - } - - if (socket_ops::close(impl.socket_, ec) == socket_error_retval) - return ec; - - impl.socket_ = invalid_socket; - } - - ec = asio::error_code(); + if (!do_assign(impl, protocol.type(), native_socket, ec)) + impl.protocol_ = protocol; return ec; } @@ -242,153 +103,23 @@ public: return impl.socket_; } - // Cancel all operations associated with the socket. - asio::error_code cancel(implementation_type& impl, - asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - reactor_.cancel_ops(impl.socket_, impl.reactor_data_); - ec = asio::error_code(); - return ec; - } - - // Determine whether the socket is at the out-of-band data mark. - bool at_mark(const implementation_type& impl, - asio::error_code& ec) const - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return false; - } - -#if defined(SIOCATMARK) - asio::detail::ioctl_arg_type value = 0; - socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec); -# if defined(ENOTTY) - if (ec.value() == ENOTTY) - ec = asio::error::not_socket; -# endif // defined(ENOTTY) -#else // defined(SIOCATMARK) - int value = sockatmark(impl.socket_); - if (value == -1) - ec = asio::error_code(errno, - asio::error::get_system_category()); - else - ec = asio::error_code(); -#endif // defined(SIOCATMARK) - return ec ? false : value != 0; - } - - // Determine the number of bytes available for reading. - std::size_t available(const implementation_type& impl, - asio::error_code& ec) const - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - asio::detail::ioctl_arg_type value = 0; - socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec); -#if defined(ENOTTY) - if (ec.value() == ENOTTY) - ec = asio::error::not_socket; -#endif // defined(ENOTTY) - return ec ? static_cast(0) : static_cast(value); - } - // Bind the socket to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); return ec; } - // Place the socket into the state where it will listen for new connections. - asio::error_code listen(implementation_type& impl, int backlog, - asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - socket_ops::listen(impl.socket_, backlog, ec); - return ec; - } - // Set a socket option. template asio::error_code set_option(implementation_type& impl, const Option& option, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - if (option.level(impl.protocol_) == custom_socket_option_level - && option.name(impl.protocol_) == enable_connection_aborted_option) - { - if (option.size(impl.protocol_) != sizeof(int)) - { - ec = asio::error::invalid_argument; - } - else - { - if (*reinterpret_cast(option.data(impl.protocol_))) - impl.flags_ |= implementation_type::enable_connection_aborted; - else - impl.flags_ &= ~implementation_type::enable_connection_aborted; - ec = asio::error_code(); - } - return ec; - } - else - { - if (option.level(impl.protocol_) == SOL_SOCKET - && option.name(impl.protocol_) == SO_LINGER) - { - impl.flags_ |= implementation_type::user_set_linger; - } - - socket_ops::setsockopt(impl.socket_, - option.level(impl.protocol_), option.name(impl.protocol_), - option.data(impl.protocol_), option.size(impl.protocol_), ec); - -#if defined(__MACH__) && defined(__APPLE__) \ -|| defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) - // To implement portable behaviour for SO_REUSEADDR with UDP sockets we - // need to also set SO_REUSEPORT on BSD-based platforms. - if (!ec && impl.protocol_.type() == SOCK_DGRAM - && option.level(impl.protocol_) == SOL_SOCKET - && option.name(impl.protocol_) == SO_REUSEADDR) - { - asio::error_code ignored_ec; - socket_ops::setsockopt(impl.socket_, SOL_SOCKET, SO_REUSEPORT, - option.data(impl.protocol_), option.size(impl.protocol_), - ignored_ec); - } -#endif - - return ec; - } + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; } // Set a socket option. @@ -396,78 +127,12 @@ public: asio::error_code get_option(const implementation_type& impl, Option& option, asio::error_code& ec) const { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - if (option.level(impl.protocol_) == custom_socket_option_level - && option.name(impl.protocol_) == enable_connection_aborted_option) - { - if (option.size(impl.protocol_) != sizeof(int)) - { - ec = asio::error::invalid_argument; - } - else - { - int* target = reinterpret_cast(option.data(impl.protocol_)); - if (impl.flags_ & implementation_type::enable_connection_aborted) - *target = 1; - else - *target = 0; - option.resize(impl.protocol_, sizeof(int)); - ec = asio::error_code(); - } - return ec; - } - else - { - size_t size = option.size(impl.protocol_); - socket_ops::getsockopt(impl.socket_, - option.level(impl.protocol_), option.name(impl.protocol_), - option.data(impl.protocol_), &size, ec); - if (!ec) - option.resize(impl.protocol_, size); - return ec; - } - } - - // Perform an IO control command on the socket. - template - asio::error_code io_control(implementation_type& impl, - IO_Control_Command& command, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - socket_ops::ioctl(impl.socket_, command.name(), - static_cast(command.data()), ec); - - // When updating the non-blocking mode we always perform the ioctl - // syscall, even if the flags would otherwise indicate that the socket is - // already in the correct state. This ensures that the underlying socket - // is put into the state that has been requested by the user. If the ioctl - // syscall was successful then we need to update the flags to match. - if (!ec && command.name() == static_cast(FIONBIO)) - { - if (*static_cast(command.data())) - { - impl.flags_ |= implementation_type::user_set_non_blocking; - } - else - { - // Clearing the non-blocking mode always overrides any internally-set - // non-blocking flag. Any subsequent asynchronous operations will need - // to re-enable non-blocking I/O. - impl.flags_ &= ~(implementation_type::user_set_non_blocking - | implementation_type::internal_non_blocking); - } - } - + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); return ec; } @@ -475,12 +140,6 @@ public: endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return endpoint_type(); - } - endpoint_type endpoint; std::size_t addr_len = endpoint.capacity(); if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) @@ -493,218 +152,15 @@ public: endpoint_type remote_endpoint(const implementation_type& impl, asio::error_code& ec) const { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return endpoint_type(); - } - endpoint_type endpoint; std::size_t addr_len = endpoint.capacity(); - if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec)) + if (socket_ops::getpeername(impl.socket_, + endpoint.data(), &addr_len, false, ec)) return endpoint_type(); endpoint.resize(addr_len); return endpoint; } - /// Disable sends or receives on the socket. - asio::error_code shutdown(implementation_type& impl, - socket_base::shutdown_type what, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - socket_ops::shutdown(impl.socket_, what, ec); - return ec; - } - - // Send the given data to the peer. - template - size_t send(implementation_type& impl, const ConstBufferSequence& buffers, - socket_base::message_flags flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - buffer_sequence_adapter bufs(buffers); - - // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) - { - ec = asio::error_code(); - return 0; - } - - // Send the data. - for (;;) - { - // Try to complete the operation without blocking. - int bytes_sent = socket_ops::send(impl.socket_, - bufs.buffers(), bufs.count(), flags, ec); - - // Check if operation succeeded. - if (bytes_sent >= 0) - return bytes_sent; - - // Operation failed. - if ((impl.flags_ & implementation_type::user_set_non_blocking) - || (ec != asio::error::would_block - && ec != asio::error::try_again)) - return 0; - - // Wait for socket to become ready. - if (socket_ops::poll_write(impl.socket_, ec) < 0) - return 0; - } - } - - // Wait until data can be sent without blocking. - size_t send(implementation_type& impl, const null_buffers&, - socket_base::message_flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - // Wait for socket to become ready. - socket_ops::poll_write(impl.socket_, ec); - - return 0; - } - - template - class send_op_base : public reactor_op - { - public: - send_op_base(socket_type socket, const ConstBufferSequence& buffers, - socket_base::message_flags flags, func_type complete_func) - : reactor_op(&send_op_base::do_perform, complete_func), - socket_(socket), - buffers_(buffers), - flags_(flags) - { - } - - static bool do_perform(reactor_op* base) - { - send_op_base* o(static_cast(base)); - - buffer_sequence_adapter bufs(o->buffers_); - - for (;;) - { - // Send the data. - asio::error_code ec; - int bytes = socket_ops::send(o->socket_, - bufs.buffers(), bufs.count(), o->flags_, ec); - - // Retry operation if interrupted by signal. - if (ec == asio::error::interrupted) - continue; - - // Check if we need to run the operation again. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - return false; - - o->ec_ = ec; - o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); - return true; - } - } - - private: - socket_type socket_; - ConstBufferSequence buffers_; - socket_base::message_flags flags_; - }; - - template - class send_op : public send_op_base - { - public: - send_op(socket_type socket, const ConstBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - : send_op_base(socket, - buffers, flags, &send_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - send_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->bytes_transferred_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - - // Start an asynchronous send. The data being sent must be valid for the - // lifetime of the asynchronous operation. - template - void async_send(implementation_type& impl, const ConstBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef send_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, - impl.socket_, buffers, flags, handler); - - start_op(impl, reactor::write_op, ptr.get(), true, - (impl.protocol_.type() == SOCK_STREAM - && buffer_sequence_adapter::all_empty(buffers))); - ptr.release(); - } - - // Start an asynchronous wait until data can be sent without blocking. - template - void async_send(implementation_type& impl, const null_buffers&, - socket_base::message_flags, Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - start_op(impl, reactor::write_op, ptr.get(), false, false); - ptr.release(); - } - // Send a datagram to the specified endpoint. Returns the number of bytes // sent. template @@ -712,148 +168,25 @@ public: const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - buffer_sequence_adapter bufs(buffers); - // Send the data. - for (;;) - { - // Try to complete the operation without blocking. - int bytes_sent = socket_ops::sendto(impl.socket_, bufs.buffers(), - bufs.count(), flags, destination.data(), destination.size(), ec); - - // Check if operation succeeded. - if (bytes_sent >= 0) - return bytes_sent; - - // Operation failed. - if ((impl.flags_ & implementation_type::user_set_non_blocking) - || (ec != asio::error::would_block - && ec != asio::error::try_again)) - return 0; - - // Wait for socket to become ready. - if (socket_ops::poll_write(impl.socket_, ec) < 0) - return 0; - } + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); } // Wait until data can be sent without blocking. size_t send_to(implementation_type& impl, const null_buffers&, - socket_base::message_flags, const endpoint_type&, + const endpoint_type&, socket_base::message_flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - // Wait for socket to become ready. socket_ops::poll_write(impl.socket_, ec); return 0; } - template - class send_to_op_base : public reactor_op - { - public: - send_to_op_base(socket_type socket, const ConstBufferSequence& buffers, - const endpoint_type& endpoint, socket_base::message_flags flags, - func_type complete_func) - : reactor_op(&send_to_op_base::do_perform, complete_func), - socket_(socket), - buffers_(buffers), - destination_(endpoint), - flags_(flags) - { - } - - static bool do_perform(reactor_op* base) - { - send_to_op_base* o(static_cast(base)); - - buffer_sequence_adapter bufs(o->buffers_); - - for (;;) - { - // Send the data. - asio::error_code ec; - int bytes = socket_ops::sendto(o->socket_, bufs.buffers(), bufs.count(), - o->flags_, o->destination_.data(), o->destination_.size(), ec); - - // Retry operation if interrupted by signal. - if (ec == asio::error::interrupted) - continue; - - // Check if we need to run the operation again. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - return false; - - o->ec_ = ec; - o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); - return true; - } - } - - private: - socket_type socket_; - ConstBufferSequence buffers_; - endpoint_type destination_; - socket_base::message_flags flags_; - }; - - template - class send_to_op : public send_to_op_base - { - public: - send_to_op(socket_type socket, const ConstBufferSequence& buffers, - const endpoint_type& endpoint, socket_base::message_flags flags, - Handler handler) - : send_to_op_base(socket, - buffers, endpoint, flags, &send_to_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - send_to_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->bytes_transferred_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous send. The data being sent must be valid for the // lifetime of the asynchronous operation. template @@ -863,235 +196,31 @@ public: Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef send_to_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, impl.socket_, - buffers, destination, flags, handler); + typedef reactive_socket_sendto_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.socket_, buffers, destination, flags, handler); - start_op(impl, reactor::write_op, ptr.get(), true, false); - ptr.release(); + start_op(impl, reactor::write_op, p.p, true, false); + p.v = p.p = 0; } // Start an asynchronous wait until data can be sent without blocking. template void async_send_to(implementation_type& impl, const null_buffers&, - socket_base::message_flags, const endpoint_type&, Handler handler) + const endpoint_type&, socket_base::message_flags, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef reactive_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); - start_op(impl, reactor::write_op, ptr.get(), false, false); - ptr.release(); - } - - // Receive some data from the peer. Returns the number of bytes received. - template - size_t receive(implementation_type& impl, - const MutableBufferSequence& buffers, - socket_base::message_flags flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - buffer_sequence_adapter bufs(buffers); - - // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) - { - ec = asio::error_code(); - return 0; - } - - // Receive some data. - for (;;) - { - // Try to complete the operation without blocking. - int bytes_recvd = socket_ops::recv(impl.socket_, - bufs.buffers(), bufs.count(), flags, ec); - - // Check if operation succeeded. - if (bytes_recvd > 0) - return bytes_recvd; - - // Check for EOF. - if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM) - { - ec = asio::error::eof; - return 0; - } - - // Operation failed. - if ((impl.flags_ & implementation_type::user_set_non_blocking) - || (ec != asio::error::would_block - && ec != asio::error::try_again)) - return 0; - - // Wait for socket to become ready. - if (socket_ops::poll_read(impl.socket_, ec) < 0) - return 0; - } - } - - // Wait until data can be received without blocking. - size_t receive(implementation_type& impl, const null_buffers&, - socket_base::message_flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - // Wait for socket to become ready. - socket_ops::poll_read(impl.socket_, ec); - - return 0; - } - - template - class receive_op_base : public reactor_op - { - public: - receive_op_base(socket_type socket, int protocol_type, - const MutableBufferSequence& buffers, - socket_base::message_flags flags, func_type complete_func) - : reactor_op(&receive_op_base::do_perform, complete_func), - socket_(socket), - protocol_type_(protocol_type), - buffers_(buffers), - flags_(flags) - { - } - - static bool do_perform(reactor_op* base) - { - receive_op_base* o(static_cast(base)); - - buffer_sequence_adapter bufs(o->buffers_); - - for (;;) - { - // Receive some data. - asio::error_code ec; - int bytes = socket_ops::recv(o->socket_, - bufs.buffers(), bufs.count(), o->flags_, ec); - if (bytes == 0 && o->protocol_type_ == SOCK_STREAM) - ec = asio::error::eof; - - // Retry operation if interrupted by signal. - if (ec == asio::error::interrupted) - continue; - - // Check if we need to run the operation again. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - return false; - - o->ec_ = ec; - o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); - return true; - } - } - - private: - socket_type socket_; - int protocol_type_; - MutableBufferSequence buffers_; - socket_base::message_flags flags_; - }; - - template - class receive_op : public receive_op_base - { - public: - receive_op(socket_type socket, int protocol_type, - const MutableBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - : receive_op_base(socket, - protocol_type, buffers, flags, &receive_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - receive_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->bytes_transferred_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - - // Start an asynchronous receive. The buffer for the data being received - // must be valid for the lifetime of the asynchronous operation. - template - void async_receive(implementation_type& impl, - const MutableBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef receive_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - int protocol_type = impl.protocol_.type(); - handler_ptr ptr(raw_ptr, impl.socket_, - protocol_type, buffers, flags, handler); - - start_op(impl, - (flags & socket_base::message_out_of_band) - ? reactor::except_op : reactor::read_op, - ptr.get(), (flags & socket_base::message_out_of_band) == 0, - (impl.protocol_.type() == SOCK_STREAM - && buffer_sequence_adapter::all_empty(buffers))); - ptr.release(); - } - - // Wait until data can be received without blocking. - template - void async_receive(implementation_type& impl, const null_buffers&, - socket_base::message_flags flags, Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - start_op(impl, - (flags & socket_base::message_out_of_band) - ? reactor::except_op : reactor::read_op, - ptr.get(), false, false); - ptr.release(); + start_op(impl, reactor::write_op, p.p, false, false); + p.v = p.p = 0; } // Receive a datagram with the endpoint of the sender. Returns the number of @@ -1102,47 +231,18 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - buffer_sequence_adapter bufs(buffers); - // Receive some data. - for (;;) - { - // Try to complete the operation without blocking. - std::size_t addr_len = sender_endpoint.capacity(); - int bytes_recvd = socket_ops::recvfrom(impl.socket_, bufs.buffers(), - bufs.count(), flags, sender_endpoint.data(), &addr_len, ec); + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); - // Check if operation succeeded. - if (bytes_recvd > 0) - { - sender_endpoint.resize(addr_len); - return bytes_recvd; - } + if (!ec) + sender_endpoint.resize(addr_len); - // Check for EOF. - if (bytes_recvd == 0 && impl.protocol_.type() == SOCK_STREAM) - { - ec = asio::error::eof; - return 0; - } - - // Operation failed. - if ((impl.flags_ & implementation_type::user_set_non_blocking) - || (ec != asio::error::would_block - && ec != asio::error::try_again)) - return 0; - - // Wait for socket to become ready. - if (socket_ops::poll_read(impl.socket_, ec) < 0) - return 0; - } + return bytes_recvd; } // Wait until data can be received without blocking. @@ -1150,12 +250,6 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - // Wait for socket to become ready. socket_ops::poll_read(impl.socket_, ec); @@ -1165,105 +259,6 @@ public: return 0; } - template - class receive_from_op_base : public reactor_op - { - public: - receive_from_op_base(socket_type socket, int protocol_type, - const MutableBufferSequence& buffers, endpoint_type& endpoint, - socket_base::message_flags flags, func_type complete_func) - : reactor_op(&receive_from_op_base::do_perform, complete_func), - socket_(socket), - protocol_type_(protocol_type), - buffers_(buffers), - sender_endpoint_(endpoint), - flags_(flags) - { - } - - static bool do_perform(reactor_op* base) - { - receive_from_op_base* o(static_cast(base)); - - buffer_sequence_adapter bufs(o->buffers_); - - for (;;) - { - // Receive some data. - asio::error_code ec; - std::size_t addr_len = o->sender_endpoint_.capacity(); - int bytes = socket_ops::recvfrom(o->socket_, bufs.buffers(), - bufs.count(), o->flags_, o->sender_endpoint_.data(), &addr_len, ec); - if (bytes == 0 && o->protocol_type_ == SOCK_STREAM) - ec = asio::error::eof; - - // Retry operation if interrupted by signal. - if (ec == asio::error::interrupted) - continue; - - // Check if we need to run the operation again. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - return false; - - o->sender_endpoint_.resize(addr_len); - o->ec_ = ec; - o->bytes_transferred_ = (bytes < 0 ? 0 : bytes); - return true; - } - } - - private: - socket_type socket_; - int protocol_type_; - MutableBufferSequence buffers_; - endpoint_type& sender_endpoint_; - socket_base::message_flags flags_; - }; - - template - class receive_from_op : public receive_from_op_base - { - public: - receive_from_op(socket_type socket, int protocol_type, - const MutableBufferSequence& buffers, endpoint_type& endpoint, - socket_base::message_flags flags, Handler handler) - : receive_from_op_base(socket, protocol_type, - buffers, endpoint, flags, &receive_from_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - receive_from_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->bytes_transferred_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous receive. The buffer for the data being received and // the sender_endpoint object must both be valid for the lifetime of the // asynchronous operation. @@ -1273,18 +268,20 @@ public: socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef receive_from_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); + typedef reactive_socket_recvfrom_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; int protocol_type = impl.protocol_.type(); - handler_ptr ptr(raw_ptr, impl.socket_, - protocol_type, buffers, sender_endpoint, flags, handler); + p.p = new (p.v) op(impl.socket_, protocol_type, + buffers, sender_endpoint, flags, handler); start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, - ptr.get(), true, false); - ptr.release(); + p.p, true, false); + p.v = p.p = 0; } // Wait until data can be received without blocking. @@ -1294,10 +291,11 @@ public: socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef reactive_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); // Reset endpoint since it can be given no sensible value at this time. sender_endpoint = endpoint_type(); @@ -1305,8 +303,8 @@ public: start_op(impl, (flags & socket_base::message_out_of_band) ? reactor::except_op : reactor::read_op, - ptr.get(), false, false); - ptr.release(); + p.p, false, false); + p.v = p.p = 0; } // Accept a new connection. @@ -1314,12 +312,6 @@ public: asio::error_code accept(implementation_type& impl, Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - // We cannot accept a socket that is already open. if (peer.is_open()) { @@ -1327,182 +319,23 @@ public: return ec; } - // Accept a socket. - for (;;) + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() != invalid_socket) { - // Try to complete the operation without blocking. - socket_holder new_socket; - std::size_t addr_len = 0; if (peer_endpoint) - { - addr_len = peer_endpoint->capacity(); - new_socket.reset(socket_ops::accept(impl.socket_, - peer_endpoint->data(), &addr_len, ec)); - } - else - { - new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec)); - } - - // Check if operation succeeded. - if (new_socket.get() >= 0) - { - if (peer_endpoint) - peer_endpoint->resize(addr_len); - peer.assign(impl.protocol_, new_socket.get(), ec); - if (!ec) - new_socket.release(); - return ec; - } - - // Operation failed. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - { - if (impl.flags_ & implementation_type::user_set_non_blocking) - return ec; - // Fall through to retry operation. - } - else if (ec == asio::error::connection_aborted) - { - if (impl.flags_ & implementation_type::enable_connection_aborted) - return ec; - // Fall through to retry operation. - } -#if defined(EPROTO) - else if (ec.value() == EPROTO) - { - if (impl.flags_ & implementation_type::enable_connection_aborted) - return ec; - // Fall through to retry operation. - } -#endif // defined(EPROTO) - else - return ec; - - // Wait for socket to become ready. - if (socket_ops::poll_read(impl.socket_, ec) < 0) - return ec; + peer_endpoint->resize(addr_len); + if (!peer.assign(impl.protocol_, new_socket.get(), ec)) + new_socket.release(); } + + return ec; } - template - class accept_op_base : public reactor_op - { - public: - accept_op_base(socket_type socket, Socket& peer, - const protocol_type& protocol, endpoint_type* peer_endpoint, - bool enable_connection_aborted, func_type complete_func) - : reactor_op(&accept_op_base::do_perform, complete_func), - socket_(socket), - peer_(peer), - protocol_(protocol), - peer_endpoint_(peer_endpoint), - enable_connection_aborted_(enable_connection_aborted) - { - } - - static bool do_perform(reactor_op* base) - { - accept_op_base* o(static_cast(base)); - - for (;;) - { - // Accept the waiting connection. - asio::error_code ec; - socket_holder new_socket; - std::size_t addr_len = 0; - std::size_t* addr_len_p = 0; - socket_addr_type* addr = 0; - if (o->peer_endpoint_) - { - addr_len = o->peer_endpoint_->capacity(); - addr_len_p = &addr_len; - addr = o->peer_endpoint_->data(); - } - new_socket.reset(socket_ops::accept(o->socket_, addr, addr_len_p, ec)); - - // Retry operation if interrupted by signal. - if (ec == asio::error::interrupted) - continue; - - // Check if we need to run the operation again. - if (ec == asio::error::would_block - || ec == asio::error::try_again) - return false; - if (ec == asio::error::connection_aborted - && !o->enable_connection_aborted_) - return false; -#if defined(EPROTO) - if (ec.value() == EPROTO && !o->enable_connection_aborted_) - return false; -#endif // defined(EPROTO) - - // Transfer ownership of the new socket to the peer object. - if (!ec) - { - if (o->peer_endpoint_) - o->peer_endpoint_->resize(addr_len); - o->peer_.assign(o->protocol_, new_socket.get(), ec); - if (!ec) - new_socket.release(); - } - - o->ec_ = ec; - return true; - } - } - - private: - socket_type socket_; - Socket& peer_; - protocol_type protocol_; - endpoint_type* peer_endpoint_; - bool enable_connection_aborted_; - }; - - template - class accept_op : public accept_op_base - { - public: - accept_op(socket_type socket, Socket& peer, const protocol_type& protocol, - endpoint_type* peer_endpoint, bool enable_connection_aborted, - Handler handler) - : accept_op_base(socket, peer, protocol, peer_endpoint, - enable_connection_aborted, &accept_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - accept_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder1 - handler(o->handler_, o->ec_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous accept. The peer and peer_endpoint objects // must be valid until the accept's handler is invoked. template @@ -1510,230 +343,41 @@ public: endpoint_type* peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef accept_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - bool enable_connection_aborted = - (impl.flags_ & implementation_type::enable_connection_aborted) != 0; - handler_ptr ptr(raw_ptr, impl.socket_, peer, - impl.protocol_, peer_endpoint, enable_connection_aborted, handler); + typedef reactive_socket_accept_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.socket_, impl.state_, peer, + impl.protocol_, peer_endpoint, handler); - start_accept_op(impl, ptr.get(), peer.is_open()); - ptr.release(); + start_accept_op(impl, p.p, peer.is_open()); + p.v = p.p = 0; } // Connect the socket to the specified endpoint. asio::error_code connect(implementation_type& impl, const endpoint_type& peer_endpoint, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - // Perform the connect operation. - socket_ops::connect(impl.socket_, + socket_ops::sync_connect(impl.socket_, peer_endpoint.data(), peer_endpoint.size(), ec); - if (ec != asio::error::in_progress - && ec != asio::error::would_block) - { - // The connect operation finished immediately. - return ec; - } - - // Wait for socket to become ready. - if (socket_ops::poll_connect(impl.socket_, ec) < 0) - return ec; - - // Get the error code from the connect operation. - int connect_error = 0; - size_t connect_error_len = sizeof(connect_error); - if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_ERROR, - &connect_error, &connect_error_len, ec) == socket_error_retval) - return ec; - - // Return the result of the connect operation. - ec = asio::error_code(connect_error, - asio::error::get_system_category()); return ec; } - class connect_op_base : public reactor_op - { - public: - connect_op_base(socket_type socket, func_type complete_func) - : reactor_op(&connect_op_base::do_perform, complete_func), - socket_(socket) - { - } - - static bool do_perform(reactor_op* base) - { - connect_op_base* o(static_cast(base)); - - // Get the error code from the connect operation. - int connect_error = 0; - size_t connect_error_len = sizeof(connect_error); - if (socket_ops::getsockopt(o->socket_, SOL_SOCKET, SO_ERROR, - &connect_error, &connect_error_len, o->ec_) == socket_error_retval) - return true; - - // The connection failed so the handler will be posted with an error code. - if (connect_error) - { - o->ec_ = asio::error_code(connect_error, - asio::error::get_system_category()); - } - - return true; - } - - private: - socket_type socket_; - }; - - template - class connect_op : public connect_op_base - { - public: - connect_op(socket_type socket, Handler handler) - : connect_op_base(socket, &connect_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - connect_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder1 - handler(o->handler_, o->ec_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous connect. template void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef connect_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, impl.socket_, handler); + typedef reactive_socket_connect_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.socket_, handler); - start_connect_op(impl, ptr.get(), peer_endpoint); - ptr.release(); + start_connect_op(impl, p.p, peer_endpoint.data(), peer_endpoint.size()); + p.v = p.p = 0; } - -private: - // Start the asynchronous read or write operation. - void start_op(implementation_type& impl, int op_type, - reactor_op* op, bool non_blocking, bool noop) - { - if (!noop) - { - if (is_open(impl)) - { - if (!non_blocking || is_non_blocking(impl) - || set_non_blocking(impl, op->ec_)) - { - reactor_.start_op(op_type, impl.socket_, - impl.reactor_data_, op, non_blocking); - return; - } - } - else - op->ec_ = asio::error::bad_descriptor; - } - - io_service_impl_.post_immediate_completion(op); - } - - // Start the asynchronous accept operation. - void start_accept_op(implementation_type& impl, - reactor_op* op, bool peer_is_open) - { - if (!peer_is_open) - start_op(impl, reactor::read_op, op, true, false); - else - { - op->ec_ = asio::error::already_open; - io_service_impl_.post_immediate_completion(op); - } - } - - // Start the asynchronous connect operation. - void start_connect_op(implementation_type& impl, - reactor_op* op, const endpoint_type& peer_endpoint) - { - if (is_open(impl)) - { - if (is_non_blocking(impl) || set_non_blocking(impl, op->ec_)) - { - if (socket_ops::connect(impl.socket_, peer_endpoint.data(), - peer_endpoint.size(), op->ec_) != 0) - { - if (op->ec_ == asio::error::in_progress - || op->ec_ == asio::error::would_block) - { - op->ec_ = asio::error_code(); - reactor_.start_op(reactor::connect_op, - impl.socket_, impl.reactor_data_, op, false); - return; - } - } - } - } - else - op->ec_ = asio::error::bad_descriptor; - - io_service_impl_.post_immediate_completion(op); - } - - // Determine whether the socket has been set non-blocking. - bool is_non_blocking(implementation_type& impl) const - { - return (impl.flags_ & implementation_type::non_blocking); - } - - // Set the internal non-blocking flag. - bool set_non_blocking(implementation_type& impl, - asio::error_code& ec) - { - ioctl_arg_type non_blocking = 1; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - return false; - impl.flags_ |= implementation_type::internal_non_blocking; - return true; - } - - // The io_service implementation used to post completions. - io_service_impl& io_service_impl_; - - // The selector that performs event demultiplexing for the service. - reactor& reactor_; }; } // namespace detail @@ -1741,4 +385,6 @@ private: #include "asio/detail/pop_options.hpp" +#endif // !defined(ASIO_HAS_IOCP) + #endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_HPP diff --git a/ext/asio/asio/detail/reactive_socket_service_base.hpp b/ext/asio/asio/detail/reactive_socket_service_base.hpp new file mode 100644 index 0000000000..b26d73e74a --- /dev/null +++ b/ext/asio/asio/detail/reactive_socket_service_base.hpp @@ -0,0 +1,298 @@ +// +// detail/reactive_socket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if !defined(ASIO_HAS_IOCP) + +#include +#include "asio/buffer.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/reactive_null_buffers_op.hpp" +#include "asio/detail/reactive_socket_recv_op.hpp" +#include "asio/detail/reactive_socket_send_op.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class reactive_socket_service_base +{ +public: + // The native type of a socket. + typedef socket_type native_type; + + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + }; + + // Constructor. + ASIO_DECL reactive_socket_service_base( + asio::io_service& io_service); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown_service(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type& impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Get the native socket representation. + native_type native(base_implementation_type& impl) + { + return impl.socket_; + } + + // Cancel all operations associated with the socket. + ASIO_DECL asio::error_code cancel( + base_implementation_type& impl, asio::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(base_implementation_type& impl, + int backlog, asio::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast(command.data()), ec); + return ec; + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send the given data to the peer. + template + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_send_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.socket_, buffers, flags, handler); + + start_op(impl, reactor::write_op, p.p, true, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter::all_empty(buffers))); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); + + start_op(impl, reactor::write_op, p.p, false, false); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef reactive_socket_recv_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, (flags & socket_base::message_out_of_band) == 0, + ((impl.state_ & socket_ops::stream_oriented) + && buffer_sequence_adapter::all_empty(buffers))); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef reactive_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); + + start_op(impl, + (flags & socket_base::message_out_of_band) + ? reactor::except_op : reactor::read_op, + p.p, false, false); + p.v = p.p = 0; + } + +protected: + // Open a new socket implementation. + ASIO_DECL asio::error_code do_open( + base_implementation_type& impl, int af, + int type, int protocol, asio::error_code& ec); + + // Assign a native socket to a socket implementation. + ASIO_DECL asio::error_code do_assign( + base_implementation_type& impl, int type, + const native_type& native_socket, asio::error_code& ec); + + // Start the asynchronous read or write operation. + ASIO_DECL void start_op(base_implementation_type& impl, int op_type, + reactor_op* op, bool non_blocking, bool noop); + + // Start the asynchronous accept operation. + ASIO_DECL void start_accept_op(base_implementation_type& impl, + reactor_op* op, bool peer_is_open); + + // Start the asynchronous connect operation. + ASIO_DECL void start_connect_op(base_implementation_type& impl, + reactor_op* op, const socket_addr_type* addr, size_t addrlen); + + // The selector that performs event demultiplexing for the service. + reactor& reactor_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/reactive_socket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_REACTIVE_SOCKET_SERVICE_BASE_HPP diff --git a/ext/asio/asio/detail/reactor.hpp b/ext/asio/asio/detail/reactor.hpp index 98ac360b9e..0e938c9542 100644 --- a/ext/asio/asio/detail/reactor.hpp +++ b/ext/asio/asio/detail/reactor.hpp @@ -1,8 +1,8 @@ // -// reactor.hpp -// ~~~~~~~~~~~ +// detail/reactor.hpp +// ~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,8 +15,6 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - #include "asio/detail/reactor_fwd.hpp" #if defined(ASIO_HAS_EPOLL) @@ -29,6 +27,4 @@ # include "asio/detail/select_reactor.hpp" #endif -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_REACTOR_HPP diff --git a/ext/asio/asio/detail/reactor_fwd.hpp b/ext/asio/asio/detail/reactor_fwd.hpp index 9c33be54ca..d45d768289 100644 --- a/ext/asio/asio/detail/reactor_fwd.hpp +++ b/ext/asio/asio/detail/reactor_fwd.hpp @@ -1,8 +1,8 @@ // -// reactor_fwd.hpp -// ~~~~~~~~~~~~~~~ +// detail/reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,19 +15,25 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/dev_poll_reactor_fwd.hpp" -#include "asio/detail/epoll_reactor_fwd.hpp" -#include "asio/detail/kqueue_reactor_fwd.hpp" -#include "asio/detail/select_reactor_fwd.hpp" -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/select_reactor_fwd.hpp" +#elif defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor_fwd.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor_fwd.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor_fwd.hpp" +#else +# include "asio/detail/select_reactor_fwd.hpp" +#endif namespace asio { namespace detail { #if defined(ASIO_HAS_IOCP) -typedef select_reactor reactor; +typedef select_reactor reactor; #elif defined(ASIO_HAS_EPOLL) typedef epoll_reactor reactor; #elif defined(ASIO_HAS_KQUEUE) @@ -35,12 +41,10 @@ typedef kqueue_reactor reactor; #elif defined(ASIO_HAS_DEV_POLL) typedef dev_poll_reactor reactor; #else -typedef select_reactor reactor; +typedef select_reactor reactor; #endif } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_REACTOR_FWD_HPP diff --git a/ext/asio/asio/detail/reactor_op.hpp b/ext/asio/asio/detail/reactor_op.hpp index cd557fa6ae..3009cf9c12 100644 --- a/ext/asio/asio/detail/reactor_op.hpp +++ b/ext/asio/asio/detail/reactor_op.hpp @@ -1,8 +1,8 @@ // -// reactor_op.hpp -// ~~~~~~~~~~~~ +// detail/reactor_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/operation.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/reactor_op_queue.hpp b/ext/asio/asio/detail/reactor_op_queue.hpp index 233c0aec16..6eed398997 100644 --- a/ext/asio/asio/detail/reactor_op_queue.hpp +++ b/ext/asio/asio/detail/reactor_op_queue.hpp @@ -1,8 +1,8 @@ // -// reactor_op_queue.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// detail/reactor_op_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/error.hpp" +#include "asio/detail/config.hpp" #include "asio/detail/hash_map.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/regex_fwd.hpp b/ext/asio/asio/detail/regex_fwd.hpp new file mode 100644 index 0000000000..9da295b26c --- /dev/null +++ b/ext/asio/asio/detail/regex_fwd.hpp @@ -0,0 +1,31 @@ +// +// detail/regex_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_REGEX_FWD_HPP +#define ASIO_DETAIL_REGEX_FWD_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include + +namespace boost { + +template +struct sub_match; + +template +class match_results; + +} // namespace boost + +#endif // ASIO_DETAIL_REGEX_FWD_HPP diff --git a/ext/asio/asio/detail/resolve_endpoint_op.hpp b/ext/asio/asio/detail/resolve_endpoint_op.hpp new file mode 100644 index 0000000000..0931fd03db --- /dev/null +++ b/ext/asio/asio/detail/resolve_endpoint_op.hpp @@ -0,0 +1,116 @@ +// +// detail/resolve_endpoint_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP +#define ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolve_endpoint_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(resolve_endpoint_op); + + typedef typename Protocol::endpoint endpoint_type; + typedef asio::ip::basic_resolver_iterator iterator_type; + + resolve_endpoint_op(socket_ops::weak_cancel_token_type cancel_token, + const endpoint_type& endpoint, io_service_impl& ios, Handler handler) + : operation(&resolve_endpoint_op::do_complete), + cancel_token_(cancel_token), + endpoint_(endpoint), + io_service_impl_(ios), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the operation object. + resolve_endpoint_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + if (owner && owner != &o->io_service_impl_) + { + // The operation is being run on the worker io_service. Time to perform + // the resolver operation. + + // Perform the blocking endpoint resolution operation. + char host_name[NI_MAXHOST]; + char service_name[NI_MAXSERV]; + socket_ops::background_getnameinfo(o->cancel_token_, o->endpoint_.data(), + o->endpoint_.size(), host_name, NI_MAXHOST, service_name, NI_MAXSERV, + o->endpoint_.protocol().type(), o->ec_); + o->iter_ = iterator_type::create(o->endpoint_, host_name, service_name); + + // Pass operation back to main io_service for completion. + o->io_service_impl_.post_deferred_completion(o); + p.v = p.p = 0; + } + else + { + // The operation has been returned to the main io_service. The completion + // handler is ready to be delivered. + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an upcall, + // a sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, o->iter_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + endpoint_type endpoint_; + io_service_impl& io_service_impl_; + Handler handler_; + asio::error_code ec_; + iterator_type iter_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVER_ENDPOINT_OP_HPP diff --git a/ext/asio/asio/detail/resolve_op.hpp b/ext/asio/asio/detail/resolve_op.hpp new file mode 100644 index 0000000000..a04fbc3b52 --- /dev/null +++ b/ext/asio/asio/detail/resolve_op.hpp @@ -0,0 +1,126 @@ +// +// detail/resolve_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVE_OP_HPP +#define ASIO_DETAIL_RESOLVE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/ip/basic_resolver_iterator.hpp" +#include "asio/ip/basic_resolver_query.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class resolve_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(resolve_op); + + typedef asio::ip::basic_resolver_query query_type; + typedef asio::ip::basic_resolver_iterator iterator_type; + + resolve_op(socket_ops::weak_cancel_token_type cancel_token, + const query_type& query, io_service_impl& ios, Handler handler) + : operation(&resolve_op::do_complete), + cancel_token_(cancel_token), + query_(query), + io_service_impl_(ios), + handler_(handler), + addrinfo_(0) + { + } + + ~resolve_op() + { + if (addrinfo_) + socket_ops::freeaddrinfo(addrinfo_); + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the operation object. + resolve_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + if (owner && owner != &o->io_service_impl_) + { + // The operation is being run on the worker io_service. Time to perform + // the resolver operation. + + // Perform the blocking host resolution operation. + socket_ops::background_getaddrinfo(o->cancel_token_, + o->query_.host_name().c_str(), o->query_.service_name().c_str(), + o->query_.hints(), &o->addrinfo_, o->ec_); + + // Pass operation back to main io_service for completion. + o->io_service_impl_.post_deferred_completion(o); + p.v = p.p = 0; + } + else + { + // The operation has been returned to the main io_service. The completion + // handler is ready to be delivered. + + // Make a copy of the handler so that the memory can be deallocated + // before the upcall is made. Even if we're not about to make an upcall, + // a sub-object of the handler may be the true owner of the memory + // associated with the handler. Consequently, a local copy of the handler + // is required to ensure that any owning sub-object remains valid until + // after we have deallocated the memory here. + detail::binder2 + handler(o->handler_, o->ec_, iterator_type()); + p.h = boost::addressof(handler.handler_); + if (o->addrinfo_) + { + handler.arg2_ = iterator_type::create(o->addrinfo_, + o->query_.host_name(), o->query_.service_name()); + } + p.reset(); + + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + query_type query_; + io_service_impl& io_service_impl_; + Handler handler_; + asio::error_code ec_; + asio::detail::addrinfo_type* addrinfo_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_RESOLVE_OP_HPP diff --git a/ext/asio/asio/detail/resolver_service.hpp b/ext/asio/asio/detail/resolver_service.hpp index 562dc10476..c2c3b0c98a 100644 --- a/ext/asio/asio/detail/resolver_service.hpp +++ b/ext/asio/asio/detail/resolver_service.hpp @@ -1,8 +1,8 @@ // -// resolver_service.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// detail/resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,68 +15,25 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/io_service.hpp" +#include "asio/detail/config.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" -#include "asio/detail/bind_handler.hpp" -#include "asio/detail/fenced_block.hpp" -#include "asio/detail/mutex.hpp" -#include "asio/detail/noncopyable.hpp" -#include "asio/detail/operation.hpp" -#include "asio/detail/service_base.hpp" -#include "asio/detail/socket_ops.hpp" -#include "asio/detail/socket_types.hpp" -#include "asio/detail/thread.hpp" +#include "asio/detail/resolve_endpoint_op.hpp" +#include "asio/detail/resolve_op.hpp" +#include "asio/detail/resolver_service_base.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { template -class resolver_service - : public asio::detail::service_base > +class resolver_service : public resolver_service_base { -private: - // Helper class to perform exception-safe cleanup of addrinfo objects. - class auto_addrinfo - : private asio::detail::noncopyable - { - public: - explicit auto_addrinfo(asio::detail::addrinfo_type* ai) - : ai_(ai) - { - } - - ~auto_addrinfo() - { - if (ai_) - socket_ops::freeaddrinfo(ai_); - } - - operator asio::detail::addrinfo_type*() - { - return ai_; - } - - private: - asio::detail::addrinfo_type* ai_; - }; - public: - // The implementation type of the resolver. The shared pointer is used as a - // cancellation token to indicate to the background thread that the operation - // has been cancelled. - typedef boost::shared_ptr implementation_type; - struct noop_deleter { void operator()(void*) {} }; + // The implementation type of the resolver. A cancellation token is used to + // indicate to the background thread that the operation has been cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; // The endpoint type. typedef typename Protocol::endpoint endpoint_type; @@ -89,349 +46,69 @@ public: // Constructor. resolver_service(asio::io_service& io_service) - : asio::detail::service_base< - resolver_service >(io_service), - mutex_(), - io_service_impl_(asio::use_service(io_service)), - work_io_service_(new asio::io_service), - work_io_service_impl_(asio::use_service< - io_service_impl>(*work_io_service_)), - work_(new asio::io_service::work(*work_io_service_)), - work_thread_(0) + : resolver_service_base(io_service) { } - // Destructor. - ~resolver_service() - { - shutdown_service(); - } - - // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - work_.reset(); - if (work_io_service_) - { - work_io_service_->stop(); - if (work_thread_) - { - work_thread_->join(); - work_thread_.reset(); - } - work_io_service_.reset(); - } - } - - // Construct a new resolver implementation. - void construct(implementation_type& impl) - { - impl.reset(static_cast(0), noop_deleter()); - } - - // Destroy a resolver implementation. - void destroy(implementation_type&) - { - } - - // Cancel pending asynchronous operations. - void cancel(implementation_type& impl) - { - impl.reset(static_cast(0), noop_deleter()); - } - // Resolve a query to a list of entries. iterator_type resolve(implementation_type&, const query_type& query, asio::error_code& ec) { asio::detail::addrinfo_type* address_info = 0; - std::string host_name = query.host_name(); - std::string service_name = query.service_name(); - asio::detail::addrinfo_type hints = query.hints(); - socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0, - service_name.c_str(), &hints, &address_info, ec); + socket_ops::getaddrinfo(query.host_name().c_str(), + query.service_name().c_str(), query.hints(), &address_info, ec); auto_addrinfo auto_address_info(address_info); - if (ec) - return iterator_type(); - - return iterator_type::create(address_info, host_name, service_name); + return ec ? iterator_type() : iterator_type::create( + address_info, query.host_name(), query.service_name()); } - template - class resolve_op - : public operation - { - public: - resolve_op(implementation_type impl, const query_type& query, - io_service_impl& io_service_impl, Handler handler) - : operation(&resolve_op::do_complete), - impl_(impl), - query_(query), - io_service_impl_(io_service_impl), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the operation object. - resolve_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - if (owner) - { - if (owner != &o->io_service_impl_) - { - // The operation is being run on the worker io_service. Time to - // perform the resolver operation. - - if (o->impl_.expired()) - { - // THe operation has been cancelled. - o->ec_ = asio::error::operation_aborted; - } - else - { - // Perform the blocking host resolution operation. - asio::detail::addrinfo_type* address_info = 0; - std::string host_name = o->query_.host_name(); - std::string service_name = o->query_.service_name(); - asio::detail::addrinfo_type hints = o->query_.hints(); - socket_ops::getaddrinfo(!host_name.empty() ? host_name.c_str() : 0, - service_name.c_str(), &hints, &address_info, o->ec_); - auto_addrinfo auto_address_info(address_info); - o->iter_ = iterator_type::create( - address_info, host_name, service_name); - } - - o->io_service_impl_.post_deferred_completion(o); - ptr.release(); - } - else - { - // The operation has been returned to the main io_serice. The - // completion handler is ready to be delivered. - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object - // remains valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->iter_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - } - - private: - boost::weak_ptr impl_; - query_type query_; - io_service_impl& io_service_impl_; - Handler handler_; - asio::error_code ec_; - iterator_type iter_; - }; - // Asynchronously resolve a query to a list of entries. template void async_resolve(implementation_type& impl, const query_type& query, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef resolve_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, - impl, query, io_service_impl_, handler); + typedef resolve_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl, query, io_service_impl_, handler); - if (work_io_service_) - { - start_work_thread(); - io_service_impl_.work_started(); - work_io_service_impl_.post_immediate_completion(ptr.get()); - ptr.release(); - } + start_resolve_op(p.p); + p.v = p.p = 0; } // Resolve an endpoint to a list of entries. iterator_type resolve(implementation_type&, const endpoint_type& endpoint, asio::error_code& ec) { - // First try resolving with the service name. If that fails try resolving - // but allow the service to be returned as a number. char host_name[NI_MAXHOST]; char service_name[NI_MAXSERV]; - int flags = endpoint.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; - socket_ops::getnameinfo(endpoint.data(), endpoint.size(), - host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); - if (ec) - { - flags |= NI_NUMERICSERV; - socket_ops::getnameinfo(endpoint.data(), endpoint.size(), - host_name, NI_MAXHOST, service_name, NI_MAXSERV, flags, ec); - } + socket_ops::sync_getnameinfo(endpoint.data(), endpoint.size(), + host_name, NI_MAXHOST, service_name, NI_MAXSERV, + endpoint.protocol().type(), ec); - if (ec) - return iterator_type(); - - return iterator_type::create(endpoint, host_name, service_name); + return ec ? iterator_type() : iterator_type::create( + endpoint, host_name, service_name); } - template - class resolve_endpoint_op - : public operation - { - public: - resolve_endpoint_op(implementation_type impl, const endpoint_type& ep, - io_service_impl& io_service_impl, Handler handler) - : operation(&resolve_endpoint_op::do_complete), - impl_(impl), - ep_(ep), - io_service_impl_(io_service_impl), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the operation object. - resolve_endpoint_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - if (owner) - { - if (owner != &o->io_service_impl_) - { - // The operation is being run on the worker io_service. Time to - // perform the resolver operation. - - if (o->impl_.expired()) - { - // THe operation has been cancelled. - o->ec_ = asio::error::operation_aborted; - } - else - { - // Perform the blocking endoint resolution operation. - char host_name[NI_MAXHOST]; - char service_name[NI_MAXSERV]; - int flags = o->ep_.protocol().type() == SOCK_DGRAM ? NI_DGRAM : 0; - socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(), - host_name, NI_MAXHOST, service_name, - NI_MAXSERV, flags, o->ec_); - if (o->ec_) - { - flags |= NI_NUMERICSERV; - socket_ops::getnameinfo(o->ep_.data(), o->ep_.size(), - host_name, NI_MAXHOST, service_name, - NI_MAXSERV, flags, o->ec_); - } - o->iter_ = iterator_type::create(o->ep_, host_name, service_name); - } - - o->io_service_impl_.post_deferred_completion(o); - ptr.release(); - } - else - { - // The operation has been returned to the main io_serice. The - // completion handler is ready to be delivered. - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object - // remains valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, o->ec_, o->iter_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - } - - private: - boost::weak_ptr impl_; - endpoint_type ep_; - io_service_impl& io_service_impl_; - Handler handler_; - asio::error_code ec_; - iterator_type iter_; - }; - // Asynchronously resolve an endpoint to a list of entries. template void async_resolve(implementation_type& impl, const endpoint_type& endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef resolve_endpoint_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, - impl, endpoint, io_service_impl_, handler); + typedef resolve_endpoint_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl, endpoint, io_service_impl_, handler); - if (work_io_service_) - { - start_work_thread(); - io_service_impl_.work_started(); - work_io_service_impl_.post_immediate_completion(ptr.get()); - ptr.release(); - } + start_resolve_op(p.p); + p.v = p.p = 0; } - -private: - // Helper class to run the work io_service in a thread. - class work_io_service_runner - { - public: - work_io_service_runner(asio::io_service& io_service) - : io_service_(io_service) {} - void operator()() { io_service_.run(); } - private: - asio::io_service& io_service_; - }; - - // Start the work thread if it's not already running. - void start_work_thread() - { - asio::detail::mutex::scoped_lock lock(mutex_); - if (!work_thread_) - { - work_thread_.reset(new asio::detail::thread( - work_io_service_runner(*work_io_service_))); - } - } - - // Mutex to protect access to internal data. - asio::detail::mutex mutex_; - - // The io_service implementation used to post completions. - io_service_impl& io_service_impl_; - - // Private io_service used for performing asynchronous host resolution. - boost::scoped_ptr work_io_service_; - - // The work io_service implementation used to post completions. - io_service_impl& work_io_service_impl_; - - // Work for the private io_service to perform. - boost::scoped_ptr work_; - - // Thread used for running the work io_service's run loop. - boost::scoped_ptr work_thread_; }; } // namespace detail diff --git a/ext/asio/asio/detail/resolver_service_base.hpp b/ext/asio/asio/detail/resolver_service_base.hpp new file mode 100644 index 0000000000..d6f20bdb70 --- /dev/null +++ b/ext/asio/asio/detail/resolver_service_base.hpp @@ -0,0 +1,123 @@ +// +// detail/resolver_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP +#define ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/thread.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class resolver_service_base +{ +public: + // The implementation type of the resolver. A cancellation token is used to + // indicate to the background thread that the operation has been cancelled. + typedef socket_ops::shared_cancel_token_type implementation_type; + + // Constructor. + ASIO_DECL resolver_service_base(asio::io_service& io_service); + + // Destructor. + ASIO_DECL ~resolver_service_base(); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown_service(); + + // Construct a new resolver implementation. + ASIO_DECL void construct(implementation_type& impl); + + // Destroy a resolver implementation. + ASIO_DECL void destroy(implementation_type&); + + // Cancel pending asynchronous operations. + ASIO_DECL void cancel(implementation_type& impl); + +protected: + // Helper function to start an asynchronous resolve operation. + ASIO_DECL void start_resolve_op(operation* op); + + // Helper class to perform exception-safe cleanup of addrinfo objects. + class auto_addrinfo + : private asio::detail::noncopyable + { + public: + explicit auto_addrinfo(asio::detail::addrinfo_type* ai) + : ai_(ai) + { + } + + ~auto_addrinfo() + { + if (ai_) + socket_ops::freeaddrinfo(ai_); + } + + operator asio::detail::addrinfo_type*() + { + return ai_; + } + + private: + asio::detail::addrinfo_type* ai_; + }; + + // Helper class to run the work io_service in a thread. + class work_io_service_runner; + + // Start the work thread if it's not already running. + ASIO_DECL void start_work_thread(); + + // The io_service implementation used to post completions. + io_service_impl& io_service_impl_; + +private: + // Mutex to protect access to internal data. + asio::detail::mutex mutex_; + + // Private io_service used for performing asynchronous host resolution. + boost::scoped_ptr work_io_service_; + + // The work io_service implementation used to post completions. + io_service_impl& work_io_service_impl_; + + // Work for the private io_service to perform. + boost::scoped_ptr work_; + + // Thread used for running the work io_service's run loop. + boost::scoped_ptr work_thread_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/resolver_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_DETAIL_RESOLVER_SERVICE_BASE_HPP diff --git a/ext/asio/asio/detail/scoped_lock.hpp b/ext/asio/asio/detail/scoped_lock.hpp index e6f6ba59a0..69b572530b 100644 --- a/ext/asio/asio/detail/scoped_lock.hpp +++ b/ext/asio/asio/detail/scoped_lock.hpp @@ -1,8 +1,8 @@ // -// scoped_lock.hpp -// ~~~~~~~~~~~~~~~ +// detail/scoped_lock.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - #include "asio/detail/noncopyable.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/select_interrupter.hpp b/ext/asio/asio/detail/select_interrupter.hpp index ff5505bebc..e975ddca00 100644 --- a/ext/asio/asio/detail/select_interrupter.hpp +++ b/ext/asio/asio/detail/select_interrupter.hpp @@ -1,8 +1,8 @@ // -// select_interrupter.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,23 +15,20 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) # include "asio/detail/socket_select_interrupter.hpp" -#else +#elif defined(ASIO_HAS_EVENTFD) # include "asio/detail/eventfd_select_interrupter.hpp" +#else # include "asio/detail/pipe_select_interrupter.hpp" #endif namespace asio { namespace detail { -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) typedef socket_select_interrupter select_interrupter; #elif defined(ASIO_HAS_EVENTFD) typedef eventfd_select_interrupter select_interrupter; @@ -42,6 +39,4 @@ typedef pipe_select_interrupter select_interrupter; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/asio/detail/select_reactor.hpp b/ext/asio/asio/detail/select_reactor.hpp index 798611b281..161305f8c7 100644 --- a/ext/asio/asio/detail/select_reactor.hpp +++ b/ext/asio/asio/detail/select_reactor.hpp @@ -1,8 +1,8 @@ // -// select_reactor.hpp -// ~~~~~~~~~~~~~~~~~~ +// detail/select_reactor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,41 +15,38 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/socket_types.hpp" // Must come before posix_time. +#if defined(ASIO_HAS_IOCP) \ + || (!defined(ASIO_HAS_DEV_POLL) \ + && !defined(ASIO_HAS_EPOLL) \ + && !defined(ASIO_HAS_KQUEUE)) -#include "asio/detail/push_options.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/io_service.hpp" -#include "asio/detail/bind_handler.hpp" -#include "asio/detail/fd_set_adapter.hpp" #include "asio/detail/mutex.hpp" -#include "asio/detail/noncopyable.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/reactor_op_queue.hpp" #include "asio/detail/select_interrupter.hpp" #include "asio/detail/select_reactor_fwd.hpp" -#include "asio/detail/service_base.hpp" -#include "asio/detail/signal_blocker.hpp" -#include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" -#include "asio/detail/thread.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" +#include "asio/io_service.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/thread.hpp" +#endif // defined(ASIO_HAS_IOCP) + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { -template class select_reactor - : public asio::detail::service_base > + : public asio::detail::service_base { public: #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -66,280 +63,91 @@ public: }; // Constructor. - select_reactor(asio::io_service& io_service) - : asio::detail::service_base< - select_reactor >(io_service), - io_service_(use_service(io_service)), - mutex_(), - interrupter_(), - stop_thread_(false), - thread_(0), - shutdown_(false) - { - if (Own_Thread) - { - asio::detail::signal_blocker sb; - thread_ = new asio::detail::thread( - bind_handler(&select_reactor::call_run_thread, this)); - } - } + ASIO_DECL select_reactor(asio::io_service& io_service); // Destructor. - ~select_reactor() - { - shutdown_service(); - } + ASIO_DECL ~select_reactor(); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - asio::detail::mutex::scoped_lock lock(mutex_); - shutdown_ = true; - stop_thread_ = true; - lock.unlock(); - - if (Own_Thread) - { - if (thread_) - { - interrupter_.interrupt(); - thread_->join(); - delete thread_; - thread_ = 0; - } - } - - op_queue ops; - - for (int i = 0; i < max_ops; ++i) - op_queue_[i].get_all_operations(ops); - - timer_queues_.get_all_timers(ops); - } + ASIO_DECL void shutdown_service(); // Initialise the task, but only if the reactor is not in its own thread. - void init_task() - { - io_service_.init_task(); - } + ASIO_DECL void init_task(); // Register a socket with the reactor. Returns 0 on success, system error // code on failure. - int register_descriptor(socket_type, per_descriptor_data&) + ASIO_DECL int register_descriptor(socket_type, per_descriptor_data&); + + // Post a reactor operation for immediate completion. + void post_immediate_completion(reactor_op* op) { - return 0; + io_service_.post_immediate_completion(op); } // Start a new operation. The reactor operation will be performed when the // given descriptor is flagged as ready, or an error has occurred. - void start_op(int op_type, socket_type descriptor, - per_descriptor_data&, reactor_op* op, bool) - { - asio::detail::mutex::scoped_lock lock(mutex_); - if (!shutdown_) - { - bool first = op_queue_[op_type].enqueue_operation(descriptor, op); - io_service_.work_started(); - if (first) - interrupter_.interrupt(); - } - } + ASIO_DECL void start_op(int op_type, socket_type descriptor, + per_descriptor_data&, reactor_op* op, bool); // Cancel all operations associated with the given descriptor. The // handlers associated with the descriptor will be invoked with the // operation_aborted error. - void cancel_ops(socket_type descriptor, per_descriptor_data&) - { - asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor, asio::error::operation_aborted); - } + ASIO_DECL void cancel_ops(socket_type descriptor, per_descriptor_data&); // Cancel any operations that are running against the descriptor and remove // its registration from the reactor. - void close_descriptor(socket_type descriptor, per_descriptor_data&) - { - asio::detail::mutex::scoped_lock lock(mutex_); - cancel_ops_unlocked(descriptor, asio::error::operation_aborted); - } + ASIO_DECL void close_descriptor(socket_type descriptor, + per_descriptor_data&); // Add a new timer queue to the reactor. template - void add_timer_queue(timer_queue& timer_queue) - { - asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.insert(&timer_queue); - } + void add_timer_queue(timer_queue& queue); // Remove a timer queue from the reactor. template - void remove_timer_queue(timer_queue& timer_queue) - { - asio::detail::mutex::scoped_lock lock(mutex_); - timer_queues_.erase(&timer_queue); - } + void remove_timer_queue(timer_queue& queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template - void schedule_timer(timer_queue& timer_queue, - const typename Time_Traits::time_type& time, timer_op* op, void* token) - { - asio::detail::mutex::scoped_lock lock(mutex_); - if (!shutdown_) - { - bool earliest = timer_queue.enqueue_timer(time, op, token); - io_service_.work_started(); - if (earliest) - interrupter_.interrupt(); - } - } + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer operations associated with the given token. Returns the // number of operations that have been posted or dispatched. template - std::size_t cancel_timer(timer_queue& timer_queue, void* token) - { - asio::detail::mutex::scoped_lock lock(mutex_); - op_queue ops; - std::size_t n = timer_queue.cancel_timer(token, ops); - lock.unlock(); - io_service_.post_deferred_completions(ops); - return n; - } + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer); // Run select once until interrupted or events are ready to be dispatched. - void run(bool block, op_queue& ops) - { - asio::detail::mutex::scoped_lock lock(mutex_); - - // Check if the thread is supposed to stop. - if (Own_Thread) - if (stop_thread_) - return; - - // Set up the descriptor sets. - fd_set_adapter fds[max_select_ops]; - fds[read_op].set(interrupter_.read_descriptor()); - socket_type max_fd = 0; - bool have_work_to_do = !timer_queues_.all_empty(); - for (int i = 0; i < max_select_ops; ++i) - { - have_work_to_do = have_work_to_do || !op_queue_[i].empty(); - op_queue_[i].get_descriptors(fds[i], ops); - if (fds[i].max_descriptor() > max_fd) - max_fd = fds[i].max_descriptor(); - } - -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - // Connection operations on Windows use both except and write fd_sets. - have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty(); - op_queue_[connect_op].get_descriptors(fds[write_op], ops); - if (fds[write_op].max_descriptor() > max_fd) - max_fd = fds[write_op].max_descriptor(); - op_queue_[connect_op].get_descriptors(fds[except_op], ops); - if (fds[except_op].max_descriptor() > max_fd) - max_fd = fds[except_op].max_descriptor(); -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - - // We can return immediately if there's no work to do and the reactor is - // not supposed to block. - if (!block && !have_work_to_do) - return; - - // Determine how long to block while waiting for events. - timeval tv_buf = { 0, 0 }; - timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; - - lock.unlock(); - - // Block on the select call until descriptors become ready. - asio::error_code ec; - int retval = socket_ops::select(static_cast(max_fd + 1), - fds[read_op], fds[write_op], fds[except_op], tv, ec); - - // Reset the interrupter. - if (retval > 0 && fds[read_op].is_set(interrupter_.read_descriptor())) - interrupter_.reset(); - - lock.lock(); - - // Dispatch all ready operations. - if (retval > 0) - { -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - // Connection operations on Windows use both except and write fd_sets. - op_queue_[connect_op].perform_operations_for_descriptors( - fds[except_op], ops); - op_queue_[connect_op].perform_operations_for_descriptors( - fds[write_op], ops); -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - - // Exception operations must be processed first to ensure that any - // out-of-band data is read before normal data. - for (int i = max_select_ops - 1; i >= 0; --i) - op_queue_[i].perform_operations_for_descriptors(fds[i], ops); - } - timer_queues_.get_ready_timers(ops); - } + ASIO_DECL void run(bool block, op_queue& ops); // Interrupt the select loop. - void interrupt() - { - interrupter_.interrupt(); - } + ASIO_DECL void interrupt(); private: +#if defined(ASIO_HAS_IOCP) // Run the select loop in the thread. - void run_thread() - { - if (Own_Thread) - { - asio::detail::mutex::scoped_lock lock(mutex_); - while (!stop_thread_) - { - lock.unlock(); - op_queue ops; - run(true, ops); - io_service_.post_deferred_completions(ops); - lock.lock(); - } - } - } + ASIO_DECL void run_thread(); // Entry point for the select loop thread. - static void call_run_thread(select_reactor* reactor) - { - if (Own_Thread) - { - reactor->run_thread(); - } - } + ASIO_DECL static void call_run_thread(select_reactor* reactor); +#endif // defined(ASIO_HAS_IOCP) + + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); + + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); // Get the timeout value for the select call. - timeval* get_timeout(timeval& tv) - { - // By default we will wait no longer than 5 minutes. This will ensure that - // any changes to the system clock are detected after no longer than this. - long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); - tv.tv_sec = usec / 1000000; - tv.tv_usec = usec % 1000000; - return &tv; - } + ASIO_DECL timeval* get_timeout(timeval& tv); // Cancel all operations associated with the given descriptor. This function // does not acquire the select_reactor's mutex. - void cancel_ops_unlocked(socket_type descriptor, - const asio::error_code& ec) - { - bool need_interrupt = false; - op_queue ops; - for (int i = 0; i < max_ops; ++i) - need_interrupt = op_queue_[i].cancel_operations( - descriptor, ops, ec) || need_interrupt; - io_service_.post_deferred_completions(ops); - if (need_interrupt) - interrupter_.interrupt(); - } + ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, + const asio::error_code& ec); // The io_service implementation used to post completions. io_service_impl& io_service_; @@ -356,11 +164,13 @@ private: // The timer queues. timer_queue_set timer_queues_; +#if defined(ASIO_HAS_IOCP) // Does the reactor loop thread need to stop. bool stop_thread_; // The thread that is running the reactor loop. asio::detail::thread* thread_; +#endif // defined(ASIO_HAS_IOCP) // Whether the service has been shut down. bool shutdown_; @@ -371,4 +181,14 @@ private: #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/select_reactor.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/select_reactor.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + // || (!defined(ASIO_HAS_DEV_POLL) + // && !defined(ASIO_HAS_EPOLL) + // && !defined(ASIO_HAS_KQUEUE)) + #endif // ASIO_DETAIL_SELECT_REACTOR_HPP diff --git a/ext/asio/asio/detail/select_reactor_fwd.hpp b/ext/asio/asio/detail/select_reactor_fwd.hpp index 0b72e7e8aa..04e5ecdcac 100644 --- a/ext/asio/asio/detail/select_reactor_fwd.hpp +++ b/ext/asio/asio/detail/select_reactor_fwd.hpp @@ -1,8 +1,8 @@ // -// select_reactor_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/select_reactor_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - namespace asio { namespace detail { -template class select_reactor; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_SELECT_REACTOR_FWD_HPP diff --git a/ext/asio/asio/detail/service_registry.hpp b/ext/asio/asio/detail/service_registry.hpp index f80b4b6953..34b946d19b 100644 --- a/ext/asio/asio/detail/service_registry.hpp +++ b/ext/asio/asio/detail/service_registry.hpp @@ -1,8 +1,8 @@ // -// service_registry.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// detail/service_registry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" - -#include "asio/io_service.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/noncopyable.hpp" -#include "asio/detail/service_id.hpp" +#include "asio/io_service.hpp" #if defined(BOOST_NO_TYPEID) # if !defined(ASIO_NO_TYPEID) @@ -32,6 +27,8 @@ # endif // !defined(ASIO_NO_TYPEID) #endif // defined(BOOST_NO_TYPEID) +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -55,98 +52,43 @@ class service_registry { public: // Constructor. - service_registry(asio::io_service& o) - : owner_(o), - first_service_(0) - { - } + ASIO_DECL service_registry(asio::io_service& o); // Destructor. - ~service_registry() - { - // Shutdown all services. This must be done in a separate loop before the - // services are destroyed since the destructors of user-defined handler - // objects may try to access other service objects. - asio::io_service::service* service = first_service_; - while (service) - { - service->shutdown_service(); - service = service->next_; - } - - // Destroy all services. - while (first_service_) - { - asio::io_service::service* next_service = first_service_->next_; - destroy(first_service_); - first_service_ = next_service; - } - } + ASIO_DECL ~service_registry(); // Get the service object corresponding to the specified service type. Will // create a new service object automatically if no such object already // exists. Ownership of the service object is not transferred to the caller. template - Service& use_service() - { - asio::io_service::service::key key; - init_key(key, Service::id); - factory_type factory = &service_registry::create; - return *static_cast(do_use_service(key, factory)); - } + Service& use_service(); - // Add a service object. Returns false on error, in which case ownership of - // the object is retained by the caller. + // Add a service object. Throws on error, in which case ownership of the + // object is retained by the caller. template - bool add_service(Service* new_service) - { - asio::io_service::service::key key; - init_key(key, Service::id); - return do_add_service(key, new_service); - } + void add_service(Service* new_service); // Check whether a service object of the specified type already exists. template - bool has_service() const - { - asio::io_service::service::key key; - init_key(key, Service::id); - return do_has_service(key); - } + bool has_service() const; private: // Initialise a service's key based on its id. - void init_key(asio::io_service::service::key& key, - const asio::io_service::id& id) - { - key.type_info_ = 0; - key.id_ = &id; - } + ASIO_DECL static void init_key( + asio::io_service::service::key& key, + const asio::io_service::id& id); #if !defined(ASIO_NO_TYPEID) // Initialise a service's key based on its id. template - void init_key(asio::io_service::service::key& key, - const asio::detail::service_id& /*id*/) - { - key.type_info_ = &typeid(typeid_wrapper); - key.id_ = 0; - } + static void init_key(asio::io_service::service::key& key, + const asio::detail::service_id& /*id*/); #endif // !defined(ASIO_NO_TYPEID) // Check if a service matches the given id. - static bool keys_match( + ASIO_DECL static bool keys_match( const asio::io_service::service::key& key1, - const asio::io_service::service::key& key2) - { - if (key1.id_ && key2.id_) - if (key1.id_ == key2.id_) - return true; - if (key1.type_info_ && key2.type_info_) - if (*key1.type_info_ == *key2.type_info_) - return true; - return false; - } + const asio::io_service::service::key& key2); // The type of a factory function used for creating a service instance. typedef asio::io_service::service* @@ -155,18 +97,15 @@ private: // Factory function for creating a service instance. template static asio::io_service::service* create( - asio::io_service& owner) - { - return new Service(owner); - } + asio::io_service& owner); // Destroy a service instance. - static void destroy(asio::io_service::service* service) - { - delete service; - } + ASIO_DECL static void destroy( + asio::io_service::service* service); // Helper class to manage service pointers. + struct auto_service_ptr; + friend struct auto_service_ptr; struct auto_service_ptr { asio::io_service::service* ptr_; @@ -176,86 +115,19 @@ private: // Get the service object corresponding to the specified service key. Will // create a new service object automatically if no such object already // exists. Ownership of the service object is not transferred to the caller. - asio::io_service::service* do_use_service( + ASIO_DECL asio::io_service::service* do_use_service( const asio::io_service::service::key& key, - factory_type factory) - { - asio::detail::mutex::scoped_lock lock(mutex_); - - // First see if there is an existing service object with the given key. - asio::io_service::service* service = first_service_; - while (service) - { - if (keys_match(service->key_, key)) - return service; - service = service->next_; - } - - // Create a new service object. The service registry's mutex is not locked - // at this time to allow for nested calls into this function from the new - // service's constructor. - lock.unlock(); - auto_service_ptr new_service = { factory(owner_) }; - new_service.ptr_->key_ = key; - lock.lock(); - - // Check that nobody else created another service object of the same type - // while the lock was released. - service = first_service_; - while (service) - { - if (keys_match(service->key_, key)) - return service; - service = service->next_; - } - - // Service was successfully initialised, pass ownership to registry. - new_service.ptr_->next_ = first_service_; - first_service_ = new_service.ptr_; - new_service.ptr_ = 0; - return first_service_; - } + factory_type factory); // Add a service object. Returns false on error, in which case ownership of // the object is retained by the caller. - bool do_add_service( + ASIO_DECL void do_add_service( const asio::io_service::service::key& key, - asio::io_service::service* new_service) - { - asio::detail::mutex::scoped_lock lock(mutex_); - - // Check if there is an existing service object with the given key. - asio::io_service::service* service = first_service_; - while (service) - { - if (keys_match(service->key_, key)) - return false; - service = service->next_; - } - - // Take ownership of the service object. - new_service->key_ = key; - new_service->next_ = first_service_; - first_service_ = new_service; - - return true; - } + asio::io_service::service* new_service); // Check whether a service object with the specified key already exists. - bool do_has_service(const asio::io_service::service::key& key) const - { - asio::detail::mutex::scoped_lock lock(mutex_); - - asio::io_service::service* service = first_service_; - while (service) - { - if (keys_match(service->key_, key)) - return true; - service = service->next_; - } - - return false; - } + ASIO_DECL bool do_has_service( + const asio::io_service::service::key& key) const; // Mutex to protect access to internal data. mutable asio::detail::mutex mutex_; @@ -272,4 +144,9 @@ private: #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/service_registry.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/service_registry.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_DETAIL_SERVICE_REGISTRY_HPP diff --git a/ext/asio/asio/detail/service_registry_fwd.hpp b/ext/asio/asio/detail/service_registry_fwd.hpp index 423bb4beef..4108c1a100 100644 --- a/ext/asio/asio/detail/service_registry_fwd.hpp +++ b/ext/asio/asio/detail/service_registry_fwd.hpp @@ -1,8 +1,8 @@ // -// service_registry_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/service_registry_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,8 +15,6 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - namespace asio { namespace detail { @@ -25,6 +23,4 @@ class service_registry; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_SERVICE_REGISTRY_FWD_HPP diff --git a/ext/asio/asio/detail/shared_ptr.hpp b/ext/asio/asio/detail/shared_ptr.hpp new file mode 100644 index 0000000000..cd496f5d1f --- /dev/null +++ b/ext/asio/asio/detail/shared_ptr.hpp @@ -0,0 +1,38 @@ +// +// detail/shared_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_SHARED_PTR_HPP +#define ASIO_DETAIL_SHARED_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +# include +#else +# include +#endif + +namespace asio { +namespace detail { + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +using std::shared_ptr; +#else +using boost::shared_ptr; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_SHARED_PTR_HPP diff --git a/ext/asio/asio/detail/signal_blocker.hpp b/ext/asio/asio/detail/signal_blocker.hpp index 52f70c8578..89f45ca1dc 100644 --- a/ext/asio/asio/detail/signal_blocker.hpp +++ b/ext/asio/asio/detail/signal_blocker.hpp @@ -1,8 +1,8 @@ // -// signal_blocker.hpp -// ~~~~~~~~~~~~~~~~~~ +// detail/signal_blocker.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) \ + || defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) # include "asio/detail/null_signal_blocker.hpp" -#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) -# include "asio/detail/win_signal_blocker.hpp" #elif defined(BOOST_HAS_PTHREADS) # include "asio/detail/posix_signal_blocker.hpp" #else @@ -34,10 +29,9 @@ namespace asio { namespace detail { -#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) +#if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) \ + || defined(BOOST_WINDOWS) || defined(__CYGWIN__) || defined(__SYMBIAN32__) typedef null_signal_blocker signal_blocker; -#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) -typedef win_signal_blocker signal_blocker; #elif defined(BOOST_HAS_PTHREADS) typedef posix_signal_blocker signal_blocker; #endif @@ -45,6 +39,4 @@ typedef posix_signal_blocker signal_blocker; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_SIGNAL_BLOCKER_HPP diff --git a/ext/asio/asio/detail/signal_init.hpp b/ext/asio/asio/detail/signal_init.hpp index 12f17d37ac..80925f6978 100644 --- a/ext/asio/asio/detail/signal_init.hpp +++ b/ext/asio/asio/detail/signal_init.hpp @@ -1,8 +1,8 @@ // -// signal_init.hpp -// ~~~~~~~~~~~~~~~ +// detail/signal_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -#include "asio/detail/push_options.hpp" #include -#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -44,8 +40,8 @@ public: } // namespace detail } // namespace asio -#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + #endif // ASIO_DETAIL_SIGNAL_INIT_HPP diff --git a/ext/asio/asio/detail/socket_holder.hpp b/ext/asio/asio/detail/socket_holder.hpp index 82a38848a5..cb401759d0 100644 --- a/ext/asio/asio/detail/socket_holder.hpp +++ b/ext/asio/asio/detail/socket_holder.hpp @@ -1,8 +1,8 @@ // -// socket_holder.hpp -// ~~~~~~~~~~~~~~~~~ +// detail/socket_holder.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_ops.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -46,7 +47,8 @@ public: if (socket_ != invalid_socket) { asio::error_code ec; - socket_ops::close(socket_, ec); + socket_ops::state_type state = 0; + socket_ops::close(socket_, state, true, ec); } } @@ -62,7 +64,8 @@ public: if (socket_ != invalid_socket) { asio::error_code ec; - socket_ops::close(socket_, ec); + socket_ops::state_type state = 0; + socket_ops::close(socket_, state, true, ec); socket_ = invalid_socket; } } diff --git a/ext/asio/asio/detail/socket_ops.hpp b/ext/asio/asio/detail/socket_ops.hpp index 1a863a9175..8ad464c909 100644 --- a/ext/asio/asio/detail/socket_ops.hpp +++ b/ext/asio/asio/detail/socket_ops.hpp @@ -1,8 +1,8 @@ // -// socket_ops.hpp -// ~~~~~~~~~~~~~~ +// detail/socket_ops.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,193 +15,102 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" +#include "asio/error_code.hpp" +#include "asio/detail/shared_ptr.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/detail/weak_ptr.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { namespace socket_ops { -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) -struct msghdr { int msg_namelen; }; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - -#if defined(__hpux) -// HP-UX doesn't declare these functions extern "C", so they are declared again -// here to avoid linker errors about undefined symbols. -extern "C" char* if_indextoname(unsigned int, char*); -extern "C" unsigned int if_nametoindex(const char*); -#endif // defined(__hpux) - -inline void clear_error(asio::error_code& ec) +// Socket state bits. +enum { -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - WSASetLastError(0); -#else - errno = 0; -#endif - ec = asio::error_code(); -} + // The user wants a non-blocking socket. + user_set_non_blocking = 1, -template -inline ReturnType error_wrapper(ReturnType return_value, - asio::error_code& ec) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - ec = asio::error_code(WSAGetLastError(), - asio::error::get_system_category()); -#else - ec = asio::error_code(errno, - asio::error::get_system_category()); -#endif - return return_value; -} + // The socket has been set non-blocking. + internal_non_blocking = 2, -template -inline socket_type call_accept(SockLenType msghdr::*, - socket_type s, socket_addr_type* addr, std::size_t* addrlen) -{ - SockLenType tmp_addrlen = addrlen ? (SockLenType)*addrlen : 0; - socket_type result = ::accept(s, addr, addrlen ? &tmp_addrlen : 0); - if (addrlen) - *addrlen = (std::size_t)tmp_addrlen; - return result; -} + // Helper "state" used to determine whether the socket is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking, -inline socket_type accept(socket_type s, socket_addr_type* addr, - std::size_t* addrlen, asio::error_code& ec) -{ - clear_error(ec); + // User wants connection_aborted errors, which are disabled by default. + enable_connection_aborted = 4, - socket_type new_s = error_wrapper(call_accept( - &msghdr::msg_namelen, s, addr, addrlen), ec); - if (new_s == invalid_socket) - return new_s; + // The user set the linger option. Needs to be checked when closing. + user_set_linger = 8, -#if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) - int optval = 1; - int result = error_wrapper(::setsockopt(new_s, - SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); - if (result != 0) - { - ::close(new_s); - return invalid_socket; - } -#endif + // The socket is stream-oriented. + stream_oriented = 16, - clear_error(ec); - return new_s; -} + // The socket is datagram-oriented. + datagram_oriented = 32 +}; -template -inline int call_bind(SockLenType msghdr::*, - socket_type s, const socket_addr_type* addr, std::size_t addrlen) -{ - return ::bind(s, addr, (SockLenType)addrlen); -} +typedef unsigned char state_type; -inline int bind(socket_type s, const socket_addr_type* addr, - std::size_t addrlen, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(call_bind( - &msghdr::msg_namelen, s, addr, addrlen), ec); - if (result == 0) - clear_error(ec); - return result; -} +struct noop_deleter { void operator()(void*) {} }; +typedef shared_ptr shared_cancel_token_type; +typedef weak_ptr weak_cancel_token_type; -inline int close(socket_type s, asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - int result = error_wrapper(::closesocket(s), ec); -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - int result = error_wrapper(::close(s), ec); -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - if (result == 0) - clear_error(ec); - return result; -} +ASIO_DECL socket_type accept(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); -inline int shutdown(socket_type s, int what, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::shutdown(s, what), ec); - if (result == 0) - clear_error(ec); - return result; -} +ASIO_DECL socket_type sync_accept(socket_type s, + state_type state, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); -template -inline int call_connect(SockLenType msghdr::*, - socket_type s, const socket_addr_type* addr, std::size_t addrlen) -{ - return ::connect(s, addr, (SockLenType)addrlen); -} +#if defined(ASIO_HAS_IOCP) -inline int connect(socket_type s, const socket_addr_type* addr, - std::size_t addrlen, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(call_connect( - &msghdr::msg_namelen, s, addr, addrlen), ec); - if (result == 0) - clear_error(ec); - return result; -} +ASIO_DECL void complete_iocp_accept(socket_type s, + void* output_buffer, DWORD address_length, + socket_addr_type* addr, std::size_t* addrlen, + socket_type new_socket, asio::error_code& ec); -inline int socketpair(int af, int type, int protocol, - socket_type sv[2], asio::error_code& ec) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - (void)(af); - (void)(type); - (void)(protocol); - (void)(sv); - ec = asio::error::operation_not_supported; - return -1; -#else - clear_error(ec); - int result = error_wrapper(::socketpair(af, type, protocol, sv), ec); - if (result == 0) - clear_error(ec); - return result; -#endif -} +#else // defined(ASIO_HAS_IOCP) -inline int listen(socket_type s, int backlog, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::listen(s, backlog), ec); - if (result == 0) - clear_error(ec); - return result; -} +ASIO_DECL bool non_blocking_accept(socket_type s, + state_type state, socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, socket_type& new_socket); -inline void init_buf_iov_base(void*& base, void* addr) -{ - base = addr; -} +#endif // defined(ASIO_HAS_IOCP) -template -inline void init_buf_iov_base(T& base, void* addr) -{ - base = static_cast(addr); -} +ASIO_DECL int bind(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL int close(socket_type s, state_type& state, + bool destruction, asio::error_code& ec); + +ASIO_DECL bool set_internal_non_blocking(socket_type s, + state_type& state, asio::error_code& ec); + +ASIO_DECL int shutdown(socket_type s, + int what, asio::error_code& ec); + +ASIO_DECL int connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL void sync_connect(socket_type s, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +ASIO_DECL bool non_blocking_connect( + socket_type s, asio::error_code& ec); + +ASIO_DECL int socketpair(int af, int type, int protocol, + socket_type sv[2], asio::error_code& ec); + +ASIO_DECL bool sockatmark(socket_type s, asio::error_code& ec); + +ASIO_DECL size_t available(socket_type s, asio::error_code& ec); + +ASIO_DECL int listen(socket_type s, + int backlog, asio::error_code& ec); #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) typedef WSABUF buf; @@ -209,1699 +118,163 @@ typedef WSABUF buf; typedef iovec buf; #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -inline void init_buf(buf& b, void* data, size_t size) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - b.buf = static_cast(data); - b.len = static_cast(size); -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - init_buf_iov_base(b.iov_base, data); - b.iov_len = size; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +ASIO_DECL void init_buf(buf& b, void* data, size_t size); -inline void init_buf(buf& b, const void* data, size_t size) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - b.buf = static_cast(const_cast(data)); - b.len = static_cast(size); -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - init_buf_iov_base(b.iov_base, const_cast(data)); - b.iov_len = size; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +ASIO_DECL void init_buf(buf& b, const void* data, size_t size); -inline void init_msghdr_msg_name(void*& name, socket_addr_type* addr) -{ - name = addr; -} +ASIO_DECL int recv(socket_type s, buf* bufs, size_t count, int flags, + asio::error_code& ec); -inline void init_msghdr_msg_name(void*& name, const socket_addr_type* addr) -{ - name = const_cast(addr); -} +ASIO_DECL size_t sync_recv(socket_type s, state_type state, buf* bufs, + size_t count, int flags, bool all_empty, asio::error_code& ec); -template -inline void init_msghdr_msg_name(T& name, socket_addr_type* addr) -{ - name = reinterpret_cast(addr); -} +#if defined(ASIO_HAS_IOCP) -template -inline void init_msghdr_msg_name(T& name, const socket_addr_type* addr) -{ - name = reinterpret_cast(const_cast(addr)); -} +ASIO_DECL void complete_iocp_recv(state_type state, + const weak_cancel_token_type& cancel_token, bool all_empty, + asio::error_code& ec, size_t bytes_transferred); -inline int recv(socket_type s, buf* bufs, size_t count, int flags, - asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - // Receive some data. - DWORD recv_buf_count = static_cast(count); - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int result = error_wrapper(::WSARecv(s, bufs, - recv_buf_count, &bytes_transferred, &recv_flags, 0, 0), ec); - if (result != 0) - return -1; - clear_error(ec); - return bytes_transferred; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - msghdr msg = msghdr(); - msg.msg_iov = bufs; - msg.msg_iovlen = count; - int result = error_wrapper(::recvmsg(s, &msg, flags), ec); - if (result >= 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +#else // defined(ASIO_HAS_IOCP) -inline int recvfrom(socket_type s, buf* bufs, size_t count, int flags, +ASIO_DECL bool non_blocking_recv(socket_type s, + buf* bufs, size_t count, int flags, bool is_stream, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL int recvfrom(socket_type s, buf* bufs, size_t count, int flags, socket_addr_type* addr, std::size_t* addrlen, - asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - // Receive some data. - DWORD recv_buf_count = static_cast(count); - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int tmp_addrlen = (int)*addrlen; - int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, - &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec); - *addrlen = (std::size_t)tmp_addrlen; - if (result != 0) - return -1; - clear_error(ec); - return bytes_transferred; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - msghdr msg = msghdr(); - init_msghdr_msg_name(msg.msg_name, addr); - msg.msg_namelen = *addrlen; - msg.msg_iov = bufs; - msg.msg_iovlen = count; - int result = error_wrapper(::recvmsg(s, &msg, flags), ec); - *addrlen = msg.msg_namelen; - if (result >= 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} + asio::error_code& ec); -inline int send(socket_type s, const buf* bufs, size_t count, int flags, - asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - // Send the data. - DWORD send_buf_count = static_cast(count); - DWORD bytes_transferred = 0; - DWORD send_flags = flags; - int result = error_wrapper(::WSASend(s, const_cast(bufs), - send_buf_count, &bytes_transferred, send_flags, 0, 0), ec); - if (result != 0) - return -1; - clear_error(ec); - return bytes_transferred; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - msghdr msg = msghdr(); - msg.msg_iov = const_cast(bufs); - msg.msg_iovlen = count; -#if defined(__linux__) - flags |= MSG_NOSIGNAL; -#endif // defined(__linux__) - int result = error_wrapper(::sendmsg(s, &msg, flags), ec); - if (result >= 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +ASIO_DECL size_t sync_recvfrom(socket_type s, state_type state, + buf* bufs, size_t count, int flags, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); -inline int sendto(socket_type s, const buf* bufs, size_t count, int flags, +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_recvfrom( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_recvfrom(socket_type s, + buf* bufs, size_t count, int flags, + socket_addr_type* addr, std::size_t* addrlen, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL int send(socket_type s, const buf* bufs, + size_t count, int flags, asio::error_code& ec); + +ASIO_DECL size_t sync_send(socket_type s, state_type state, + const buf* bufs, size_t count, int flags, + bool all_empty, asio::error_code& ec); + +#if defined(ASIO_HAS_IOCP) + +ASIO_DECL void complete_iocp_send( + const weak_cancel_token_type& cancel_token, + asio::error_code& ec); + +#else // defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_send(socket_type s, + const buf* bufs, size_t count, int flags, + asio::error_code& ec, size_t& bytes_transferred); + +#endif // defined(ASIO_HAS_IOCP) + +ASIO_DECL int sendto(socket_type s, const buf* bufs, size_t count, + int flags, const socket_addr_type* addr, std::size_t addrlen, + asio::error_code& ec); + +ASIO_DECL size_t sync_sendto(socket_type s, state_type state, + const buf* bufs, size_t count, int flags, const socket_addr_type* addr, + std::size_t addrlen, asio::error_code& ec); + +#if !defined(ASIO_HAS_IOCP) + +ASIO_DECL bool non_blocking_sendto(socket_type s, + const buf* bufs, size_t count, int flags, const socket_addr_type* addr, std::size_t addrlen, - asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - // Send the data. - DWORD send_buf_count = static_cast(count); - DWORD bytes_transferred = 0; - int result = error_wrapper(::WSASendTo(s, const_cast(bufs), - send_buf_count, &bytes_transferred, flags, addr, - static_cast(addrlen), 0, 0), ec); - if (result != 0) - return -1; - clear_error(ec); - return bytes_transferred; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - msghdr msg = msghdr(); - init_msghdr_msg_name(msg.msg_name, addr); - msg.msg_namelen = addrlen; - msg.msg_iov = const_cast(bufs); - msg.msg_iovlen = count; -#if defined(__linux__) - flags |= MSG_NOSIGNAL; -#endif // defined(__linux__) - int result = error_wrapper(::sendmsg(s, &msg, flags), ec); - if (result >= 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} + asio::error_code& ec, size_t& bytes_transferred); -inline socket_type socket(int af, int type, int protocol, - asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - socket_type s = error_wrapper(::WSASocket(af, type, protocol, 0, 0, - WSA_FLAG_OVERLAPPED), ec); - if (s == invalid_socket) - return s; +#endif // !defined(ASIO_HAS_IOCP) - if (af == AF_INET6) - { - // Try to enable the POSIX default behaviour of having IPV6_V6ONLY set to - // false. This will only succeed on Windows Vista and later versions of - // Windows, where a dual-stack IPv4/v6 implementation is available. - DWORD optval = 0; - ::setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, - reinterpret_cast(&optval), sizeof(optval)); - } +ASIO_DECL socket_type socket(int af, int type, int protocol, + asio::error_code& ec); - clear_error(ec); +ASIO_DECL int setsockopt(socket_type s, state_type& state, + int level, int optname, const void* optval, + std::size_t optlen, asio::error_code& ec); - return s; -#elif defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__) - socket_type s = error_wrapper(::socket(af, type, protocol), ec); - if (s == invalid_socket) - return s; +ASIO_DECL int getsockopt(socket_type s, state_type state, + int level, int optname, void* optval, + size_t* optlen, asio::error_code& ec); - int optval = 1; - int result = error_wrapper(::setsockopt(s, - SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)), ec); - if (result != 0) - { - ::close(s); - return invalid_socket; - } +ASIO_DECL int getpeername(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, bool cached, asio::error_code& ec); - return s; -#else - int s = error_wrapper(::socket(af, type, protocol), ec); - if (s >= 0) - clear_error(ec); - return s; -#endif -} +ASIO_DECL int getsockname(socket_type s, socket_addr_type* addr, + std::size_t* addrlen, asio::error_code& ec); -template -inline int call_setsockopt(SockLenType msghdr::*, - socket_type s, int level, int optname, - const void* optval, std::size_t optlen) -{ - return ::setsockopt(s, level, optname, - (const char*)optval, (SockLenType)optlen); -} +ASIO_DECL int ioctl(socket_type s, state_type& state, + int cmd, ioctl_arg_type* arg, asio::error_code& ec); -inline int setsockopt(socket_type s, int level, int optname, - const void* optval, std::size_t optlen, asio::error_code& ec) -{ - if (level == custom_socket_option_level && optname == always_fail_option) - { - ec = asio::error::invalid_argument; - return -1; - } +ASIO_DECL int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, timeval* timeout, asio::error_code& ec); -#if defined(__BORLANDC__) - // Mysteriously, using the getsockopt and setsockopt functions directly with - // Borland C++ results in incorrect values being set and read. The bug can be - // worked around by using function addresses resolved with GetProcAddress. - if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) - { - typedef int (WSAAPI *sso_t)(SOCKET, int, int, const char*, int); - if (sso_t sso = (sso_t)::GetProcAddress(winsock_module, "setsockopt")) - { - clear_error(ec); - return error_wrapper(sso(s, level, optname, - reinterpret_cast(optval), - static_cast(optlen)), ec); - } - } - ec = asio::error::fault; - return -1; -#else // defined(__BORLANDC__) - clear_error(ec); - int result = error_wrapper(call_setsockopt(&msghdr::msg_namelen, - s, level, optname, optval, optlen), ec); - if (result == 0) - clear_error(ec); - return result; -#endif // defined(__BORLANDC__) -} +ASIO_DECL int poll_read(socket_type s, asio::error_code& ec); -template -inline int call_getsockopt(SockLenType msghdr::*, - socket_type s, int level, int optname, - void* optval, std::size_t* optlen) -{ - SockLenType tmp_optlen = (SockLenType)*optlen; - int result = ::getsockopt(s, level, optname, (char*)optval, &tmp_optlen); - *optlen = (std::size_t)tmp_optlen; - return result; -} +ASIO_DECL int poll_write(socket_type s, asio::error_code& ec); -inline int getsockopt(socket_type s, int level, int optname, void* optval, - size_t* optlen, asio::error_code& ec) -{ - if (level == custom_socket_option_level && optname == always_fail_option) - { - ec = asio::error::invalid_argument; - return -1; - } +ASIO_DECL int poll_connect(socket_type s, asio::error_code& ec); -#if defined(__BORLANDC__) - // Mysteriously, using the getsockopt and setsockopt functions directly with - // Borland C++ results in incorrect values being set and read. The bug can be - // worked around by using function addresses resolved with GetProcAddress. - if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) - { - typedef int (WSAAPI *gso_t)(SOCKET, int, int, char*, int*); - if (gso_t gso = (gso_t)::GetProcAddress(winsock_module, "getsockopt")) - { - clear_error(ec); - int tmp_optlen = static_cast(*optlen); - int result = error_wrapper(gso(s, level, optname, - reinterpret_cast(optval), &tmp_optlen), ec); - *optlen = static_cast(tmp_optlen); - if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY - && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) - { - // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are - // only supported on Windows Vista and later. To simplify program logic - // we will fake success of getting this option and specify that the - // value is non-zero (i.e. true). This corresponds to the behavior of - // IPv6 sockets on Windows platforms pre-Vista. - *static_cast(optval) = 1; - clear_error(ec); - } - return result; - } - } - ec = asio::error::fault; - return -1; -#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) - clear_error(ec); - int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, - s, level, optname, optval, optlen), ec); - if (result != 0 && level == IPPROTO_IPV6 && optname == IPV6_V6ONLY - && ec.value() == WSAENOPROTOOPT && *optlen == sizeof(DWORD)) - { - // Dual-stack IPv4/v6 sockets, and the IPV6_V6ONLY socket option, are only - // supported on Windows Vista and later. To simplify program logic we will - // fake success of getting this option and specify that the value is - // non-zero (i.e. true). This corresponds to the behavior of IPv6 sockets - // on Windows platforms pre-Vista. - *static_cast(optval) = 1; - clear_error(ec); - } - if (result == 0) - clear_error(ec); - return result; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - clear_error(ec); - int result = error_wrapper(call_getsockopt(&msghdr::msg_namelen, - s, level, optname, optval, optlen), ec); -#if defined(__linux__) - if (result == 0 && level == SOL_SOCKET && *optlen == sizeof(int) - && (optname == SO_SNDBUF || optname == SO_RCVBUF)) - { - // On Linux, setting SO_SNDBUF or SO_RCVBUF to N actually causes the kernel - // to set the buffer size to N*2. Linux puts additional stuff into the - // buffers so that only about half is actually available to the application. - // The retrieved value is divided by 2 here to make it appear as though the - // correct value has been set. - *static_cast(optval) /= 2; - } -#endif // defined(__linux__) - if (result == 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +ASIO_DECL const char* inet_ntop(int af, const void* src, char* dest, + size_t length, unsigned long scope_id, asio::error_code& ec); -template -inline int call_getpeername(SockLenType msghdr::*, - socket_type s, socket_addr_type* addr, std::size_t* addrlen) -{ - SockLenType tmp_addrlen = (SockLenType)*addrlen; - int result = ::getpeername(s, addr, &tmp_addrlen); - *addrlen = (std::size_t)tmp_addrlen; - return result; -} +ASIO_DECL int inet_pton(int af, const char* src, void* dest, + unsigned long* scope_id, asio::error_code& ec); -inline int getpeername(socket_type s, socket_addr_type* addr, - std::size_t* addrlen, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(call_getpeername( - &msghdr::msg_namelen, s, addr, addrlen), ec); - if (result == 0) - clear_error(ec); - return result; -} +ASIO_DECL int gethostname(char* name, + int namelen, asio::error_code& ec); -template -inline int call_getsockname(SockLenType msghdr::*, - socket_type s, socket_addr_type* addr, std::size_t* addrlen) -{ - SockLenType tmp_addrlen = (SockLenType)*addrlen; - int result = ::getsockname(s, addr, &tmp_addrlen); - *addrlen = (std::size_t)tmp_addrlen; - return result; -} +ASIO_DECL asio::error_code getaddrinfo(const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec); -inline int getsockname(socket_type s, socket_addr_type* addr, - std::size_t* addrlen, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(call_getsockname( - &msghdr::msg_namelen, s, addr, addrlen), ec); - if (result == 0) - clear_error(ec); - return result; -} +ASIO_DECL asio::error_code background_getaddrinfo( + const weak_cancel_token_type& cancel_token, const char* host, + const char* service, const addrinfo_type& hints, + addrinfo_type** result, asio::error_code& ec); -inline int ioctl(socket_type s, long cmd, ioctl_arg_type* arg, - asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - int result = error_wrapper(::ioctlsocket(s, cmd, arg), ec); -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - int result = error_wrapper(::ioctl(s, cmd, arg), ec); -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - if (result >= 0) - clear_error(ec); - return result; -} +ASIO_DECL void freeaddrinfo(addrinfo_type* ai); -inline int select(int nfds, fd_set* readfds, fd_set* writefds, - fd_set* exceptfds, timeval* timeout, asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - if (!readfds && !writefds && !exceptfds && timeout) - { - DWORD milliseconds = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; - if (milliseconds == 0) - milliseconds = 1; // Force context switch. - ::Sleep(milliseconds); - ec = asio::error_code(); - return 0; - } +ASIO_DECL asio::error_code getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int flags, asio::error_code& ec); - // The select() call allows timeout values measured in microseconds, but the - // system clock (as wrapped by boost::posix_time::microsec_clock) typically - // has a resolution of 10 milliseconds. This can lead to a spinning select - // reactor, meaning increased CPU usage, when waiting for the earliest - // scheduled timeout if it's less than 10 milliseconds away. To avoid a tight - // spin we'll use a minimum timeout of 1 millisecond. - if (timeout && timeout->tv_sec == 0 - && timeout->tv_usec > 0 && timeout->tv_usec < 1000) - timeout->tv_usec = 1000; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +ASIO_DECL asio::error_code sync_getnameinfo( + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec); -#if defined(__hpux) && defined(__HP_aCC) - timespec ts; - ts.tv_sec = timeout ? timeout->tv_sec : 0; - ts.tv_nsec = timeout ? timeout->tv_usec * 1000 : 0; - return error_wrapper(::pselect(nfds, readfds, - writefds, exceptfds, timeout ? &ts : 0, 0), ec); -#else - int result = error_wrapper(::select(nfds, readfds, - writefds, exceptfds, timeout), ec); - if (result >= 0) - clear_error(ec); - return result; -#endif -} +ASIO_DECL asio::error_code background_getnameinfo( + const weak_cancel_token_type& cancel_token, + const socket_addr_type* addr, std::size_t addrlen, + char* host, std::size_t hostlen, char* serv, + std::size_t servlen, int sock_type, asio::error_code& ec); -inline int poll_read(socket_type s, asio::error_code& ec) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - FD_SET fds; - FD_ZERO(&fds); - FD_SET(s, &fds); - clear_error(ec); - int result = error_wrapper(::select(s, &fds, 0, 0, 0), ec); - if (result >= 0) - clear_error(ec); - return result; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - pollfd fds; - fds.fd = s; - fds.events = POLLIN; - fds.revents = 0; - clear_error(ec); - int result = error_wrapper(::poll(&fds, 1, -1), ec); - if (result >= 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +ASIO_DECL u_long_type network_to_host_long(u_long_type value); -inline int poll_write(socket_type s, asio::error_code& ec) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - FD_SET fds; - FD_ZERO(&fds); - FD_SET(s, &fds); - clear_error(ec); - int result = error_wrapper(::select(s, 0, &fds, 0, 0), ec); - if (result >= 0) - clear_error(ec); - return result; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - pollfd fds; - fds.fd = s; - fds.events = POLLOUT; - fds.revents = 0; - clear_error(ec); - int result = error_wrapper(::poll(&fds, 1, -1), ec); - if (result >= 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +ASIO_DECL u_long_type host_to_network_long(u_long_type value); -inline int poll_connect(socket_type s, asio::error_code& ec) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - FD_SET write_fds; - FD_ZERO(&write_fds); - FD_SET(s, &write_fds); - FD_SET except_fds; - FD_ZERO(&except_fds); - FD_SET(s, &except_fds); - clear_error(ec); - int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec); - if (result >= 0) - clear_error(ec); - return result; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - pollfd fds; - fds.fd = s; - fds.events = POLLOUT; - fds.revents = 0; - clear_error(ec); - int result = error_wrapper(::poll(&fds, 1, -1), ec); - if (result >= 0) - clear_error(ec); - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} +ASIO_DECL u_short_type network_to_host_short(u_short_type value); -inline const char* inet_ntop(int af, const void* src, char* dest, size_t length, - unsigned long scope_id, asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - using namespace std; // For memcpy. - - if (af != AF_INET && af != AF_INET6) - { - ec = asio::error::address_family_not_supported; - return 0; - } - - union - { - socket_addr_type base; - sockaddr_storage_type storage; - sockaddr_in4_type v4; - sockaddr_in6_type v6; - } address; - DWORD address_length; - if (af == AF_INET) - { - address_length = sizeof(sockaddr_in4_type); - address.v4.sin_family = AF_INET; - address.v4.sin_port = 0; - memcpy(&address.v4.sin_addr, src, sizeof(in4_addr_type)); - } - else // AF_INET6 - { - address_length = sizeof(sockaddr_in6_type); - address.v6.sin6_family = AF_INET6; - address.v6.sin6_port = 0; - address.v6.sin6_flowinfo = 0; - address.v6.sin6_scope_id = scope_id; - memcpy(&address.v6.sin6_addr, src, sizeof(in6_addr_type)); - } - - DWORD string_length = static_cast(length); -#if defined(BOOST_NO_ANSI_APIS) - LPWSTR string_buffer = (LPWSTR)_alloca(length * sizeof(WCHAR)); - int result = error_wrapper(::WSAAddressToStringW(&address.base, - address_length, 0, string_buffer, &string_length), ec); - ::WideCharToMultiByte(CP_ACP, 0, string_buffer, -1, dest, length, 0, 0); -#else - int result = error_wrapper(::WSAAddressToStringA( - &address.base, address_length, 0, dest, &string_length), ec); -#endif - - // Windows may set error code on success. - if (result != socket_error_retval) - clear_error(ec); - - // Windows may not set an error code on failure. - else if (result == socket_error_retval && !ec) - ec = asio::error::invalid_argument; - - return result == socket_error_retval ? 0 : dest; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - const char* result = error_wrapper(::inet_ntop(af, src, dest, length), ec); - if (result == 0 && !ec) - ec = asio::error::invalid_argument; - if (result != 0 && af == AF_INET6 && scope_id != 0) - { - using namespace std; // For strcat and sprintf. - char if_name[IF_NAMESIZE + 1] = "%"; - const in6_addr_type* ipv6_address = static_cast(src); - bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); - if (!is_link_local || if_indextoname(scope_id, if_name + 1) == 0) - sprintf(if_name + 1, "%lu", scope_id); - strcat(dest, if_name); - } - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} - -inline int inet_pton(int af, const char* src, void* dest, - unsigned long* scope_id, asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - using namespace std; // For memcpy and strcmp. - - if (af != AF_INET && af != AF_INET6) - { - ec = asio::error::address_family_not_supported; - return -1; - } - - union - { - socket_addr_type base; - sockaddr_storage_type storage; - sockaddr_in4_type v4; - sockaddr_in6_type v6; - } address; - int address_length = sizeof(sockaddr_storage_type); -#if defined(BOOST_NO_ANSI_APIS) - int num_wide_chars = strlen(src) + 1; - LPWSTR wide_buffer = (LPWSTR)_alloca(num_wide_chars * sizeof(WCHAR)); - ::MultiByteToWideChar(CP_ACP, 0, src, -1, wide_buffer, num_wide_chars); - int result = error_wrapper(::WSAStringToAddressW( - wide_buffer, af, 0, &address.base, &address_length), ec); -#else - int result = error_wrapper(::WSAStringToAddressA( - const_cast(src), af, 0, &address.base, &address_length), ec); -#endif - - if (af == AF_INET) - { - if (result != socket_error_retval) - { - memcpy(dest, &address.v4.sin_addr, sizeof(in4_addr_type)); - clear_error(ec); - } - else if (strcmp(src, "255.255.255.255") == 0) - { - static_cast(dest)->s_addr = INADDR_NONE; - clear_error(ec); - } - } - else // AF_INET6 - { - if (result != socket_error_retval) - { - memcpy(dest, &address.v6.sin6_addr, sizeof(in6_addr_type)); - if (scope_id) - *scope_id = address.v6.sin6_scope_id; - clear_error(ec); - } - } - - // Windows may not set an error code on failure. - if (result == socket_error_retval && !ec) - ec = asio::error::invalid_argument; - - if (result != socket_error_retval) - clear_error(ec); - - return result == socket_error_retval ? -1 : 1; -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - int result = error_wrapper(::inet_pton(af, src, dest), ec); - if (result <= 0 && !ec) - ec = asio::error::invalid_argument; - if (result > 0 && af == AF_INET6 && scope_id) - { - using namespace std; // For strchr and atoi. - *scope_id = 0; - if (const char* if_name = strchr(src, '%')) - { - in6_addr_type* ipv6_address = static_cast(dest); - bool is_link_local = IN6_IS_ADDR_LINKLOCAL(ipv6_address); - if (is_link_local) - *scope_id = if_nametoindex(if_name + 1); - if (*scope_id == 0) - *scope_id = atoi(if_name + 1); - } - } - return result; -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -} - -inline int gethostname(char* name, int namelen, asio::error_code& ec) -{ - clear_error(ec); - int result = error_wrapper(::gethostname(name, namelen), ec); -#if defined(BOOST_WINDOWS) - if (result == 0) - clear_error(ec); -#endif - return result; -} - -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) \ - || defined(__MACH__) && defined(__APPLE__) - -// The following functions are only needed for emulation of getaddrinfo and -// getnameinfo. - -inline asio::error_code translate_netdb_error(int error) -{ - switch (error) - { - case 0: - return asio::error_code(); - case HOST_NOT_FOUND: - return asio::error::host_not_found; - case TRY_AGAIN: - return asio::error::host_not_found_try_again; - case NO_RECOVERY: - return asio::error::no_recovery; - case NO_DATA: - return asio::error::no_data; - default: - BOOST_ASSERT(false); - return asio::error::invalid_argument; - } -} - -inline hostent* gethostbyaddr(const char* addr, int length, int af, - hostent* result, char* buffer, int buflength, asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - (void)(buffer); - (void)(buflength); - hostent* retval = error_wrapper(::gethostbyaddr(addr, length, af), ec); - if (!retval) - return 0; - clear_error(ec); - *result = *retval; - return retval; -#elif defined(__sun) || defined(__QNX__) - int error = 0; - hostent* retval = error_wrapper(::gethostbyaddr_r(addr, length, af, result, - buffer, buflength, &error), ec); - if (error) - ec = translate_netdb_error(error); - return retval; -#elif defined(__MACH__) && defined(__APPLE__) - (void)(buffer); - (void)(buflength); - int error = 0; - hostent* retval = error_wrapper(::getipnodebyaddr( - addr, length, af, &error), ec); - if (error) - ec = translate_netdb_error(error); - if (!retval) - return 0; - *result = *retval; - return retval; -#else - hostent* retval = 0; - int error = 0; - error_wrapper(::gethostbyaddr_r(addr, length, af, result, buffer, - buflength, &retval, &error), ec); - if (error) - ec = translate_netdb_error(error); - return retval; -#endif -} - -inline hostent* gethostbyname(const char* name, int af, struct hostent* result, - char* buffer, int buflength, int ai_flags, asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - (void)(buffer); - (void)(buflength); - (void)(ai_flags); - if (af != AF_INET) - { - ec = asio::error::address_family_not_supported; - return 0; - } - hostent* retval = error_wrapper(::gethostbyname(name), ec); - if (!retval) - return 0; - clear_error(ec); - *result = *retval; - return result; -#elif defined(__sun) || defined(__QNX__) - (void)(ai_flags); - if (af != AF_INET) - { - ec = asio::error::address_family_not_supported; - return 0; - } - int error = 0; - hostent* retval = error_wrapper(::gethostbyname_r(name, result, buffer, - buflength, &error), ec); - if (error) - ec = translate_netdb_error(error); - return retval; -#elif defined(__MACH__) && defined(__APPLE__) - (void)(buffer); - (void)(buflength); - int error = 0; - hostent* retval = error_wrapper(::getipnodebyname( - name, af, ai_flags, &error), ec); - if (error) - ec = translate_netdb_error(error); - if (!retval) - return 0; - *result = *retval; - return retval; -#else - (void)(ai_flags); - if (af != AF_INET) - { - ec = asio::error::address_family_not_supported; - return 0; - } - hostent* retval = 0; - int error = 0; - error_wrapper(::gethostbyname_r(name, result, - buffer, buflength, &retval, &error), ec); - if (error) - ec = translate_netdb_error(error); - return retval; -#endif -} - -inline void freehostent(hostent* h) -{ -#if defined(__MACH__) && defined(__APPLE__) - if (h) - ::freehostent(h); -#else - (void)(h); -#endif -} - -// Emulation of getaddrinfo based on implementation in: -// Stevens, W. R., UNIX Network Programming Vol. 1, 2nd Ed., Prentice-Hall 1998. - -struct gai_search -{ - const char* host; - int family; -}; - -inline int gai_nsearch(const char* host, - const addrinfo_type* hints, gai_search (&search)[2]) -{ - int search_count = 0; - if (host == 0 || host[0] == '\0') - { - if (hints->ai_flags & AI_PASSIVE) - { - // No host and AI_PASSIVE implies wildcard bind. - switch (hints->ai_family) - { - case AF_INET: - search[search_count].host = "0.0.0.0"; - search[search_count].family = AF_INET; - ++search_count; - break; - case AF_INET6: - search[search_count].host = "0::0"; - search[search_count].family = AF_INET6; - ++search_count; - break; - case AF_UNSPEC: - search[search_count].host = "0::0"; - search[search_count].family = AF_INET6; - ++search_count; - search[search_count].host = "0.0.0.0"; - search[search_count].family = AF_INET; - ++search_count; - break; - default: - break; - } - } - else - { - // No host and not AI_PASSIVE means connect to local host. - switch (hints->ai_family) - { - case AF_INET: - search[search_count].host = "localhost"; - search[search_count].family = AF_INET; - ++search_count; - break; - case AF_INET6: - search[search_count].host = "localhost"; - search[search_count].family = AF_INET6; - ++search_count; - break; - case AF_UNSPEC: - search[search_count].host = "localhost"; - search[search_count].family = AF_INET6; - ++search_count; - search[search_count].host = "localhost"; - search[search_count].family = AF_INET; - ++search_count; - break; - default: - break; - } - } - } - else - { - // Host is specified. - switch (hints->ai_family) - { - case AF_INET: - search[search_count].host = host; - search[search_count].family = AF_INET; - ++search_count; - break; - case AF_INET6: - search[search_count].host = host; - search[search_count].family = AF_INET6; - ++search_count; - break; - case AF_UNSPEC: - search[search_count].host = host; - search[search_count].family = AF_INET6; - ++search_count; - search[search_count].host = host; - search[search_count].family = AF_INET; - ++search_count; - break; - default: - break; - } - } - return search_count; -} - -template -inline T* gai_alloc(std::size_t size = sizeof(T)) -{ - using namespace std; - T* p = static_cast(::operator new(size, std::nothrow)); - if (p) - memset(p, 0, size); - return p; -} - -inline void gai_free(void* p) -{ - ::operator delete(p); -} - -inline void gai_strcpy(char* target, const char* source, std::size_t max_size) -{ - using namespace std; -#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) - strcpy_s(target, max_size, source); -#else - *target = 0; - strncat(target, source, max_size); -#endif -} - -enum { gai_clone_flag = 1 << 30 }; - -inline int gai_aistruct(addrinfo_type*** next, const addrinfo_type* hints, - const void* addr, int family) -{ - using namespace std; - - addrinfo_type* ai = gai_alloc(); - if (ai == 0) - return EAI_MEMORY; - - ai->ai_next = 0; - **next = ai; - *next = &ai->ai_next; - - ai->ai_canonname = 0; - ai->ai_socktype = hints->ai_socktype; - if (ai->ai_socktype == 0) - ai->ai_flags |= gai_clone_flag; - ai->ai_protocol = hints->ai_protocol; - ai->ai_family = family; - - switch (ai->ai_family) - { - case AF_INET: - { - sockaddr_in4_type* sinptr = gai_alloc(); - if (sinptr == 0) - return EAI_MEMORY; - sinptr->sin_family = AF_INET; - memcpy(&sinptr->sin_addr, addr, sizeof(in4_addr_type)); - ai->ai_addr = reinterpret_cast(sinptr); - ai->ai_addrlen = sizeof(sockaddr_in4_type); - break; - } - case AF_INET6: - { - sockaddr_in6_type* sin6ptr = gai_alloc(); - if (sin6ptr == 0) - return EAI_MEMORY; - sin6ptr->sin6_family = AF_INET6; - memcpy(&sin6ptr->sin6_addr, addr, sizeof(in6_addr_type)); - ai->ai_addr = reinterpret_cast(sin6ptr); - ai->ai_addrlen = sizeof(sockaddr_in6_type); - break; - } - default: - break; - } - - return 0; -} - -inline addrinfo_type* gai_clone(addrinfo_type* ai) -{ - using namespace std; - - addrinfo_type* new_ai = gai_alloc(); - if (new_ai == 0) - return new_ai; - - new_ai->ai_next = ai->ai_next; - ai->ai_next = new_ai; - - new_ai->ai_flags = 0; - new_ai->ai_family = ai->ai_family; - new_ai->ai_socktype = ai->ai_socktype; - new_ai->ai_protocol = ai->ai_protocol; - new_ai->ai_canonname = 0; - new_ai->ai_addrlen = ai->ai_addrlen; - new_ai->ai_addr = gai_alloc(ai->ai_addrlen); - memcpy(new_ai->ai_addr, ai->ai_addr, ai->ai_addrlen); - - return new_ai; -} - -inline int gai_port(addrinfo_type* aihead, int port, int socktype) -{ - int num_found = 0; - - for (addrinfo_type* ai = aihead; ai; ai = ai->ai_next) - { - if (ai->ai_flags & gai_clone_flag) - { - if (ai->ai_socktype != 0) - { - ai = gai_clone(ai); - if (ai == 0) - return -1; - // ai now points to newly cloned entry. - } - } - else if (ai->ai_socktype != socktype) - { - // Ignore if mismatch on socket type. - continue; - } - - ai->ai_socktype = socktype; - - switch (ai->ai_family) - { - case AF_INET: - { - sockaddr_in4_type* sinptr = - reinterpret_cast(ai->ai_addr); - sinptr->sin_port = port; - ++num_found; - break; - } - case AF_INET6: - { - sockaddr_in6_type* sin6ptr = - reinterpret_cast(ai->ai_addr); - sin6ptr->sin6_port = port; - ++num_found; - break; - } - default: - break; - } - } - - return num_found; -} - -inline int gai_serv(addrinfo_type* aihead, - const addrinfo_type* hints, const char* serv) -{ - using namespace std; - - int num_found = 0; - - if ( -#if defined(AI_NUMERICSERV) - (hints->ai_flags & AI_NUMERICSERV) || -#endif - isdigit(serv[0])) - { - int port = htons(atoi(serv)); - if (hints->ai_socktype) - { - // Caller specifies socket type. - int rc = gai_port(aihead, port, hints->ai_socktype); - if (rc < 0) - return EAI_MEMORY; - num_found += rc; - } - else - { - // Caller does not specify socket type. - int rc = gai_port(aihead, port, SOCK_STREAM); - if (rc < 0) - return EAI_MEMORY; - num_found += rc; - rc = gai_port(aihead, port, SOCK_DGRAM); - if (rc < 0) - return EAI_MEMORY; - num_found += rc; - } - } - else - { - // Try service name with TCP first, then UDP. - if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_STREAM) - { - servent* sptr = getservbyname(serv, "tcp"); - if (sptr != 0) - { - int rc = gai_port(aihead, sptr->s_port, SOCK_STREAM); - if (rc < 0) - return EAI_MEMORY; - num_found += rc; - } - } - if (hints->ai_socktype == 0 || hints->ai_socktype == SOCK_DGRAM) - { - servent* sptr = getservbyname(serv, "udp"); - if (sptr != 0) - { - int rc = gai_port(aihead, sptr->s_port, SOCK_DGRAM); - if (rc < 0) - return EAI_MEMORY; - num_found += rc; - } - } - } - - if (num_found == 0) - { - if (hints->ai_socktype == 0) - { - // All calls to getservbyname() failed. - return EAI_NONAME; - } - else - { - // Service not supported for socket type. - return EAI_SERVICE; - } - } - - return 0; -} - -inline int gai_echeck(const char* host, const char* service, - int flags, int family, int socktype, int protocol) -{ - (void)(flags); - (void)(protocol); - - // Host or service must be specified. - if (host == 0 || host[0] == '\0') - if (service == 0 || service[0] == '\0') - return EAI_NONAME; - - // Check combination of family and socket type. - switch (family) - { - case AF_UNSPEC: - break; - case AF_INET: - case AF_INET6: - if (service != 0 && service[0] != '\0') - if (socktype != 0 && socktype != SOCK_STREAM && socktype != SOCK_DGRAM) - return EAI_SOCKTYPE; - break; - default: - return EAI_FAMILY; - } - - return 0; -} - -inline void freeaddrinfo_emulation(addrinfo_type* aihead) -{ - addrinfo_type* ai = aihead; - while (ai) - { - gai_free(ai->ai_addr); - gai_free(ai->ai_canonname); - addrinfo_type* ainext = ai->ai_next; - gai_free(ai); - ai = ainext; - } -} - -inline int getaddrinfo_emulation(const char* host, const char* service, - const addrinfo_type* hintsp, addrinfo_type** result) -{ - // Set up linked list of addrinfo structures. - addrinfo_type* aihead = 0; - addrinfo_type** ainext = &aihead; - char* canon = 0; - - // Supply default hints if not specified by caller. - addrinfo_type hints = addrinfo_type(); - hints.ai_family = AF_UNSPEC; - if (hintsp) - hints = *hintsp; - - // If the resolution is not specifically for AF_INET6, remove the AI_V4MAPPED - // and AI_ALL flags. -#if defined(AI_V4MAPPED) - if (hints.ai_family != AF_INET6) - hints.ai_flags &= ~AI_V4MAPPED; -#endif -#if defined(AI_ALL) - if (hints.ai_family != AF_INET6) - hints.ai_flags &= ~AI_ALL; -#endif - - // Basic error checking. - int rc = gai_echeck(host, service, hints.ai_flags, hints.ai_family, - hints.ai_socktype, hints.ai_protocol); - if (rc != 0) - { - freeaddrinfo_emulation(aihead); - return rc; - } - - gai_search search[2]; - int search_count = gai_nsearch(host, &hints, search); - for (gai_search* sptr = search; sptr < search + search_count; ++sptr) - { - // Check for IPv4 dotted decimal string. - in4_addr_type inaddr; - asio::error_code ec; - if (socket_ops::inet_pton(AF_INET, sptr->host, &inaddr, 0, ec) == 1) - { - if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) - { - freeaddrinfo_emulation(aihead); - gai_free(canon); - return EAI_FAMILY; - } - if (sptr->family == AF_INET) - { - rc = gai_aistruct(&ainext, &hints, &inaddr, AF_INET); - if (rc != 0) - { - freeaddrinfo_emulation(aihead); - gai_free(canon); - return rc; - } - } - continue; - } - - // Check for IPv6 hex string. - in6_addr_type in6addr; - if (socket_ops::inet_pton(AF_INET6, sptr->host, &in6addr, 0, ec) == 1) - { - if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6) - { - freeaddrinfo_emulation(aihead); - gai_free(canon); - return EAI_FAMILY; - } - if (sptr->family == AF_INET6) - { - rc = gai_aistruct(&ainext, &hints, &in6addr, AF_INET6); - if (rc != 0) - { - freeaddrinfo_emulation(aihead); - gai_free(canon); - return rc; - } - } - continue; - } - - // Look up hostname. - hostent hent; - char hbuf[8192] = ""; - hostent* hptr = socket_ops::gethostbyname(sptr->host, - sptr->family, &hent, hbuf, sizeof(hbuf), hints.ai_flags, ec); - if (hptr == 0) - { - if (search_count == 2) - { - // Failure is OK if there are multiple searches. - continue; - } - freeaddrinfo_emulation(aihead); - gai_free(canon); - if (ec == asio::error::host_not_found) - return EAI_NONAME; - if (ec == asio::error::host_not_found_try_again) - return EAI_AGAIN; - if (ec == asio::error::no_recovery) - return EAI_FAIL; - if (ec == asio::error::no_data) - return EAI_NONAME; - return EAI_NONAME; - } - - // Check for address family mismatch if one was specified. - if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) - { - freeaddrinfo_emulation(aihead); - gai_free(canon); - socket_ops::freehostent(hptr); - return EAI_FAMILY; - } - - // Save canonical name first time. - if (host != 0 && host[0] != '\0' && hptr->h_name && hptr->h_name[0] - && (hints.ai_flags & AI_CANONNAME) && canon == 0) - { - std::size_t canon_len = strlen(hptr->h_name) + 1; - canon = gai_alloc(canon_len); - if (canon == 0) - { - freeaddrinfo_emulation(aihead); - socket_ops::freehostent(hptr); - return EAI_MEMORY; - } - gai_strcpy(canon, hptr->h_name, canon_len); - } - - // Create an addrinfo structure for each returned address. - for (char** ap = hptr->h_addr_list; *ap; ++ap) - { - rc = gai_aistruct(&ainext, &hints, *ap, hptr->h_addrtype); - if (rc != 0) - { - freeaddrinfo_emulation(aihead); - gai_free(canon); - socket_ops::freehostent(hptr); - return EAI_FAMILY; - } - } - - socket_ops::freehostent(hptr); - } - - // Check if we found anything. - if (aihead == 0) - { - gai_free(canon); - return EAI_NONAME; - } - - // Return canonical name in first entry. - if (host != 0 && host[0] != '\0' && (hints.ai_flags & AI_CANONNAME)) - { - if (canon) - { - aihead->ai_canonname = canon; - canon = 0; - } - else - { - std::size_t canonname_len = strlen(search[0].host) + 1; - aihead->ai_canonname = gai_alloc(canonname_len); - if (aihead->ai_canonname == 0) - { - freeaddrinfo_emulation(aihead); - return EAI_MEMORY; - } - gai_strcpy(aihead->ai_canonname, search[0].host, canonname_len); - } - } - gai_free(canon); - - // Process the service name. - if (service != 0 && service[0] != '\0') - { - rc = gai_serv(aihead, &hints, service); - if (rc != 0) - { - freeaddrinfo_emulation(aihead); - return rc; - } - } - - // Return result to caller. - *result = aihead; - return 0; -} - -inline asio::error_code getnameinfo_emulation( - const socket_addr_type* sa, std::size_t salen, char* host, - std::size_t hostlen, char* serv, std::size_t servlen, int flags, - asio::error_code& ec) -{ - using namespace std; - - const char* addr; - size_t addr_len; - unsigned short port; - switch (sa->sa_family) - { - case AF_INET: - if (salen != sizeof(sockaddr_in4_type)) - { - return ec = asio::error::invalid_argument; - } - addr = reinterpret_cast( - &reinterpret_cast(sa)->sin_addr); - addr_len = sizeof(in4_addr_type); - port = reinterpret_cast(sa)->sin_port; - break; - case AF_INET6: - if (salen != sizeof(sockaddr_in6_type)) - { - return ec = asio::error::invalid_argument; - } - addr = reinterpret_cast( - &reinterpret_cast(sa)->sin6_addr); - addr_len = sizeof(in6_addr_type); - port = reinterpret_cast(sa)->sin6_port; - break; - default: - return ec = asio::error::address_family_not_supported; - } - - if (host && hostlen > 0) - { - if (flags & NI_NUMERICHOST) - { - if (socket_ops::inet_ntop(sa->sa_family, addr, host, hostlen, 0, ec) == 0) - { - return ec; - } - } - else - { - hostent hent; - char hbuf[8192] = ""; - hostent* hptr = socket_ops::gethostbyaddr(addr, - static_cast(addr_len), sa->sa_family, - &hent, hbuf, sizeof(hbuf), ec); - if (hptr && hptr->h_name && hptr->h_name[0] != '\0') - { - if (flags & NI_NOFQDN) - { - char* dot = strchr(hptr->h_name, '.'); - if (dot) - { - *dot = 0; - } - } - gai_strcpy(host, hptr->h_name, hostlen); - socket_ops::freehostent(hptr); - } - else - { - socket_ops::freehostent(hptr); - if (flags & NI_NAMEREQD) - { - return ec = asio::error::host_not_found; - } - if (socket_ops::inet_ntop(sa->sa_family, - addr, host, hostlen, 0, ec) == 0) - { - return ec; - } - } - } - } - - if (serv && servlen > 0) - { - if (flags & NI_NUMERICSERV) - { - if (servlen < 6) - { - return ec = asio::error::no_buffer_space; - } -#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) - sprintf_s(serv, servlen, "%u", ntohs(port)); -#else - sprintf(serv, "%u", ntohs(port)); -#endif - } - else - { -#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) - static ::pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - ::pthread_mutex_lock(&mutex); -#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) - servent* sptr = ::getservbyport(port, (flags & NI_DGRAM) ? "udp" : 0); - if (sptr && sptr->s_name && sptr->s_name[0] != '\0') - { - gai_strcpy(serv, sptr->s_name, servlen); - } - else - { - if (servlen < 6) - { - return ec = asio::error::no_buffer_space; - } -#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) && !defined(UNDER_CE) - sprintf_s(serv, servlen, "%u", ntohs(port)); -#else - sprintf(serv, "%u", ntohs(port)); -#endif - } -#if defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) - ::pthread_mutex_unlock(&mutex); -#endif // defined(BOOST_HAS_THREADS) && defined(BOOST_HAS_PTHREADS) - } - } - - clear_error(ec); - return ec; -} - -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - // || defined(__MACH__) && defined(__APPLE__) - -inline asio::error_code translate_addrinfo_error(int error) -{ - switch (error) - { - case 0: - return asio::error_code(); - case EAI_AGAIN: - return asio::error::host_not_found_try_again; - case EAI_BADFLAGS: - return asio::error::invalid_argument; - case EAI_FAIL: - return asio::error::no_recovery; - case EAI_FAMILY: - return asio::error::address_family_not_supported; - case EAI_MEMORY: - return asio::error::no_memory; - case EAI_NONAME: -#if defined(EAI_ADDRFAMILY) - case EAI_ADDRFAMILY: -#endif -#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) - case EAI_NODATA: -#endif - return asio::error::host_not_found; - case EAI_SERVICE: - return asio::error::service_not_found; - case EAI_SOCKTYPE: - return asio::error::socket_type_not_supported; - default: // Possibly the non-portable EAI_SYSTEM. -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - return asio::error_code( - WSAGetLastError(), asio::error::get_system_category()); -#else - return asio::error_code( - errno, asio::error::get_system_category()); -#endif - } -} - -inline asio::error_code getaddrinfo(const char* host, - const char* service, const addrinfo_type* hints, addrinfo_type** result, - asio::error_code& ec) -{ - clear_error(ec); -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) -# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) - // Building for Windows XP, Windows Server 2003, or later. - int error = ::getaddrinfo(host, service, hints, result); - return ec = translate_addrinfo_error(error); -# else - // Building for Windows 2000 or earlier. - typedef int (WSAAPI *gai_t)(const char*, - const char*, const addrinfo_type*, addrinfo_type**); - if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) - { - if (gai_t gai = (gai_t)::GetProcAddress(winsock_module, "getaddrinfo")) - { - int error = gai(host, service, hints, result); - return ec = translate_addrinfo_error(error); - } - } - int error = getaddrinfo_emulation(host, service, hints, result); - return ec = translate_addrinfo_error(error); -# endif -#elif defined(__MACH__) && defined(__APPLE__) - int error = getaddrinfo_emulation(host, service, hints, result); - return ec = translate_addrinfo_error(error); -#else - int error = ::getaddrinfo(host, service, hints, result); - return ec = translate_addrinfo_error(error); -#endif -} - -inline void freeaddrinfo(addrinfo_type* ai) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) -# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) - // Building for Windows XP, Windows Server 2003, or later. - ::freeaddrinfo(ai); -# else - // Building for Windows 2000 or earlier. - typedef int (WSAAPI *fai_t)(addrinfo_type*); - if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) - { - if (fai_t fai = (fai_t)::GetProcAddress(winsock_module, "freeaddrinfo")) - { - fai(ai); - return; - } - } - freeaddrinfo_emulation(ai); -# endif -#elif defined(__MACH__) && defined(__APPLE__) - freeaddrinfo_emulation(ai); -#else - ::freeaddrinfo(ai); -#endif -} - -inline asio::error_code getnameinfo(const socket_addr_type* addr, - std::size_t addrlen, char* host, std::size_t hostlen, - char* serv, std::size_t servlen, int flags, asio::error_code& ec) -{ -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) -# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501) || defined(UNDER_CE) - // Building for Windows XP, Windows Server 2003, or later. - clear_error(ec); - int error = ::getnameinfo(addr, static_cast(addrlen), - host, static_cast(hostlen), - serv, static_cast(servlen), flags); - return ec = translate_addrinfo_error(error); -# else - // Building for Windows 2000 or earlier. - typedef int (WSAAPI *gni_t)(const socket_addr_type*, - int, char*, DWORD, char*, DWORD, int); - if (HMODULE winsock_module = ::GetModuleHandleA("ws2_32")) - { - if (gni_t gni = (gni_t)::GetProcAddress(winsock_module, "getnameinfo")) - { - clear_error(ec); - int error = gni(addr, static_cast(addrlen), - host, static_cast(hostlen), - serv, static_cast(servlen), flags); - return ec = translate_addrinfo_error(error); - } - } - clear_error(ec); - return getnameinfo_emulation(addr, addrlen, - host, hostlen, serv, servlen, flags, ec); -# endif -#elif defined(__MACH__) && defined(__APPLE__) - using namespace std; // For memcpy. - sockaddr_storage_type tmp_addr; - memcpy(&tmp_addr, addr, addrlen); - tmp_addr.ss_len = addrlen; - addr = reinterpret_cast(&tmp_addr); - clear_error(ec); - return getnameinfo_emulation(addr, addrlen, - host, hostlen, serv, servlen, flags, ec); -#else - clear_error(ec); - int error = ::getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags); - return ec = translate_addrinfo_error(error); -#endif -} - -inline u_long_type network_to_host_long(u_long_type value) -{ - return ntohl(value); -} - -inline u_long_type host_to_network_long(u_long_type value) -{ - return htonl(value); -} - -inline u_short_type network_to_host_short(u_short_type value) -{ - return ntohs(value); -} - -inline u_short_type host_to_network_short(u_short_type value) -{ - return htons(value); -} +ASIO_DECL u_short_type host_to_network_short(u_short_type value); } // namespace socket_ops } // namespace detail @@ -1909,4 +282,8 @@ inline u_short_type host_to_network_short(u_short_type value) #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/socket_ops.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_DETAIL_SOCKET_OPS_HPP diff --git a/ext/asio/asio/detail/socket_option.hpp b/ext/asio/asio/detail/socket_option.hpp index ac070b7018..c9d789698d 100644 --- a/ext/asio/asio/detail/socket_option.hpp +++ b/ext/asio/asio/detail/socket_option.hpp @@ -1,8 +1,8 @@ // -// socket_option.hpp -// ~~~~~~~~~~~~~~~~~ +// detail/socket_option.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { namespace socket_option { diff --git a/ext/asio/asio/detail/socket_select_interrupter.hpp b/ext/asio/asio/detail/socket_select_interrupter.hpp index 62e1063e30..b05a4ffa2d 100644 --- a/ext/asio/asio/detail/socket_select_interrupter.hpp +++ b/ext/asio/asio/detail/socket_select_interrupter.hpp @@ -1,8 +1,8 @@ // -// socket_select_interrupter.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/socket_select_interrupter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,19 +15,16 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" +#if defined(BOOST_WINDOWS) \ + || defined(__CYGWIN__) \ + || defined(__SYMBIAN32__) -#include "asio/error.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/socket_holder.hpp" -#include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -35,135 +32,16 @@ class socket_select_interrupter { public: // Constructor. - socket_select_interrupter() - { - asio::error_code ec; - socket_holder acceptor(socket_ops::socket( - AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); - if (acceptor.get() == invalid_socket) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - int opt = 1; - socket_ops::setsockopt(acceptor.get(), - SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); - - using namespace std; // For memset. - sockaddr_in4_type addr; - std::size_t addr_len = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - addr.sin_port = 0; - if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, - addr_len, ec) == socket_error_retval) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, - &addr_len, ec) == socket_error_retval) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - // Some broken firewalls on Windows will intermittently cause getsockname to - // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We - // explicitly specify the target address here to work around this problem. - addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - - if (socket_ops::listen(acceptor.get(), - SOMAXCONN, ec) == socket_error_retval) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - socket_holder client(socket_ops::socket( - AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); - if (client.get() == invalid_socket) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, - addr_len, ec) == socket_error_retval) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); - if (server.get() == invalid_socket) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - ioctl_arg_type non_blocking = 1; - if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking, ec)) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - opt = 1; - socket_ops::setsockopt(client.get(), - IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); - - non_blocking = 1; - if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking, ec)) - { - asio::system_error e(ec, "socket_select_interrupter"); - boost::throw_exception(e); - } - - opt = 1; - socket_ops::setsockopt(server.get(), - IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); - - read_descriptor_ = server.release(); - write_descriptor_ = client.release(); - } + ASIO_DECL socket_select_interrupter(); // Destructor. - ~socket_select_interrupter() - { - asio::error_code ec; - if (read_descriptor_ != invalid_socket) - socket_ops::close(read_descriptor_, ec); - if (write_descriptor_ != invalid_socket) - socket_ops::close(write_descriptor_, ec); - } + ASIO_DECL ~socket_select_interrupter(); // Interrupt the select call. - void interrupt() - { - char byte = 0; - socket_ops::buf b; - socket_ops::init_buf(b, &byte, 1); - asio::error_code ec; - socket_ops::send(write_descriptor_, &b, 1, 0, ec); - } + ASIO_DECL void interrupt(); // Reset the select interrupt. Returns true if the call was interrupted. - bool reset() - { - char data[1024]; - socket_ops::buf b; - socket_ops::init_buf(b, data, sizeof(data)); - asio::error_code ec; - int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); - bool was_interrupted = (bytes_read > 0); - while (bytes_read == sizeof(data)) - bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); - return was_interrupted; - } + ASIO_DECL bool reset(); // Get the read descriptor to be passed to select. socket_type read_descriptor() const @@ -189,4 +67,12 @@ private: #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/socket_select_interrupter.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_WINDOWS) + // || defined(__CYGWIN__) + // || defined(__SYMBIAN32__) + #endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP diff --git a/ext/asio/asio/detail/socket_types.hpp b/ext/asio/asio/detail/socket_types.hpp index 34b3d3e631..4e29e513ee 100644 --- a/ext/asio/asio/detail/socket_types.hpp +++ b/ext/asio/asio/detail/socket_types.hpp @@ -1,8 +1,8 @@ // -// socket_types.hpp -// ~~~~~~~~~~~~~~~~ +// detail/socket_types.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,69 +15,19 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/detail/push_options.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) # if defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) # error WinSock.h has already been included # endif // defined(_WINSOCKAPI_) && !defined(_WINSOCK2API_) -# if !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) -# if defined(_MSC_VER) || defined(__BORLANDC__) -# pragma message( \ - "Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. For example:\n"\ - "- add -D_WIN32_WINNT=0x0501 to the compiler command line; or\n"\ - "- add _WIN32_WINNT=0x0501 to your project's Preprocessor Definitions.\n"\ - "Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target).") -# else // defined(_MSC_VER) || defined(__BORLANDC__) -# warning Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately. -# warning For example, add -D_WIN32_WINNT=0x0501 to the compiler command line. -# warning Assuming _WIN32_WINNT=0x0501 (i.e. Windows XP target). -# endif // defined(_MSC_VER) || defined(__BORLANDC__) -# define _WIN32_WINNT 0x0501 -# endif // !defined(_WIN32_WINNT) && !defined(_WIN32_WINDOWS) -# if defined(_MSC_VER) -# if defined(_WIN32) && !defined(WIN32) -# if !defined(_WINSOCK2API_) -# define WIN32 // Needed for correct types in winsock2.h -# else // !defined(_WINSOCK2API_) -# error Please define the macro WIN32 in your compiler options -# endif // !defined(_WINSOCK2API_) -# endif // defined(_WIN32) && !defined(WIN32) -# endif // defined(_MSC_VER) # if defined(__BORLANDC__) # include // Needed for __errno -# if defined(__WIN32__) && !defined(WIN32) -# if !defined(_WINSOCK2API_) -# define WIN32 // Needed for correct types in winsock2.h -# else // !defined(_WINSOCK2API_) -# error Please define the macro WIN32 in your compiler options -# endif // !defined(_WINSOCK2API_) -# endif // defined(__WIN32__) && !defined(WIN32) # if !defined(_WSPIAPI_H_) # define _WSPIAPI_H_ # define ASIO_WSPIAPI_H_DEFINED # endif // !defined(_WSPIAPI_H_) # endif // defined(__BORLANDC__) -# if !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) -# if !defined(WIN32_LEAN_AND_MEAN) -# define WIN32_LEAN_AND_MEAN -# endif // !defined(WIN32_LEAN_AND_MEAN) -# endif // !defined(ASIO_NO_WIN32_LEAN_AND_MEAN) -# if !defined(ASIO_NO_NOMINMAX) -# if !defined(NOMINMAX) -# define NOMINMAX 1 -# endif // !defined(NOMINMAX) -# endif // !defined(ASIO_NO_NOMINMAX) -# if defined(__CYGWIN__) -# if !defined(__USE_W32_SOCKETS) -# error You must add -D__USE_W32_SOCKETS to your compiler options. -# endif // !defined(__USE_W32_SOCKETS) -# endif // defined(__CYGWIN__) # include # include # include @@ -96,18 +46,25 @@ # include "asio/detail/old_win_sdk_compat.hpp" #else # include -# include +# if !defined(__SYMBIAN32__) +# include +# endif # include -# if defined(__hpux) && !defined(__HP_aCC) +# include +# include +# if defined(__hpux) # include -# else +# endif +# if !defined(__hpux) || defined(__SELECT) # include # endif # include # include # include # include -# include +# if !defined(__SYMBIAN32__) +# include +# endif # include # include # include @@ -117,7 +74,8 @@ # include # endif #endif -#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/solaris_fenced_block.hpp b/ext/asio/asio/detail/solaris_fenced_block.hpp index d337f3b62e..411c824515 100644 --- a/ext/asio/asio/detail/solaris_fenced_block.hpp +++ b/ext/asio/asio/detail/solaris_fenced_block.hpp @@ -1,8 +1,8 @@ // -// solaris_fenced_block.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/solaris_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(__sun) -#include "asio/detail/push_options.hpp" #include -#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -50,8 +46,8 @@ public: } // namespace detail } // namespace asio -#endif // defined(__sun) - #include "asio/detail/pop_options.hpp" +#endif // defined(__sun) + #endif // ASIO_DETAIL_SOLARIS_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/strand_service.hpp b/ext/asio/asio/detail/strand_service.hpp index ea50f412a1..4885b93ee8 100644 --- a/ext/asio/asio/detail/strand_service.hpp +++ b/ext/asio/asio/detail/strand_service.hpp @@ -1,8 +1,8 @@ // -// strand_service.hpp -// ~~~~~~~~~~~~~~~~~~ +// detail/strand_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,23 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" - #include "asio/io_service.hpp" -#include "asio/detail/call_stack.hpp" -#include "asio/detail/completion_handler.hpp" -#include "asio/detail/fenced_block.hpp" -#include "asio/detail/handler_alloc_helpers.hpp" -#include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/operation.hpp" -#include "asio/detail/service_base.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -41,7 +32,10 @@ class strand_service : public asio::detail::service_base { private: + // Helper class to re-post the strand on exit. struct on_do_complete_exit; + + // Helper class to re-post the strand on exit. struct on_dispatch_exit; public: @@ -51,11 +45,7 @@ public: : public operation { public: - strand_impl() - : operation(&strand_service::do_complete), - count_(0) - { - } + strand_impl(); private: // Only this service will have access to the internal values. @@ -76,180 +66,29 @@ public: typedef strand_impl* implementation_type; // Construct a new strand service for the specified io_service. - explicit strand_service(asio::io_service& io_service) - : asio::detail::service_base(io_service), - io_service_(asio::use_service(io_service)), - mutex_(), - salt_(0) - { - } + ASIO_DECL explicit strand_service(asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - op_queue ops; - - asio::detail::mutex::scoped_lock lock(mutex_); - - for (std::size_t i = 0; i < num_implementations; ++i) - if (strand_impl* impl = implementations_[i].get()) - ops.push(impl->queue_); - } + ASIO_DECL void shutdown_service(); // Construct a new strand implementation. - void construct(implementation_type& impl) - { - std::size_t index = boost::hash_value(&impl); - boost::hash_combine(index, salt_++); - index = index % num_implementations; - - asio::detail::mutex::scoped_lock lock(mutex_); - - if (!implementations_[index]) - implementations_[index].reset(new strand_impl); - impl = implementations_[index].get(); - } + ASIO_DECL void construct(implementation_type& impl); // Destroy a strand implementation. - void destroy(implementation_type& impl) - { - impl = 0; - } + void destroy(implementation_type& impl); // Request the io_service to invoke the given handler. template - void dispatch(implementation_type& impl, Handler handler) - { - // If we are already in the strand then the handler can run immediately. - if (call_stack::contains(impl)) - { - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - return; - } - - // Allocate and construct an object to wrap the handler. - typedef completion_handler value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - // If we are running inside the io_service, and no other handler is queued - // or running, then the handler can run immediately. - bool can_dispatch = call_stack::contains(&io_service_); - impl->mutex_.lock(); - bool first = (++impl->count_ == 1); - if (can_dispatch && first) - { - // Immediate invocation is allowed. - impl->mutex_.unlock(); - - // Memory must be releaesed before any upcall is made. - ptr.reset(); - - // Indicate that this strand is executing on the current thread. - call_stack::context ctx(impl); - - // Ensure the next handler, if any, is scheduled on block exit. - on_dispatch_exit on_exit = { &io_service_, impl }; - (void)on_exit; - - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - return; - } - - // Immediate invocation is not allowed, so enqueue for later. - impl->queue_.push(ptr.get()); - impl->mutex_.unlock(); - ptr.release(); - - // The first handler to be enqueued is responsible for scheduling the - // strand. - if (first) - io_service_.post_immediate_completion(impl); - } + void dispatch(implementation_type& impl, Handler handler); // Request the io_service to invoke the given handler and return immediately. template - void post(implementation_type& impl, Handler handler) - { - // Allocate and construct an object to wrap the handler. - typedef completion_handler value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - // Add the handler to the queue. - impl->mutex_.lock(); - bool first = (++impl->count_ == 1); - impl->queue_.push(ptr.get()); - impl->mutex_.unlock(); - ptr.release(); - - // The first handler to be enqueue is responsible for scheduling the strand. - if (first) - io_service_.post_immediate_completion(impl); - } + void post(implementation_type& impl, Handler handler); private: - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - if (owner) - { - strand_impl* impl = static_cast(base); - - // Get the next handler to be executed. - impl->mutex_.lock(); - operation* o = impl->queue_.front(); - impl->queue_.pop(); - impl->mutex_.unlock(); - - // Indicate that this strand is executing on the current thread. - call_stack::context ctx(impl); - - // Ensure the next handler, if any, is scheduled on block exit. - on_do_complete_exit on_exit = { owner, impl }; - (void)on_exit; - - o->complete(*owner); - } - } - - // Helper class to re-post the strand on exit. - struct on_do_complete_exit - { - io_service_impl* owner_; - strand_impl* impl_; - - ~on_do_complete_exit() - { - impl_->mutex_.lock(); - bool more_handlers = (--impl_->count_ > 0); - impl_->mutex_.unlock(); - - if (more_handlers) - owner_->post_immediate_completion(impl_); - } - }; - - // Helper class to re-post the strand on exit. - struct on_dispatch_exit - { - io_service_impl* io_service_; - strand_impl* impl_; - - ~on_dispatch_exit() - { - impl_->mutex_.lock(); - bool more_handlers = (--impl_->count_ > 0); - impl_->mutex_.unlock(); - - if (more_handlers) - io_service_->post_immediate_completion(impl_); - } - }; + ASIO_DECL static void do_complete(io_service_impl* owner, + operation* base, asio::error_code ec, + std::size_t bytes_transferred); // The io_service implementation used to post completions. io_service_impl& io_service_; @@ -273,4 +112,9 @@ private: #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/strand_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/strand_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_DETAIL_STRAND_SERVICE_HPP diff --git a/ext/asio/asio/detail/task_io_service.hpp b/ext/asio/asio/detail/task_io_service.hpp index eb77c1d8de..2d69513b46 100644 --- a/ext/asio/asio/detail/task_io_service.hpp +++ b/ext/asio/asio/detail/task_io_service.hpp @@ -1,8 +1,8 @@ // -// task_io_service.hpp -// ~~~~~~~~~~~~~~~~~~~ +// detail/task_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,180 +15,59 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#if !defined(ASIO_HAS_IOCP) + +#include #include "asio/error_code.hpp" #include "asio/io_service.hpp" -#include "asio/detail/call_stack.hpp" -#include "asio/detail/completion_handler.hpp" -#include "asio/detail/event.hpp" -#include "asio/detail/fenced_block.hpp" -#include "asio/detail/handler_alloc_helpers.hpp" -#include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" -#include "asio/detail/service_base.hpp" +#include "asio/detail/reactor_fwd.hpp" #include "asio/detail/task_io_service_fwd.hpp" #include "asio/detail/task_io_service_operation.hpp" #include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace detail { -template class task_io_service - : public asio::detail::service_base > + : public asio::detail::service_base { public: - typedef task_io_service_operation operation; + typedef task_io_service_operation operation; // Constructor. - task_io_service(asio::io_service& io_service) - : asio::detail::service_base >(io_service), - mutex_(), - task_(0), - task_interrupted_(true), - outstanding_work_(0), - stopped_(false), - shutdown_(false), - first_idle_thread_(0) - { - } + ASIO_DECL task_io_service(asio::io_service& io_service); - void init(size_t /*concurrency_hint*/) - { - } + // How many concurrent threads are likely to run the io_service. + ASIO_DECL void init(std::size_t concurrency_hint); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - asio::detail::mutex::scoped_lock lock(mutex_); - shutdown_ = true; - lock.unlock(); - - // Destroy handler objects. - while (!op_queue_.empty()) - { - operation* o = op_queue_.front(); - op_queue_.pop(); - if (o != &task_operation_) - o->destroy(); - } - - // Reset to initial state. - task_ = 0; - } + ASIO_DECL void shutdown_service(); // Initialise the task, if required. - void init_task() - { - asio::detail::mutex::scoped_lock lock(mutex_); - if (!shutdown_ && !task_) - { - task_ = &use_service(this->get_io_service()); - op_queue_.push(&task_operation_); - wake_one_thread_and_unlock(lock); - } - } + ASIO_DECL void init_task(); // Run the event loop until interrupted or no more work. - size_t run(asio::error_code& ec) - { - ec = asio::error_code(); - if (outstanding_work_ == 0) - { - stop(); - return 0; - } - - typename call_stack::context ctx(this); - - idle_thread_info this_idle_thread; - this_idle_thread.next = 0; - - asio::detail::mutex::scoped_lock lock(mutex_); - - size_t n = 0; - for (; do_one(lock, &this_idle_thread); lock.lock()) - if (n != (std::numeric_limits::max)()) - ++n; - return n; - } + ASIO_DECL std::size_t run(asio::error_code& ec); // Run until interrupted or one operation is performed. - size_t run_one(asio::error_code& ec) - { - ec = asio::error_code(); - if (outstanding_work_ == 0) - { - stop(); - return 0; - } - - typename call_stack::context ctx(this); - - idle_thread_info this_idle_thread; - this_idle_thread.next = 0; - - asio::detail::mutex::scoped_lock lock(mutex_); - - return do_one(lock, &this_idle_thread); - } + ASIO_DECL std::size_t run_one(asio::error_code& ec); // Poll for operations without blocking. - size_t poll(asio::error_code& ec) - { - if (outstanding_work_ == 0) - { - stop(); - ec = asio::error_code(); - return 0; - } - - typename call_stack::context ctx(this); - - asio::detail::mutex::scoped_lock lock(mutex_); - - size_t n = 0; - for (; do_one(lock, 0); lock.lock()) - if (n != (std::numeric_limits::max)()) - ++n; - return n; - } + ASIO_DECL std::size_t poll(asio::error_code& ec); // Poll for one operation without blocking. - size_t poll_one(asio::error_code& ec) - { - ec = asio::error_code(); - if (outstanding_work_ == 0) - { - stop(); - return 0; - } - - typename call_stack::context ctx(this); - - asio::detail::mutex::scoped_lock lock(mutex_); - - return do_one(lock, 0); - } + ASIO_DECL std::size_t poll_one(asio::error_code& ec); // Interrupt the event processing loop. - void stop() - { - asio::detail::mutex::scoped_lock lock(mutex_); - stop_all_threads(lock); - } + ASIO_DECL void stop(); // Reset in preparation for a subsequent run invocation. - void reset() - { - asio::detail::mutex::scoped_lock lock(mutex_); - stopped_ = false; - } + ASIO_DECL void reset(); // Notify that some work has started. void work_started() @@ -205,228 +84,60 @@ public: // Request invocation of the given handler. template - void dispatch(Handler handler) - { - if (call_stack::contains(this)) - { - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - else - post(handler); - } + void dispatch(Handler handler); // Request invocation of the given handler and return immediately. template - void post(Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef completion_handler value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - post_immediate_completion(ptr.get()); - ptr.release(); - } + void post(Handler handler); // Request invocation of the given operation and return immediately. Assumes // that work_started() has not yet been called for the operation. - void post_immediate_completion(operation* op) - { - work_started(); - post_deferred_completion(op); - } + ASIO_DECL void post_immediate_completion(operation* op); // Request invocation of the given operation and return immediately. Assumes // that work_started() was previously called for the operation. - void post_deferred_completion(operation* op) - { - asio::detail::mutex::scoped_lock lock(mutex_); - op_queue_.push(op); - wake_one_thread_and_unlock(lock); - } + ASIO_DECL void post_deferred_completion(operation* op); // Request invocation of the given operations and return immediately. Assumes // that work_started() was previously called for each operation. - void post_deferred_completions(op_queue& ops) - { - if (!ops.empty()) - { - asio::detail::mutex::scoped_lock lock(mutex_); - op_queue_.push(ops); - wake_one_thread_and_unlock(lock); - } - } + ASIO_DECL void post_deferred_completions(op_queue& ops); private: + // Structure containing information about an idle thread. struct idle_thread_info; - size_t do_one(asio::detail::mutex::scoped_lock& lock, - idle_thread_info* this_idle_thread) - { - bool polling = !this_idle_thread; - bool task_has_run = false; - while (!stopped_) - { - if (!op_queue_.empty()) - { - // Prepare to execute first handler from queue. - operation* o = op_queue_.front(); - op_queue_.pop(); - bool more_handlers = (!op_queue_.empty()); - - if (o == &task_operation_) - { - task_interrupted_ = more_handlers || polling; - - // If the task has already run and we're polling then we're done. - if (task_has_run && polling) - { - task_interrupted_ = true; - op_queue_.push(&task_operation_); - return 0; - } - task_has_run = true; - - if (!more_handlers || !wake_one_idle_thread_and_unlock(lock)) - lock.unlock(); - - op_queue completed_ops; - task_cleanup c = { this, &lock, &completed_ops }; - (void)c; - - // Run the task. May throw an exception. Only block if the operation - // queue is empty and we're not polling, otherwise we want to return - // as soon as possible. - task_->run(!more_handlers && !polling, completed_ops); - } - else - { - if (more_handlers) - wake_one_thread_and_unlock(lock); - else - lock.unlock(); - - // Ensure the count of outstanding work is decremented on block exit. - work_finished_on_block_exit on_exit = { this }; - (void)on_exit; - - // Complete the operation. May throw an exception. - o->complete(*this); // deletes the operation object - - return 1; - } - } - else if (this_idle_thread) - { - // Nothing to run right now, so just wait for work to do. - this_idle_thread->next = first_idle_thread_; - first_idle_thread_ = this_idle_thread; - this_idle_thread->wakeup_event.clear(lock); - this_idle_thread->wakeup_event.wait(lock); - } - else - { - return 0; - } - } - - return 0; - } + // Run at most one operation. Blocks only if this_idle_thread is non-null. + ASIO_DECL std::size_t do_one(mutex::scoped_lock& lock, + idle_thread_info* this_idle_thread); // Stop the task and all idle threads. - void stop_all_threads( - asio::detail::mutex::scoped_lock& lock) - { - stopped_ = true; - - while (first_idle_thread_) - { - idle_thread_info* idle_thread = first_idle_thread_; - first_idle_thread_ = idle_thread->next; - idle_thread->next = 0; - idle_thread->wakeup_event.signal(lock); - } - - if (!task_interrupted_ && task_) - { - task_interrupted_ = true; - task_->interrupt(); - } - } + ASIO_DECL void stop_all_threads(mutex::scoped_lock& lock); // Wakes a single idle thread and unlocks the mutex. Returns true if an idle // thread was found. If there is no idle thread, returns false and leaves the // mutex locked. - bool wake_one_idle_thread_and_unlock( - asio::detail::mutex::scoped_lock& lock) - { - if (first_idle_thread_) - { - idle_thread_info* idle_thread = first_idle_thread_; - first_idle_thread_ = idle_thread->next; - idle_thread->next = 0; - idle_thread->wakeup_event.signal_and_unlock(lock); - return true; - } - return false; - } + ASIO_DECL bool wake_one_idle_thread_and_unlock( + mutex::scoped_lock& lock); // Wake a single idle thread, or the task, and always unlock the mutex. - void wake_one_thread_and_unlock( - asio::detail::mutex::scoped_lock& lock) - { - if (!wake_one_idle_thread_and_unlock(lock)) - { - if (!task_interrupted_ && task_) - { - task_interrupted_ = true; - task_->interrupt(); - } - lock.unlock(); - } - } + ASIO_DECL void wake_one_thread_and_unlock( + mutex::scoped_lock& lock); // Helper class to perform task-related operations on block exit. struct task_cleanup; friend struct task_cleanup; - struct task_cleanup - { - ~task_cleanup() - { - // Enqueue the completed operations and reinsert the task at the end of - // the operation queue. - lock_->lock(); - task_io_service_->task_interrupted_ = true; - task_io_service_->op_queue_.push(*ops_); - task_io_service_->op_queue_.push(&task_io_service_->task_operation_); - } - - task_io_service* task_io_service_; - asio::detail::mutex::scoped_lock* lock_; - op_queue* ops_; - }; // Helper class to call work_finished() on block exit. - struct work_finished_on_block_exit - { - ~work_finished_on_block_exit() - { - task_io_service_->work_finished(); - } - - task_io_service* task_io_service_; - }; + struct work_finished_on_block_exit; // Mutex to protect access to internal data. - asio::detail::mutex mutex_; + mutex mutex_; // The task to be run by this service. - Task* task_; + reactor* task_; // Operation object to represent the position of the task in the queue. - struct task_operation : public operation + struct task_operation : operation { task_operation() : operation(0) {} } task_operation_; @@ -446,13 +157,6 @@ private: // Flag to indicate that the dispatcher has been shut down. bool shutdown_; - // Structure containing information about an idle thread. - struct idle_thread_info - { - event wakeup_event; - idle_thread_info* next; - }; - // The threads that are currently idle. idle_thread_info* first_idle_thread_; }; @@ -462,4 +166,11 @@ private: #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/task_io_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/task_io_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // !defined(ASIO_HAS_IOCP) + #endif // ASIO_DETAIL_TASK_IO_SERVICE_HPP diff --git a/ext/asio/asio/detail/task_io_service_fwd.hpp b/ext/asio/asio/detail/task_io_service_fwd.hpp index 5b18d1d700..b66cb07907 100644 --- a/ext/asio/asio/detail/task_io_service_fwd.hpp +++ b/ext/asio/asio/detail/task_io_service_fwd.hpp @@ -1,8 +1,8 @@ // -// task_io_service_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~ +// detail/task_io_service_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - namespace asio { namespace detail { -template class task_io_service; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_TASK_IO_SERVICE_FWD_HPP diff --git a/ext/asio/asio/detail/task_io_service_operation.hpp b/ext/asio/asio/detail/task_io_service_operation.hpp index 6776f6992f..dd4384a14f 100644 --- a/ext/asio/asio/detail/task_io_service_operation.hpp +++ b/ext/asio/asio/detail/task_io_service_operation.hpp @@ -1,8 +1,8 @@ // -// task_io_service_operation.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/task_io_service_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,22 +15,21 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - #include "asio/error_code.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/task_io_service_fwd.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { // Base class for all operations. A function pointer is used instead of virtual // functions to avoid the associated overhead. -template class task_io_service_operation { public: - void complete(task_io_service& owner) + void complete(task_io_service& owner) { func_(&owner, this, asio::error_code(), 0); } @@ -41,8 +40,9 @@ public: } protected: - typedef void (*func_type)(task_io_service*, - task_io_service_operation*, asio::error_code, std::size_t); + typedef void (*func_type)(task_io_service*, + task_io_service_operation*, + asio::error_code, std::size_t); task_io_service_operation(func_type func) : next_(0), diff --git a/ext/asio/asio/detail/thread.hpp b/ext/asio/asio/detail/thread.hpp index 3c9280bcd3..7c4103b28e 100644 --- a/ext/asio/asio/detail/thread.hpp +++ b/ext/asio/asio/detail/thread.hpp @@ -1,8 +1,8 @@ // -// thread.hpp -// ~~~~~~~~~~ +// detail/thread.hpp +// ~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_thread.hpp" @@ -53,6 +49,4 @@ typedef posix_thread thread; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_THREAD_HPP diff --git a/ext/asio/asio/detail/throw_error.hpp b/ext/asio/asio/detail/throw_error.hpp index 51d6e48f4f..d39aa92958 100644 --- a/ext/asio/asio/detail/throw_error.hpp +++ b/ext/asio/asio/detail/throw_error.hpp @@ -1,8 +1,8 @@ // -// throw_error.hpp -// ~~~~~~~~~~~~~~~ +// detail/throw_error.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,25 +15,30 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/error_code.hpp" -#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { +ASIO_DECL void do_throw_error(const asio::error_code& err); + +ASIO_DECL void do_throw_error(const asio::error_code& err, + const char* location); + inline void throw_error(const asio::error_code& err) { if (err) - { - asio::system_error e(err); - boost::throw_exception(e); - } + do_throw_error(err); +} + +inline void throw_error(const asio::error_code& err, + const char* location) +{ + if (err) + do_throw_error(err, location); } } // namespace detail @@ -41,4 +46,8 @@ inline void throw_error(const asio::error_code& err) #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/throw_error.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_DETAIL_THROW_ERROR_HPP diff --git a/ext/asio/asio/detail/timer_op.hpp b/ext/asio/asio/detail/timer_op.hpp index bf3c3ae593..62cd292247 100644 --- a/ext/asio/asio/detail/timer_op.hpp +++ b/ext/asio/asio/detail/timer_op.hpp @@ -1,8 +1,8 @@ // -// timer_op.hpp -// ~~~~~~~~~~~~ +// detail/timer_op.hpp +// ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/operation.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/timer_queue.hpp b/ext/asio/asio/detail/timer_queue.hpp index 2e4d2d5d97..514c434e53 100644 --- a/ext/asio/asio/detail/timer_queue.hpp +++ b/ext/asio/asio/detail/timer_queue.hpp @@ -1,8 +1,8 @@ // -// timer_queue.hpp -// ~~~~~~~~~~~~~~~ +// detail/timer_queue.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,21 +15,22 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include #include #include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/detail/hash_map.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" +#include "asio/error.hpp" +#include "asio/time_traits.hpp" + +#include "asio/detail/push_options.hpp" +#include +#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -45,6 +46,26 @@ public: // The duration type. typedef typename Time_Traits::duration_type duration_type; + // Per-timer data. + class per_timer_data + { + public: + per_timer_data() : next_(0), prev_(0) {} + + private: + friend class timer_queue; + + // The operations waiting on the timer. + op_queue op_queue_; + + // The index of the timer in the heap. + std::size_t heap_index_; + + // Pointers to adjacent timers in a linked list. + per_timer_data* next_; + per_timer_data* prev_; + }; + // Constructor. timer_queue() : timers_(), @@ -55,35 +76,45 @@ public: // Add a new timer to the queue. Returns true if this is the timer that is // earliest in the queue, in which case the reactor's event demultiplexing // function call may need to be interrupted and restarted. - bool enqueue_timer(const time_type& time, timer_op* op, void* token) + bool enqueue_timer(const time_type& time, per_timer_data& timer, timer_op* op) { - // Ensure that there is space for the timer in the heap. We reserve here so - // that the push_back below will not throw due to a reallocation failure. - heap_.reserve(heap_.size() + 1); - - // Insert the new timer into the hash. - typedef typename hash_map::iterator iterator; - typedef typename hash_map::value_type value_type; - std::pair result = - timers_.insert(value_type(token, timer())); - result.first->second.op_queue_.push(op); - if (result.second) + // Enqueue the timer object. + if (timer.prev_ == 0 && &timer != timers_) { - // Put the new timer at the correct position in the heap. - result.first->second.time_ = time; - result.first->second.heap_index_ = heap_.size(); - result.first->second.token_ = token; - heap_.push_back(&result.first->second); - up_heap(heap_.size() - 1); + if (this->is_positive_infinity(time)) + { + // No heap entry is required for timers that never expire. + timer.heap_index_ = (std::numeric_limits::max)(); + } + else + { + // Put the new timer at the correct position in the heap. This is done + // first since push_back() can throw due to allocation failure. + timer.heap_index_ = heap_.size(); + heap_entry entry = { time, &timer }; + heap_.push_back(entry); + up_heap(heap_.size() - 1); + } + + // Insert the new timer into the linked list of active timers. + timer.next_ = timers_; + timer.prev_ = 0; + if (timers_) + timers_->prev_ = &timer; + timers_ = &timer; } - return (heap_[0] == &result.first->second); + // Enqueue the individual timer operation. + timer.op_queue_.push(op); + + // Interrupt reactor only if newly added timer is first to expire. + return timer.heap_index_ == 0 && timer.op_queue_.front() == op; } // Whether there are no timers in the queue. virtual bool empty() const { - return heap_.empty(); + return timers_ == 0; } // Get the time for the timer that is earliest in the queue. @@ -93,12 +124,14 @@ public: return max_duration; boost::posix_time::time_duration duration = Time_Traits::to_posix_duration( - Time_Traits::subtract(heap_[0]->time_, Time_Traits::now())); + Time_Traits::subtract(heap_[0].time_, Time_Traits::now())); if (duration > boost::posix_time::milliseconds(max_duration)) duration = boost::posix_time::milliseconds(max_duration); - else if (duration < boost::posix_time::milliseconds(0)) + else if (duration <= boost::posix_time::milliseconds(0)) duration = boost::posix_time::milliseconds(0); + else if (duration < boost::posix_time::milliseconds(1)) + duration = boost::posix_time::milliseconds(1); return duration.total_milliseconds(); } @@ -110,12 +143,14 @@ public: return max_duration; boost::posix_time::time_duration duration = Time_Traits::to_posix_duration( - Time_Traits::subtract(heap_[0]->time_, Time_Traits::now())); + Time_Traits::subtract(heap_[0].time_, Time_Traits::now())); if (duration > boost::posix_time::microseconds(max_duration)) duration = boost::posix_time::microseconds(max_duration); - else if (duration < boost::posix_time::microseconds(0)) + else if (duration <= boost::posix_time::microseconds(0)) duration = boost::posix_time::microseconds(0); + else if (duration < boost::posix_time::microseconds(1)) + duration = boost::posix_time::microseconds(1); return duration.total_microseconds(); } @@ -124,77 +159,54 @@ public: virtual void get_ready_timers(op_queue& ops) { const time_type now = Time_Traits::now(); - while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0]->time_)) + while (!heap_.empty() && !Time_Traits::less_than(now, heap_[0].time_)) { - timer* t = heap_[0]; - ops.push(t->op_queue_); - remove_timer(t); + per_timer_data* timer = heap_[0].timer_; + ops.push(timer->op_queue_); + remove_timer(*timer); } } // Dequeue all timers. virtual void get_all_timers(op_queue& ops) { - typename hash_map::iterator i = timers_.begin(); - typename hash_map::iterator end = timers_.end(); - while (i != end) + while (timers_) { - ops.push(i->second.op_queue_); - typename hash_map::iterator old_i = i++; - timers_.erase(old_i); + per_timer_data* timer = timers_; + timers_ = timers_->next_; + ops.push(timer->op_queue_); + timer->next_ = 0; + timer->prev_ = 0; } heap_.clear(); - timers_.clear(); } // Cancel and dequeue the timers with the given token. - std::size_t cancel_timer(void* timer_token, op_queue& ops) + std::size_t cancel_timer(per_timer_data& timer, op_queue& ops) { std::size_t num_cancelled = 0; - typedef typename hash_map::iterator iterator; - iterator it = timers_.find(timer_token); - if (it != timers_.end()) + if (timer.prev_ != 0 || &timer == timers_) { - while (timer_op* op = it->second.op_queue_.front()) + while (timer_op* op = timer.op_queue_.front()) { op->ec_ = asio::error::operation_aborted; - it->second.op_queue_.pop(); + timer.op_queue_.pop(); ops.push(op); ++num_cancelled; } - remove_timer(&it->second); + remove_timer(timer); } return num_cancelled; } private: - // Structure representing a single outstanding timer. - struct timer - { - timer() {} - timer(const timer&) {} - void operator=(const timer&) {} - - // The time when the timer should fire. - time_type time_; - - // The operations waiting on the timer. - op_queue op_queue_; - - // The index of the timer in the heap. - size_t heap_index_; - - // The token associated with the timer. - void* token_; - }; - // Move the item at the given index up the heap to its correct position. - void up_heap(size_t index) + void up_heap(std::size_t index) { - size_t parent = (index - 1) / 2; + std::size_t parent = (index - 1) / 2; while (index > 0 - && Time_Traits::less_than(heap_[index]->time_, heap_[parent]->time_)) + && Time_Traits::less_than(heap_[index].time_, heap_[parent].time_)) { swap_heap(index, parent); index = parent; @@ -203,16 +215,16 @@ private: } // Move the item at the given index down the heap to its correct position. - void down_heap(size_t index) + void down_heap(std::size_t index) { - size_t child = index * 2 + 1; + std::size_t child = index * 2 + 1; while (child < heap_.size()) { - size_t min_child = (child + 1 == heap_.size() + std::size_t min_child = (child + 1 == heap_.size() || Time_Traits::less_than( - heap_[child]->time_, heap_[child + 1]->time_)) + heap_[child].time_, heap_[child + 1].time_)) ? child : child + 1; - if (Time_Traits::less_than(heap_[index]->time_, heap_[min_child]->time_)) + if (Time_Traits::less_than(heap_[index].time_, heap_[min_child].time_)) break; swap_heap(index, min_child); index = min_child; @@ -221,20 +233,20 @@ private: } // Swap two entries in the heap. - void swap_heap(size_t index1, size_t index2) + void swap_heap(std::size_t index1, std::size_t index2) { - timer* tmp = heap_[index1]; + heap_entry tmp = heap_[index1]; heap_[index1] = heap_[index2]; heap_[index2] = tmp; - heap_[index1]->heap_index_ = index1; - heap_[index2]->heap_index_ = index2; + heap_[index1].timer_->heap_index_ = index1; + heap_[index2].timer_->heap_index_ = index2; } // Remove a timer from the heap and list of timers. - void remove_timer(timer* t) + void remove_timer(per_timer_data& timer) { // Remove the timer from the heap. - size_t index = t->heap_index_; + std::size_t index = timer.heap_index_; if (!heap_.empty() && index < heap_.size()) { if (index == heap_.size() - 1) @@ -245,29 +257,112 @@ private: { swap_heap(index, heap_.size() - 1); heap_.pop_back(); - size_t parent = (index - 1) / 2; + std::size_t parent = (index - 1) / 2; if (index > 0 && Time_Traits::less_than( - heap_[index]->time_, heap_[parent]->time_)) + heap_[index].time_, heap_[parent].time_)) up_heap(index); else down_heap(index); } } - // Remove the timer from the hash. - typedef typename hash_map::iterator iterator; - iterator it = timers_.find(t->token_); - if (it != timers_.end()) - timers_.erase(it); + // Remove the timer from the linked list of active timers. + if (timers_ == &timer) + timers_ = timer.next_; + if (timer.prev_) + timer.prev_->next_ = timer.next_; + if (timer.next_) + timer.next_->prev_= timer.prev_; + timer.next_ = 0; + timer.prev_ = 0; } - // A hash of timer token to linked lists of timers. - hash_map timers_; + // Determine if the specified absolute time is positive infinity. + template + static bool is_positive_infinity(const Time_Type&) + { + return false; + } + + // Determine if the specified absolute time is positive infinity. + static bool is_positive_infinity(const boost::posix_time::ptime& time) + { + return time == boost::posix_time::pos_infin; + } + + // The head of a linked list of all active timers. + per_timer_data* timers_; + + struct heap_entry + { + // The time when the timer should fire. + time_type time_; + + // The associated timer with enqueued operations. + per_timer_data* timer_; + }; // The heap of timers, with the earliest timer at the front. - std::vector heap_; + std::vector heap_; }; +#if !defined(ASIO_HEADER_ONLY) + +struct forwarding_posix_time_traits : time_traits {}; + +// Template specialisation for the commonly used instantation. +template <> +class timer_queue > + : public timer_queue_base +{ +public: + // The time type. + typedef boost::posix_time::ptime time_type; + + // The duration type. + typedef boost::posix_time::time_duration duration_type; + + // Per-timer data. + typedef timer_queue::per_timer_data + per_timer_data; + + // Constructor. + ASIO_DECL timer_queue(); + + // Destructor. + ASIO_DECL virtual ~timer_queue(); + + // Add a new timer to the queue. Returns true if this is the timer that is + // earliest in the queue, in which case the reactor's event demultiplexing + // function call may need to be interrupted and restarted. + ASIO_DECL bool enqueue_timer(const time_type& time, + per_timer_data& timer, timer_op* op); + + // Whether there are no timers in the queue. + ASIO_DECL virtual bool empty() const; + + // Get the time for the timer that is earliest in the queue. + ASIO_DECL virtual long wait_duration_msec(long max_duration) const; + + // Get the time for the timer that is earliest in the queue. + ASIO_DECL virtual long wait_duration_usec(long max_duration) const; + + // Dequeue all timers not later than the current time. + ASIO_DECL virtual void get_ready_timers(op_queue& ops); + + // Dequeue all timers. + ASIO_DECL virtual void get_all_timers(op_queue& ops); + + // Cancel and dequeue the timers with the given token. + ASIO_DECL std::size_t cancel_timer( + per_timer_data& timer, op_queue& ops); + +private: + timer_queue impl_; +}; + +#endif // !defined(ASIO_HEADER_ONLY) + } // namespace detail } // namespace asio diff --git a/ext/asio/asio/detail/timer_queue_base.hpp b/ext/asio/asio/detail/timer_queue_base.hpp index f978667535..0e8d4c7868 100644 --- a/ext/asio/asio/detail/timer_queue_base.hpp +++ b/ext/asio/asio/detail/timer_queue_base.hpp @@ -1,8 +1,8 @@ // -// timer_queue_base.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// detail/timer_queue_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/op_queue.hpp" #include "asio/detail/operation.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/timer_queue_fwd.hpp b/ext/asio/asio/detail/timer_queue_fwd.hpp index 53172448bc..0cca96703c 100644 --- a/ext/asio/asio/detail/timer_queue_fwd.hpp +++ b/ext/asio/asio/detail/timer_queue_fwd.hpp @@ -1,8 +1,8 @@ // -// timer_queue_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~ +// detail/timer_queue_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,8 +15,6 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - namespace asio { namespace detail { @@ -26,6 +24,4 @@ class timer_queue; } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_TIMER_QUEUE_FWD_HPP diff --git a/ext/asio/asio/detail/timer_queue_set.hpp b/ext/asio/asio/detail/timer_queue_set.hpp index 6860867074..9e8d428414 100644 --- a/ext/asio/asio/detail/timer_queue_set.hpp +++ b/ext/asio/asio/detail/timer_queue_set.hpp @@ -1,8 +1,8 @@ // -// timer_queue_set.hpp -// ~~~~~~~~~~~~~~~~~~~ +// detail/timer_queue_set.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/timer_queue_base.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -26,82 +27,28 @@ class timer_queue_set { public: // Constructor. - timer_queue_set() - : first_(0) - { - } + ASIO_DECL timer_queue_set(); // Add a timer queue to the set. - void insert(timer_queue_base* q) - { - q->next_ = first_; - first_ = q; - } + ASIO_DECL void insert(timer_queue_base* q); // Remove a timer queue from the set. - void erase(timer_queue_base* q) - { - if (first_) - { - if (q == first_) - { - first_ = q->next_; - q->next_ = 0; - return; - } - - for (timer_queue_base* p = first_; p->next_; p = p->next_) - { - if (p->next_ == q) - { - p->next_ = q->next_; - q->next_ = 0; - return; - } - } - } - } + ASIO_DECL void erase(timer_queue_base* q); // Determine whether all queues are empty. - bool all_empty() const - { - for (timer_queue_base* p = first_; p; p = p->next_) - if (!p->empty()) - return false; - return true; - } + ASIO_DECL bool all_empty() const; // Get the wait duration in milliseconds. - long wait_duration_msec(long max_duration) const - { - long min_duration = max_duration; - for (timer_queue_base* p = first_; p; p = p->next_) - min_duration = p->wait_duration_msec(min_duration); - return min_duration; - } + ASIO_DECL long wait_duration_msec(long max_duration) const; // Get the wait duration in microseconds. - long wait_duration_usec(long max_duration) const - { - long min_duration = max_duration; - for (timer_queue_base* p = first_; p; p = p->next_) - min_duration = p->wait_duration_usec(min_duration); - return min_duration; - } + ASIO_DECL long wait_duration_usec(long max_duration) const; // Dequeue all ready timers. - void get_ready_timers(op_queue& ops) - { - for (timer_queue_base* p = first_; p; p = p->next_) - p->get_ready_timers(ops); - } + ASIO_DECL void get_ready_timers(op_queue& ops); // Dequeue all timers. - void get_all_timers(op_queue& ops) - { - for (timer_queue_base* p = first_; p; p = p->next_) - p->get_all_timers(ops); - } + ASIO_DECL void get_all_timers(op_queue& ops); private: timer_queue_base* first_; @@ -112,4 +59,8 @@ private: #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/timer_queue_set.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_DETAIL_TIMER_QUEUE_SET_HPP diff --git a/ext/asio/asio/detail/timer_scheduler.hpp b/ext/asio/asio/detail/timer_scheduler.hpp index 6822d3f793..1222e630e1 100644 --- a/ext/asio/asio/detail/timer_scheduler.hpp +++ b/ext/asio/asio/detail/timer_scheduler.hpp @@ -1,8 +1,8 @@ // -// timer_scheduler.hpp -// ~~~~~~~~~~~~~~~~~~~ +// detail/timer_scheduler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,8 +15,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/timer_scheduler_fwd.hpp" #if defined(ASIO_HAS_IOCP) @@ -31,6 +30,4 @@ # include "asio/detail/select_reactor.hpp" #endif -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_TIMER_SCHEDULER_HPP diff --git a/ext/asio/asio/detail/timer_scheduler_fwd.hpp b/ext/asio/asio/detail/timer_scheduler_fwd.hpp index d766a87fb2..5fced5005f 100644 --- a/ext/asio/asio/detail/timer_scheduler_fwd.hpp +++ b/ext/asio/asio/detail/timer_scheduler_fwd.hpp @@ -1,8 +1,8 @@ // -// timer_scheduler_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~ +// detail/timer_scheduler_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,19 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/dev_poll_reactor_fwd.hpp" -#include "asio/detail/epoll_reactor_fwd.hpp" -#include "asio/detail/kqueue_reactor_fwd.hpp" -#include "asio/detail/select_reactor_fwd.hpp" -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_service_fwd.hpp" +#elif defined(ASIO_HAS_EPOLL) +# include "asio/detail/epoll_reactor_fwd.hpp" +#elif defined(ASIO_HAS_KQUEUE) +# include "asio/detail/kqueue_reactor_fwd.hpp" +#elif defined(ASIO_HAS_DEV_POLL) +# include "asio/detail/dev_poll_reactor_fwd.hpp" +#else +# include "asio/detail/select_reactor_fwd.hpp" +#endif namespace asio { namespace detail { @@ -35,12 +41,10 @@ typedef kqueue_reactor timer_scheduler; #elif defined(ASIO_HAS_DEV_POLL) typedef dev_poll_reactor timer_scheduler; #else -typedef select_reactor timer_scheduler; +typedef select_reactor timer_scheduler; #endif } // namespace detail } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_DETAIL_TIMER_SCHEDULER_FWD_HPP diff --git a/ext/asio/asio/detail/tss_ptr.hpp b/ext/asio/asio/detail/tss_ptr.hpp index ac67d9f0ce..0218f5f779 100644 --- a/ext/asio/asio/detail/tss_ptr.hpp +++ b/ext/asio/asio/detail/tss_ptr.hpp @@ -1,8 +1,8 @@ // -// tss_ptr.hpp -// ~~~~~~~~~~~ +// detail/tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_HAS_THREADS) || defined(ASIO_DISABLE_THREADS) # include "asio/detail/null_tss_ptr.hpp" @@ -31,6 +27,8 @@ # error Only Windows and POSIX are supported! #endif +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/detail/wait_handler.hpp b/ext/asio/asio/detail/wait_handler.hpp new file mode 100644 index 0000000000..0df16cb6a9 --- /dev/null +++ b/ext/asio/asio/detail/wait_handler.hpp @@ -0,0 +1,76 @@ +// +// detail/wait_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WAIT_HANDLER_HPP +#define ASIO_DETAIL_WAIT_HANDLER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/timer_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class wait_handler : public timer_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(wait_handler); + + wait_handler(Handler h) + : timer_op(&wait_handler::do_complete), + handler_(h) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + // Take ownership of the handler object. + wait_handler* h(static_cast(base)); + ptr p = { boost::addressof(h->handler_), h, h }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(h->handler_, h->ec_); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_DETAIL_WAIT_HANDLER_HPP diff --git a/ext/asio/asio/detail/weak_ptr.hpp b/ext/asio/asio/detail/weak_ptr.hpp new file mode 100644 index 0000000000..35dca19a41 --- /dev/null +++ b/ext/asio/asio/detail/weak_ptr.hpp @@ -0,0 +1,39 @@ +// +// detail/weak_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WEAK_PTR_HPP +#define ASIO_DETAIL_WEAK_PTR_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +# include +#else +# include +#endif + +namespace asio { +namespace detail { + +#if defined(_MSC_VER) && (_MSC_VER >= 1600) +using std::weak_ptr; +#else +using boost::weak_ptr; +#endif + +} // namespace detail +} // namespace asio + +#endif // ASIO_DETAIL_WEAK_PTR_HPP diff --git a/ext/asio/asio/detail/win_event.hpp b/ext/asio/asio/detail/win_event.hpp index cabb2c389c..71ad7e0b5a 100644 --- a/ext/asio/asio/detail/win_event.hpp +++ b/ext/asio/asio/detail/win_event.hpp @@ -1,8 +1,8 @@ // -// win_event.hpp -// ~~~~~~~~~~~~~ +// detail/win_event.hpp +// ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,23 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) -#include "asio/error.hpp" -#include "asio/system_error.hpp" +#include #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace detail { @@ -41,19 +33,7 @@ class win_event { public: // Constructor. - win_event() - : event_(::CreateEvent(0, true, false, 0)) - { - if (!event_) - { - DWORD last_error = ::GetLastError(); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "event"); - boost::throw_exception(e); - } - } + ASIO_DECL win_event(); // Destructor. ~win_event() @@ -105,8 +85,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_event.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_WINDOWS) + #endif // ASIO_DETAIL_WIN_EVENT_HPP diff --git a/ext/asio/asio/detail/win_fd_set_adapter.hpp b/ext/asio/asio/detail/win_fd_set_adapter.hpp index 012a10ff76..0cb9d8c9d8 100644 --- a/ext/asio/asio/detail/win_fd_set_adapter.hpp +++ b/ext/asio/asio/detail/win_fd_set_adapter.hpp @@ -1,8 +1,8 @@ // -// win_fd_set_adapter.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_fd_set_adapter.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/socket_types.hpp" -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -81,8 +83,8 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + #endif // ASIO_DETAIL_WIN_FD_SET_ADAPTER_HPP diff --git a/ext/asio/asio/detail/win_fenced_block.hpp b/ext/asio/asio/detail/win_fenced_block.hpp index 6338488f91..011f44b23a 100644 --- a/ext/asio/asio/detail/win_fenced_block.hpp +++ b/ext/asio/asio/detail/win_fenced_block.hpp @@ -1,8 +1,8 @@ // -// win_fenced_block.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// detail/win_fenced_block.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) && !defined(UNDER_CE) #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -35,7 +33,10 @@ public: // Constructor. win_fenced_block() { -#if defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +#if defined(__BORLANDC__) + LONG barrier = 0; + ::InterlockedExchange(&barrier, 1); +#elif defined(BOOST_MSVC) && ((BOOST_MSVC < 1400) || !defined(MemoryBarrier)) # if defined(_M_IX86) # pragma warning(push) # pragma warning(disable:4793) @@ -43,15 +44,18 @@ public: __asm { xchg barrier, eax } # pragma warning(pop) # endif // defined(_M_IX86) -#else // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +#else MemoryBarrier(); -#endif // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +#endif } // Destructor. ~win_fenced_block() { -#if defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +#if defined(__BORLANDC__) + LONG barrier = 0; + ::InterlockedExchange(&barrier, 1); +#elif defined(BOOST_MSVC) && ((BOOST_MSVC < 1400) || !defined(MemoryBarrier)) # if defined(_M_IX86) # pragma warning(push) # pragma warning(disable:4793) @@ -59,17 +63,17 @@ public: __asm { xchg barrier, eax } # pragma warning(pop) # endif // defined(_M_IX86) -#else // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +#else MemoryBarrier(); -#endif // defined(BOOST_MSVC) && (BOOST_MSVC < 1400) +#endif } }; } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) - #include "asio/detail/pop_options.hpp" +#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) + #endif // ASIO_DETAIL_WIN_FENCED_BLOCK_HPP diff --git a/ext/asio/asio/detail/win_iocp_handle_read_op.hpp b/ext/asio/asio/detail/win_iocp_handle_read_op.hpp new file mode 100644 index 0000000000..fb358bed7b --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_handle_read_op.hpp @@ -0,0 +1,101 @@ +// +// detail/win_iocp_handle_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_handle_read_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_read_op); + + win_iocp_handle_read_op(const MutableBufferSequence& buffers, Handler handler) + : operation(&win_iocp_handle_read_op::do_complete), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_handle_read_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (owner) + { + // Check whether buffers are still valid. + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_HANDLE_EOF) + ec = asio::error::eof; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_READ_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_handle_service.hpp b/ext/asio/asio/detail/win_iocp_handle_service.hpp index bfc159ce72..34f725f163 100644 --- a/ext/asio/asio/detail/win_iocp_handle_service.hpp +++ b/ext/asio/asio/detail/win_iocp_handle_service.hpp @@ -1,8 +1,8 @@ // -// win_iocp_handle_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_iocp_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,26 +16,23 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) -#include "asio/detail/push_options.hpp" #include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/bind_handler.hpp" #include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/detail/handler_alloc_helpers.hpp" -#include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/operation.hpp" +#include "asio/detail/win_iocp_handle_read_op.hpp" +#include "asio/detail/win_iocp_handle_write_op.hpp" #include "asio/detail/win_iocp_io_service.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -76,75 +73,20 @@ public: implementation_type* prev_; }; - win_iocp_handle_service(asio::io_service& io_service) - : iocp_service_(asio::use_service(io_service)), - mutex_(), - impl_list_(0) - { - } + ASIO_DECL win_iocp_handle_service(asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - // Close all implementations, causing all operations to complete. - asio::detail::mutex::scoped_lock lock(mutex_); - implementation_type* impl = impl_list_; - while (impl) - { - close_for_destruction(*impl); - impl = impl->next_; - } - } + ASIO_DECL void shutdown_service(); // Construct a new handle implementation. - void construct(implementation_type& impl) - { - impl.handle_ = INVALID_HANDLE_VALUE; - impl.safe_cancellation_thread_id_ = 0; - - // Insert implementation into linked list of all implementations. - asio::detail::mutex::scoped_lock lock(mutex_); - impl.next_ = impl_list_; - impl.prev_ = 0; - if (impl_list_) - impl_list_->prev_ = &impl; - impl_list_ = &impl; - } + ASIO_DECL void construct(implementation_type& impl); // Destroy a handle implementation. - void destroy(implementation_type& impl) - { - close_for_destruction(impl); - - // Remove implementation from linked list of all implementations. - asio::detail::mutex::scoped_lock lock(mutex_); - if (impl_list_ == &impl) - impl_list_ = impl.next_; - if (impl.prev_) - impl.prev_->next_ = impl.next_; - if (impl.next_) - impl.next_->prev_= impl.prev_; - impl.next_ = 0; - impl.prev_ = 0; - } + ASIO_DECL void destroy(implementation_type& impl); // Assign a native handle to a handle implementation. - asio::error_code assign(implementation_type& impl, - const native_type& native_handle, asio::error_code& ec) - { - if (is_open(impl)) - { - ec = asio::error::already_open; - return ec; - } - - if (iocp_service_.register_handle(native_handle, ec)) - return ec; - - impl.handle_ = native_handle; - ec = asio::error_code(); - return ec; - } + ASIO_DECL asio::error_code assign(implementation_type& impl, + const native_type& native_handle, asio::error_code& ec); // Determine whether the handle is open. bool is_open(const implementation_type& impl) const @@ -153,26 +95,8 @@ public: } // Destroy a handle implementation. - asio::error_code close(implementation_type& impl, - asio::error_code& ec) - { - if (is_open(impl)) - { - if (!::CloseHandle(impl.handle_)) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - impl.handle_ = INVALID_HANDLE_VALUE; - impl.safe_cancellation_thread_id_ = 0; - } - - ec = asio::error_code(); - return ec; - } + ASIO_DECL asio::error_code close(implementation_type& impl, + asio::error_code& ec); // Get the native handle representation. native_type native(const implementation_type& impl) const @@ -181,106 +105,8 @@ public: } // Cancel all operations associated with the handle. - asio::error_code cancel(implementation_type& impl, - asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - } - else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( - ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) - { - // The version of Windows supports cancellation from any thread. - typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); - cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; - if (!cancel_io_ex(impl.handle_, 0)) - { - DWORD last_error = ::GetLastError(); - if (last_error == ERROR_NOT_FOUND) - { - // ERROR_NOT_FOUND means that there were no operations to be - // cancelled. We swallow this error to match the behaviour on other - // platforms. - ec = asio::error_code(); - } - else - { - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - } - else - { - ec = asio::error_code(); - } - } - else if (impl.safe_cancellation_thread_id_ == 0) - { - // No operations have been started, so there's nothing to cancel. - ec = asio::error_code(); - } - else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) - { - // Asynchronous operations have been started from the current thread only, - // so it is safe to try to cancel them using CancelIo. - if (!::CancelIo(impl.handle_)) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - else - { - ec = asio::error_code(); - } - } - else - { - // Asynchronous operations have been started from more than one thread, - // so cancellation is not safe. - ec = asio::error::operation_not_supported; - } - - return ec; - } - - class overlapped_wrapper - : public OVERLAPPED - { - public: - explicit overlapped_wrapper(asio::error_code& ec) - { - Internal = 0; - InternalHigh = 0; - Offset = 0; - OffsetHigh = 0; - - // Create a non-signalled manual-reset event, for GetOverlappedResult. - hEvent = ::CreateEvent(0, TRUE, FALSE, 0); - if (hEvent) - { - // As documented in GetQueuedCompletionStatus, setting the low order - // bit of this event prevents our synchronous writes from being treated - // as completion port events. - *reinterpret_cast(&hEvent) |= 1; - } - else - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - } - - ~overlapped_wrapper() - { - if (hEvent) - { - ::CloseHandle(hEvent); - } - } - }; + ASIO_DECL asio::error_code cancel(implementation_type& impl, + asio::error_code& ec); // Write the given data. Returns the number of bytes written. template @@ -296,109 +122,13 @@ public: size_t write_some_at(implementation_type& impl, boost::uint64_t offset, const ConstBufferSequence& buffers, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - asio::const_buffer buffer = buffer_sequence_adapter::first(buffers); - // A request to write 0 bytes on a handle is a no-op. - if (asio::buffer_size(buffer) == 0) - { - ec = asio::error_code(); - return 0; - } - - overlapped_wrapper overlapped(ec); - if (ec) - { - return 0; - } - - // Write the data. - overlapped.Offset = offset & 0xFFFFFFFF; - overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; - BOOL ok = ::WriteFile(impl.handle_, - asio::buffer_cast(buffer), - static_cast(asio::buffer_size(buffer)), 0, &overlapped); - if (!ok) - { - DWORD last_error = ::GetLastError(); - if (last_error != ERROR_IO_PENDING) - { - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return 0; - } - } - - // Wait for the operation to complete. - DWORD bytes_transferred = 0; - ok = ::GetOverlappedResult(impl.handle_, - &overlapped, &bytes_transferred, TRUE); - if (!ok) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return 0; - } - - ec = asio::error_code(); - return bytes_transferred; + return do_write(impl, offset, buffer, ec); } - template - class write_op : public operation - { - public: - write_op(const ConstBufferSequence& buffers, Handler handler) - : operation(&write_op::do_complete), - buffers_(buffers), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - write_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { -#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - buffer_sequence_adapter::validate(o->buffers_); -#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, ec, bytes_transferred); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - ConstBufferSequence buffers_; - Handler handler_; - }; - // Start an asynchronous write. The data being written must be valid for the // lifetime of the asynchronous operation. template @@ -415,15 +145,16 @@ public: const ConstBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef write_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, buffers, handler); + typedef win_iocp_handle_write_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(buffers, handler); start_write_op(impl, offset, buffer_sequence_adapter::first(buffers), ptr.get()); - ptr.release(); + ConstBufferSequence>::first(buffers), p.p); + p.v = p.p = 0; } // Read some data. Returns the number of bytes received. @@ -439,129 +170,13 @@ public: size_t read_some_at(implementation_type& impl, boost::uint64_t offset, const MutableBufferSequence& buffers, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - asio::mutable_buffer buffer = buffer_sequence_adapter::first(buffers); - // A request to read 0 bytes on a stream handle is a no-op. - if (asio::buffer_size(buffer) == 0) - { - ec = asio::error_code(); - return 0; - } - - overlapped_wrapper overlapped(ec); - if (ec) - { - return 0; - } - - // Read some data. - overlapped.Offset = offset & 0xFFFFFFFF; - overlapped.OffsetHigh = (offset >> 32) & 0xFFFFFFFF; - BOOL ok = ::ReadFile(impl.handle_, - asio::buffer_cast(buffer), - static_cast(asio::buffer_size(buffer)), 0, &overlapped); - if (!ok) - { - DWORD last_error = ::GetLastError(); - if (last_error != ERROR_IO_PENDING && last_error != ERROR_MORE_DATA) - { - if (last_error == ERROR_HANDLE_EOF) - { - ec = asio::error::eof; - } - else - { - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - return 0; - } - } - - // Wait for the operation to complete. - DWORD bytes_transferred = 0; - ok = ::GetOverlappedResult(impl.handle_, - &overlapped, &bytes_transferred, TRUE); - if (!ok) - { - DWORD last_error = ::GetLastError(); - if (last_error == ERROR_HANDLE_EOF) - { - ec = asio::error::eof; - } - else - { - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - return 0; - } - - ec = asio::error_code(); - return bytes_transferred; + return do_read(impl, offset, buffer, ec); } - template - class read_op : public operation - { - public: - read_op(const MutableBufferSequence& buffers, Handler handler) - : operation(&read_op::do_complete), - buffers_(buffers), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - read_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { -#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - buffer_sequence_adapter::validate(o->buffers_); -#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) - - // Map non-portable errors to their portable counterparts. - if (ec.value() == ERROR_HANDLE_EOF) - { - ec = asio::error::eof; - } - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, ec, bytes_transferred); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - MutableBufferSequence buffers_; - Handler handler_; - }; - // Start an asynchronous read. The buffer for the data being received must be // valid for the lifetime of the asynchronous operation. template @@ -579,15 +194,16 @@ public: const MutableBufferSequence& buffers, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef read_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, buffers, handler); + typedef win_iocp_handle_read_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(buffers, handler); start_read_op(impl, offset, buffer_sequence_adapter::first(buffers), ptr.get()); - ptr.release(); + MutableBufferSequence>::first(buffers), p.p); + p.v = p.p = 0; } private: @@ -613,113 +229,42 @@ private: void async_read_some_at(implementation_type& impl, boost::uint64_t offset, const null_buffers& buffers, Handler handler); - // Helper function to start a write operation. - void start_write_op(implementation_type& impl, boost::uint64_t offset, - const asio::const_buffer& buffer, operation* op) - { - update_cancellation_thread_id(impl); - iocp_service_.work_started(); + // Helper class for waiting for synchronous operations to complete. + class overlapped_wrapper; - if (!is_open(impl)) - { - iocp_service_.on_completion(op, asio::error::bad_descriptor); - } - else if (asio::buffer_size(buffer) == 0) - { - // A request to write 0 bytes on a handle is a no-op. - iocp_service_.on_completion(op); - } - else - { - DWORD bytes_transferred = 0; - op->Offset = offset & 0xFFFFFFFF; - op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; - BOOL ok = ::WriteFile(impl.handle_, - asio::buffer_cast(buffer), - static_cast(asio::buffer_size(buffer)), - &bytes_transferred, op); - DWORD last_error = ::GetLastError(); - if (!ok && last_error != ERROR_IO_PENDING - && last_error != ERROR_MORE_DATA) - { - iocp_service_.on_completion(op, last_error, bytes_transferred); - } - else - { - iocp_service_.on_pending(op); - } - } - } + // Helper function to perform a synchronous write operation. + ASIO_DECL size_t do_write(implementation_type& impl, + boost::uint64_t offset, const asio::const_buffer& buffer, + asio::error_code& ec); + + // Helper function to start a write operation. + ASIO_DECL void start_write_op(implementation_type& impl, + boost::uint64_t offset, const asio::const_buffer& buffer, + operation* op); + + // Helper function to perform a synchronous write operation. + ASIO_DECL size_t do_read(implementation_type& impl, + boost::uint64_t offset, const asio::mutable_buffer& buffer, + asio::error_code& ec); // Helper function to start a read operation. - void start_read_op(implementation_type& impl, boost::uint64_t offset, - const asio::mutable_buffer& buffer, operation* op) - { - update_cancellation_thread_id(impl); - iocp_service_.work_started(); - - if (!is_open(impl)) - { - iocp_service_.on_completion(op, asio::error::bad_descriptor); - } - else if (asio::buffer_size(buffer) == 0) - { - // A request to read 0 bytes on a handle is a no-op. - iocp_service_.on_completion(op); - } - else - { - DWORD bytes_transferred = 0; - op->Offset = offset & 0xFFFFFFFF; - op->OffsetHigh = (offset >> 32) & 0xFFFFFFFF; - BOOL ok = ::ReadFile(impl.handle_, - asio::buffer_cast(buffer), - static_cast(asio::buffer_size(buffer)), - &bytes_transferred, op); - DWORD last_error = ::GetLastError(); - if (!ok && last_error != ERROR_IO_PENDING - && last_error != ERROR_MORE_DATA) - { - iocp_service_.on_completion(op, last_error, bytes_transferred); - } - else - { - iocp_service_.on_pending(op); - } - } - } + ASIO_DECL void start_read_op(implementation_type& impl, + boost::uint64_t offset, const asio::mutable_buffer& buffer, + operation* op); // Update the ID of the thread from which cancellation is safe. - void update_cancellation_thread_id(implementation_type& impl) - { -#if defined(ASIO_ENABLE_CANCELIO) - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#else // defined(ASIO_ENABLE_CANCELIO) - (void)impl; -#endif // defined(ASIO_ENABLE_CANCELIO) - } + ASIO_DECL void update_cancellation_thread_id(implementation_type& impl); // Helper function to close a handle when the associated object is being // destroyed. - void close_for_destruction(implementation_type& impl) - { - if (is_open(impl)) - { - ::CloseHandle(impl.handle_); - impl.handle_ = INVALID_HANDLE_VALUE; - impl.safe_cancellation_thread_id_ = 0; - } - } + ASIO_DECL void close_for_destruction(implementation_type& impl); // The IOCP service used for running asynchronous operations and dispatching // handlers. win_iocp_io_service& iocp_service_; // Mutex to protect access to the linked list of implementations. - asio::detail::mutex mutex_; + mutex mutex_; // The head of a linked list of all implementations. implementation_type* impl_list_; @@ -728,8 +273,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_IOCP) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_handle_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + #endif // ASIO_DETAIL_WIN_IOCP_HANDLE_SERVICE_HPP diff --git a/ext/asio/asio/detail/win_iocp_handle_write_op.hpp b/ext/asio/asio/detail/win_iocp_handle_write_op.hpp new file mode 100644 index 0000000000..ddfe8fc2a8 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_handle_write_op.hpp @@ -0,0 +1,97 @@ +// +// detail/win_iocp_handle_write_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_handle_write_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_handle_write_op); + + win_iocp_handle_write_op(const ConstBufferSequence& buffers, Handler handler) + : operation(&win_iocp_handle_write_op::do_complete), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_handle_write_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + if (owner) + { + // Check whether buffers are still valid. + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + ConstBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_HANDLE_WRITE_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_io_service.hpp b/ext/asio/asio/detail/win_iocp_io_service.hpp index fd899c1146..c8974b60cd 100644 --- a/ext/asio/asio/detail/win_iocp_io_service.hpp +++ b/ext/asio/asio/detail/win_iocp_io_service.hpp @@ -1,8 +1,8 @@ // -// win_iocp_io_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_iocp_io_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,33 +15,24 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - +#include #include "asio/io_service.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/call_stack.hpp" -#include "asio/detail/completion_handler.hpp" -#include "asio/detail/fenced_block.hpp" -#include "asio/detail/handler_alloc_helpers.hpp" -#include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/mutex.hpp" #include "asio/detail/op_queue.hpp" -#include "asio/detail/service_base.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/timer_op.hpp" #include "asio/detail/timer_queue_base.hpp" #include "asio/detail/timer_queue_fwd.hpp" #include "asio/detail/timer_queue_set.hpp" +#include "asio/detail/win_iocp_io_service_fwd.hpp" #include "asio/detail/win_iocp_operation.hpp" +#include "asio/detail/thread.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -52,69 +43,13 @@ class win_iocp_io_service : public asio::detail::service_base { public: - typedef win_iocp_operation operation; - // Constructor. - win_iocp_io_service(asio::io_service& io_service) - : asio::detail::service_base(io_service), - iocp_(), - outstanding_work_(0), - stopped_(0), - shutdown_(0), - timer_thread_(0), - timer_interrupt_issued_(false) - { - } + ASIO_DECL win_iocp_io_service(asio::io_service& io_service); - void init(size_t concurrency_hint) - { - iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, - static_cast((std::min)(concurrency_hint, DWORD(~0)))); - if (!iocp_.handle) - { - DWORD last_error = ::GetLastError(); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "iocp"); - boost::throw_exception(e); - } - } + ASIO_DECL void init(size_t concurrency_hint); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - ::InterlockedExchange(&shutdown_, 1); - - while (::InterlockedExchangeAdd(&outstanding_work_, 0) > 0) - { - op_queue ops; - timer_queues_.get_all_timers(ops); - ops.push(completed_ops_); - if (!ops.empty()) - { - while (operation* op = ops.front()) - { - ops.pop(); - ::InterlockedDecrement(&outstanding_work_); - op->destroy(); - } - } - else - { - DWORD bytes_transferred = 0; - dword_ptr_t completion_key = 0; - LPOVERLAPPED overlapped = 0; - ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, - &completion_key, &overlapped, max_timeout); - if (overlapped) - { - ::InterlockedDecrement(&outstanding_work_); - static_cast(overlapped)->destroy(); - } - } - } - } + ASIO_DECL void shutdown_service(); // Initialise the task. Nothing to do here. void init_task() @@ -122,106 +57,23 @@ public: } // Register a handle with the IO completion port. - asio::error_code register_handle( - HANDLE handle, asio::error_code& ec) - { - if (::CreateIoCompletionPort(handle, iocp_.handle, 0, 0) == 0) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - else - { - ec = asio::error_code(); - } - return ec; - } + ASIO_DECL asio::error_code register_handle( + HANDLE handle, asio::error_code& ec); // Run the event loop until stopped or no more work. - size_t run(asio::error_code& ec) - { - if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) - { - stop(); - ec = asio::error_code(); - return 0; - } - - call_stack::context ctx(this); - - size_t n = 0; - while (do_one(true, ec)) - if (n != (std::numeric_limits::max)()) - ++n; - return n; - } + ASIO_DECL size_t run(asio::error_code& ec); // Run until stopped or one operation is performed. - size_t run_one(asio::error_code& ec) - { - if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) - { - stop(); - ec = asio::error_code(); - return 0; - } - - call_stack::context ctx(this); - - return do_one(true, ec); - } + ASIO_DECL size_t run_one(asio::error_code& ec); // Poll for operations without blocking. - size_t poll(asio::error_code& ec) - { - if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) - { - stop(); - ec = asio::error_code(); - return 0; - } - - call_stack::context ctx(this); - - size_t n = 0; - while (do_one(false, ec)) - if (n != (std::numeric_limits::max)()) - ++n; - return n; - } + ASIO_DECL size_t poll(asio::error_code& ec); // Poll for one operation without blocking. - size_t poll_one(asio::error_code& ec) - { - if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0) - { - stop(); - ec = asio::error_code(); - return 0; - } - - call_stack::context ctx(this); - - return do_one(false, ec); - } + ASIO_DECL size_t poll_one(asio::error_code& ec); // Stop the event processing loop. - void stop() - { - if (::InterlockedExchange(&stopped_, 1) == 0) - { - if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) - { - DWORD last_error = ::GetLastError(); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "pqcs"); - boost::throw_exception(e); - } - } - } + ASIO_DECL void stop(); // Reset in preparation for a subsequent run invocation. void reset() @@ -244,34 +96,15 @@ public: // Request invocation of the given handler. template - void dispatch(Handler handler) - { - if (call_stack::contains(this)) - { - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - else - post(handler); - } + void dispatch(Handler handler); // Request invocation of the given handler and return immediately. template - void post(Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef completion_handler value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - post_immediate_completion(ptr.get()); - ptr.release(); - } + void post(Handler handler); // Request invocation of the given operation and return immediately. Assumes // that work_started() has not yet been called for the operation. - void post_immediate_completion(operation* op) + void post_immediate_completion(win_iocp_operation* op) { work_started(); post_deferred_completion(op); @@ -279,170 +112,50 @@ public: // Request invocation of the given operation and return immediately. Assumes // that work_started() was previously called for the operation. - void post_deferred_completion(operation* op) - { - // Flag the operation as ready. - op->ready_ = 1; - - // Enqueue the operation on the I/O completion port. - if (!::PostQueuedCompletionStatus(iocp_.handle, - 0, overlapped_contains_result, op)) - { - // Out of resources. Put on completed queue instead. - asio::detail::mutex::scoped_lock lock(timer_mutex_); - completed_ops_.push(op); - } - } + ASIO_DECL void post_deferred_completion(win_iocp_operation* op); // Request invocation of the given operation and return immediately. Assumes // that work_started() was previously called for the operations. - void post_deferred_completions(op_queue& ops) - { - while (operation* op = ops.front()) - { - ops.pop(); - - // Flag the operation as ready. - op->ready_ = 1; - - // Enqueue the operation on the I/O completion port. - if (!::PostQueuedCompletionStatus(iocp_.handle, - 0, overlapped_contains_result, op)) - { - // Out of resources. Put on completed queue instead. - asio::detail::mutex::scoped_lock lock(timer_mutex_); - completed_ops_.push(op); - completed_ops_.push(ops); - } - } - } + ASIO_DECL void post_deferred_completions( + op_queue& ops); // Called after starting an overlapped I/O operation that did not complete // immediately. The caller must have already called work_started() prior to // starting the operation. - void on_pending(operation* op) - { - if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) - { - // Enqueue the operation on the I/O completion port. - if (!::PostQueuedCompletionStatus(iocp_.handle, - 0, overlapped_contains_result, op)) - { - // Out of resources. Put on completed queue instead. - asio::detail::mutex::scoped_lock lock(timer_mutex_); - completed_ops_.push(op); - } - } - } + ASIO_DECL void on_pending(win_iocp_operation* op); // Called after starting an overlapped I/O operation that completed // immediately. The caller must have already called work_started() prior to // starting the operation. - void on_completion(operation* op, - DWORD last_error = 0, DWORD bytes_transferred = 0) - { - // Flag that the operation is ready for invocation. - op->ready_ = 1; - - // Store results in the OVERLAPPED structure. - op->Internal = asio::error::get_system_category(); - op->Offset = last_error; - op->OffsetHigh = bytes_transferred; - - // Enqueue the operation on the I/O completion port. - if (!::PostQueuedCompletionStatus(iocp_.handle, - 0, overlapped_contains_result, op)) - { - // Out of resources. Put on completed queue instead. - asio::detail::mutex::scoped_lock lock(timer_mutex_); - completed_ops_.push(op); - } - } + ASIO_DECL void on_completion(win_iocp_operation* op, + DWORD last_error = 0, DWORD bytes_transferred = 0); // Called after starting an overlapped I/O operation that completed // immediately. The caller must have already called work_started() prior to // starting the operation. - void on_completion(operation* op, - const asio::error_code& ec, DWORD bytes_transferred = 0) - { - // Flag that the operation is ready for invocation. - op->ready_ = 1; - - // Store results in the OVERLAPPED structure. - op->Internal = ec.category(); - op->Offset = ec.value(); - op->OffsetHigh = bytes_transferred; - - // Enqueue the operation on the I/O completion port. - if (!::PostQueuedCompletionStatus(iocp_.handle, - 0, overlapped_contains_result, op)) - { - // Out of resources. Put on completed queue instead. - asio::detail::mutex::scoped_lock lock(timer_mutex_); - completed_ops_.push(op); - } - } + ASIO_DECL void on_completion(win_iocp_operation* op, + const asio::error_code& ec, DWORD bytes_transferred = 0); // Add a new timer queue to the service. template - void add_timer_queue(timer_queue& timer_queue) - { - asio::detail::mutex::scoped_lock lock(timer_mutex_); - timer_queues_.insert(&timer_queue); - } + void add_timer_queue(timer_queue& timer_queue); // Remove a timer queue from the service. template - void remove_timer_queue(timer_queue& timer_queue) - { - asio::detail::mutex::scoped_lock lock(timer_mutex_); - timer_queues_.erase(&timer_queue); - } + void remove_timer_queue(timer_queue& timer_queue); // Schedule a new operation in the given timer queue to expire at the // specified absolute time. template - void schedule_timer(timer_queue& timer_queue, - const typename Time_Traits::time_type& time, timer_op* op, void* token) - { - // If the service has been shut down we silently discard the timer. - if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) - return; - - asio::detail::mutex::scoped_lock lock(timer_mutex_); - bool interrupt = timer_queue.enqueue_timer(time, op, token); - work_started(); - if (interrupt && !timer_interrupt_issued_) - { - timer_interrupt_issued_ = true; - lock.unlock(); - ::PostQueuedCompletionStatus(iocp_.handle, - 0, steal_timer_dispatching, 0); - } - } + void schedule_timer(timer_queue& queue, + const typename Time_Traits::time_type& time, + typename timer_queue::per_timer_data& timer, timer_op* op); // Cancel the timer associated with the given token. Returns the number of // handlers that have been posted or dispatched. template - std::size_t cancel_timer(timer_queue& timer_queue, void* token) - { - // If the service has been shut down we silently ignore the cancellation. - if (::InterlockedExchangeAdd(&shutdown_, 0) != 0) - return 0; - - asio::detail::mutex::scoped_lock lock(timer_mutex_); - op_queue ops; - std::size_t n = timer_queue.cancel_timer(token, ops); - post_deferred_completions(ops); - if (n > 0 && !timer_interrupt_issued_) - { - timer_interrupt_issued_ = true; - lock.unlock(); - ::PostQueuedCompletionStatus(iocp_.handle, - 0, steal_timer_dispatching, 0); - } - return n; - } + std::size_t cancel_timer(timer_queue& queue, + typename timer_queue::per_timer_data& timer); private: #if defined(WINVER) && (WINVER < 0x0500) @@ -456,181 +169,30 @@ private: // Dequeues at most one operation from the I/O completion port, and then // executes it. Returns the number of operations that were dequeued (i.e. // either 0 or 1). - size_t do_one(bool block, asio::error_code& ec) - { - long this_thread_id = static_cast(::GetCurrentThreadId()); + ASIO_DECL size_t do_one(bool block, asio::error_code& ec); - for (;;) - { - // Try to acquire responsibility for dispatching timers. - bool dispatching_timers = (::InterlockedCompareExchange( - &timer_thread_, this_thread_id, 0) == 0); + // Helper function to add a new timer queue. + ASIO_DECL void do_add_timer_queue(timer_queue_base& queue); - // Calculate timeout for GetQueuedCompletionStatus call. - DWORD timeout = max_timeout; - if (dispatching_timers) - { - asio::detail::mutex::scoped_lock lock(timer_mutex_); - timer_interrupt_issued_ = false; - timeout = get_timeout(); - } + // Helper function to remove a timer queue. + ASIO_DECL void do_remove_timer_queue(timer_queue_base& queue); - // Get the next operation from the queue. - DWORD bytes_transferred = 0; - dword_ptr_t completion_key = 0; - LPOVERLAPPED overlapped = 0; - ::SetLastError(0); - BOOL ok = ::GetQueuedCompletionStatus(iocp_.handle, &bytes_transferred, - &completion_key, &overlapped, block ? timeout : 0); - DWORD last_error = ::GetLastError(); - - // Dispatch any pending timers. - if (dispatching_timers) - { - asio::detail::mutex::scoped_lock lock(timer_mutex_); - op_queue ops; - ops.push(completed_ops_); - timer_queues_.get_ready_timers(ops); - post_deferred_completions(ops); - } - - if (!ok && overlapped == 0) - { - if (block && last_error == WAIT_TIMEOUT) - { - // Relinquish responsibility for dispatching timers. - if (dispatching_timers) - { - ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id); - } - - continue; - } - - // Transfer responsibility for dispatching timers to another thread. - if (dispatching_timers && ::InterlockedCompareExchange( - &timer_thread_, 0, this_thread_id) == this_thread_id) - { - ::PostQueuedCompletionStatus(iocp_.handle, - 0, transfer_timer_dispatching, 0); - } - - ec = asio::error_code(); - return 0; - } - else if (overlapped) - { - operation* op = static_cast(overlapped); - asio::error_code result_ec(last_error, - asio::error::get_system_category()); - - // Transfer responsibility for dispatching timers to another thread. - if (dispatching_timers && ::InterlockedCompareExchange( - &timer_thread_, 0, this_thread_id) == this_thread_id) - { - ::PostQueuedCompletionStatus(iocp_.handle, - 0, transfer_timer_dispatching, 0); - } - - // We may have been passed the last_error and bytes_transferred in the - // OVERLAPPED structure itself. - if (completion_key == overlapped_contains_result) - { - result_ec = asio::error_code(static_cast(op->Offset), - static_cast(op->Internal)); - bytes_transferred = op->OffsetHigh; - } - - // Otherwise ensure any result has been saved into the OVERLAPPED - // structure. - else - { - op->Internal = result_ec.category(); - op->Offset = result_ec.value(); - op->OffsetHigh = bytes_transferred; - } - - // Dispatch the operation only if ready. The operation may not be ready - // if the initiating function (e.g. a call to WSARecv) has not yet - // returned. This is because the initiating function still wants access - // to the operation's OVERLAPPED structure. - if (::InterlockedCompareExchange(&op->ready_, 1, 0) == 1) - { - // Ensure the count of outstanding work is decremented on block exit. - work_finished_on_block_exit on_exit = { this }; - (void)on_exit; - - op->complete(*this, result_ec, bytes_transferred); - ec = asio::error_code(); - return 1; - } - } - else if (completion_key == transfer_timer_dispatching) - { - // Woken up to try to acquire responsibility for dispatching timers. - ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id); - } - else if (completion_key == steal_timer_dispatching) - { - // Woken up to steal responsibility for dispatching timers. - ::InterlockedExchange(&timer_thread_, 0); - } - else - { - // Relinquish responsibility for dispatching timers. If the io_service - // is not being stopped then the thread will get an opportunity to - // reacquire timer responsibility on the next loop iteration. - if (dispatching_timers) - { - ::InterlockedCompareExchange(&timer_thread_, 0, this_thread_id); - } - - // The stopped_ flag is always checked to ensure that any leftover - // interrupts from a previous run invocation are ignored. - if (::InterlockedExchangeAdd(&stopped_, 0) != 0) - { - // Wake up next thread that is blocked on GetQueuedCompletionStatus. - if (!::PostQueuedCompletionStatus(iocp_.handle, 0, 0, 0)) - { - last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return 0; - } - - ec = asio::error_code(); - return 0; - } - } - } - } - - // Get the timeout value for the GetQueuedCompletionStatus call. The timeout - // value is returned as a number of milliseconds. We will wait no longer than - // 1000 milliseconds. - DWORD get_timeout() - { - return timer_queues_.wait_duration_msec(max_timeout); - } + // Called to recalculate and update the timeout. + ASIO_DECL void update_timeout(); // Helper class to call work_finished() on block exit. - struct work_finished_on_block_exit - { - ~work_finished_on_block_exit() - { - io_service_->work_finished(); - } + struct work_finished_on_block_exit; - win_iocp_io_service* io_service_; + // Helper class for managing a HANDLE. + struct auto_handle + { + HANDLE handle; + auto_handle() : handle(0) {} + ~auto_handle() { if (handle) ::CloseHandle(handle); } }; // The IO completion port used for queueing operations. - struct iocp_holder - { - HANDLE handle; - iocp_holder() : handle(0) {} - ~iocp_holder() { if (handle) ::CloseHandle(handle); } - } iocp_; + auto_handle iocp_; // The count of unfinished work. long outstanding_work_; @@ -643,44 +205,61 @@ private: enum { - // Maximum GetQueuedCompletionStatus timeout, in milliseconds. - max_timeout = 500, + // Timeout to use with GetQueuedCompletionStatus. Some versions of windows + // have a "bug" where a call to GetQueuedCompletionStatus can appear stuck + // even though there are events waiting on the queue. Using a timeout helps + // to work around the issue. + gqcs_timeout = 500, - // Completion key value to indicate that responsibility for dispatching - // timers is being cooperatively transferred from one thread to another. - transfer_timer_dispatching = 1, + // Maximum waitable timer timeout, in milliseconds. + max_timeout_msec = 5 * 60 * 1000, - // Completion key value to indicate that responsibility for dispatching - // timers should be stolen from another thread. - steal_timer_dispatching = 2, + // Maximum waitable timer timeout, in microseconds. + max_timeout_usec = max_timeout_msec * 1000, + + // Completion key value used to wake up a thread to dispatch timers or + // completed operations. + wake_for_dispatch = 1, // Completion key value to indicate that an operation has posted with the // original last_error and bytes_transferred values stored in the fields of // the OVERLAPPED structure. - overlapped_contains_result = 3 + overlapped_contains_result = 2 }; - // The thread that's currently in charge of dispatching timers. - long timer_thread_; + // Function object for processing timeouts in a background thread. + struct timer_thread_function; + friend struct timer_thread_function; - // Mutex for protecting access to the timer queues. - mutex timer_mutex_; + // Background thread used for processing timeouts. + boost::scoped_ptr timer_thread_; - // Whether a thread has been interrupted to process a new timeout. - bool timer_interrupt_issued_; + // A waitable timer object used for waiting for timeouts. + auto_handle waitable_timer_; + + // Non-zero if timers or completed operations need to be dispatched. + long dispatch_required_; + + // Mutex for protecting access to the timer queues and completed operations. + mutex dispatch_mutex_; // The timer queues. timer_queue_set timer_queues_; // The operations that are ready to dispatch. - op_queue completed_ops_; + op_queue completed_ops_; }; } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_IOCP) - #include "asio/detail/pop_options.hpp" +#include "asio/detail/impl/win_iocp_io_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_io_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + #endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_HPP diff --git a/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp b/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp index 29d2a05481..9f03743d2b 100644 --- a/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp +++ b/ext/asio/asio/detail/win_iocp_io_service_fwd.hpp @@ -1,8 +1,8 @@ // -// win_iocp_io_service_fwd.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_iocp_io_service_fwd.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,22 +15,9 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/detail/socket_types.hpp" - -// This service is only supported on Win32 (NT4 and later). -#if !defined(ASIO_DISABLE_IOCP) -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) -#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) -#if !defined(UNDER_CE) - -// Define this to indicate that IOCP is supported on the target platform. -#define ASIO_HAS_IOCP 1 +#if defined(ASIO_HAS_IOCP) namespace asio { namespace detail { @@ -41,11 +28,6 @@ class win_iocp_overlapped_ptr; } // namespace detail } // namespace asio -#endif // !defined(UNDER_CE) -#endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400) -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) -#endif // !defined(ASIO_DISABLE_IOCP) - -#include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_IOCP) #endif // ASIO_DETAIL_WIN_IOCP_IO_SERVICE_FWD_HPP diff --git a/ext/asio/asio/detail/win_iocp_null_buffers_op.hpp b/ext/asio/asio/detail/win_iocp_null_buffers_op.hpp new file mode 100644 index 0000000000..e39fff7c75 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_null_buffers_op.hpp @@ -0,0 +1,112 @@ +// +// detail/win_iocp_null_buffers_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_null_buffers_op : public reactor_op +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_null_buffers_op); + + win_iocp_null_buffers_op(socket_ops::weak_cancel_token_type cancel_token, + Handler handler) + : reactor_op(&win_iocp_null_buffers_op::do_perform, + &win_iocp_null_buffers_op::do_complete), + cancel_token_(cancel_token), + handler_(handler) + { + } + + static bool do_perform(reactor_op*) + { + return true; + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_null_buffers_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // The reactor may have stored a result in the operation object. + if (o->ec_) + ec = o->ec_; + + // Map non-portable errors to their portable counterparts. + if (ec.value() == ERROR_NETNAME_DELETED) + { + if (o->cancel_token_.expired()) + ec = asio::error::operation_aborted; + else + ec = asio::error::connection_reset; + } + else if (ec.value() == ERROR_PORT_UNREACHABLE) + { + ec = asio::error::connection_refused; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_NULL_BUFFERS_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_operation.hpp b/ext/asio/asio/detail/win_iocp_operation.hpp index ac8106255a..649edd1a88 100644 --- a/ext/asio/asio/detail/win_iocp_operation.hpp +++ b/ext/asio/asio/detail/win_iocp_operation.hpp @@ -1,8 +1,8 @@ // -// win_iocp_operation.hpp -// ~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_iocp_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) -#include "asio/error_code.hpp" #include "asio/detail/op_queue.hpp" +#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -82,8 +83,8 @@ private: } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_IOCP) - #include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_IOCP) + #endif // ASIO_DETAIL_WIN_IOCP_OPERATION_HPP diff --git a/ext/asio/asio/detail/win_iocp_overlapped_op.hpp b/ext/asio/asio/detail/win_iocp_overlapped_op.hpp new file mode 100644 index 0000000000..f2b7ddf624 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_overlapped_op.hpp @@ -0,0 +1,84 @@ +// +// detail/win_iocp_overlapped_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include "asio/error.hpp" +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_overlapped_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_overlapped_op); + + win_iocp_overlapped_op(Handler handler) + : operation(&win_iocp_overlapped_op::do_complete), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_overlapped_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp b/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp index 47a3f70cf1..919affe414 100644 --- a/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp +++ b/ext/asio/asio/detail/win_iocp_overlapped_ptr.hpp @@ -1,8 +1,8 @@ // -// win_iocp_overlapped_ptr.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_iocp_overlapped_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) -#include "asio/detail/fenced_block.hpp" +#include +#include "asio/io_service.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/noncopyable.hpp" +#include "asio/detail/win_iocp_overlapped_op.hpp" #include "asio/detail/win_iocp_io_service.hpp" -#include "asio/detail/win_iocp_operation.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { @@ -74,13 +76,15 @@ public: template void reset(asio::io_service& io_service, Handler handler) { - typedef overlapped_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef win_iocp_overlapped_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(handler); io_service.impl_.work_started(); reset(); - ptr_ = ptr.release(); + ptr_ = p.p; + p.v = p.p = 0; iocp_service_ = &io_service.impl_; } @@ -122,44 +126,6 @@ public: } private: - template - struct overlapped_op : public win_iocp_operation - { - overlapped_op(Handler handler) - : win_iocp_operation(&overlapped_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - overlapped_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, ec, bytes_transferred); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - win_iocp_operation* ptr_; win_iocp_io_service* iocp_service_; }; @@ -167,8 +133,8 @@ private: } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_IOCP) - #include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_IOCP) + #endif // ASIO_DETAIL_WIN_IOCP_OVERLAPPED_PTR_HPP diff --git a/ext/asio/asio/detail/win_iocp_serial_port_service.hpp b/ext/asio/asio/detail/win_iocp_serial_port_service.hpp index ed5f75e91c..bf3a692d55 100644 --- a/ext/asio/asio/detail/win_iocp_serial_port_service.hpp +++ b/ext/asio/asio/detail/win_iocp_serial_port_service.hpp @@ -1,8 +1,8 @@ // -// win_iocp_serial_port_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_iocp_serial_port_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,21 +16,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) -#include "asio/detail/push_options.hpp" -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/detail/win_iocp_io_service_fwd.hpp" - -#if defined(ASIO_HAS_IOCP) - #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/detail/win_iocp_handle_service.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -38,133 +34,56 @@ namespace detail { class win_iocp_serial_port_service { public: - // The native type of a stream handle. + // The native type of a serial port. typedef win_iocp_handle_service::native_type native_type; - // The implementation type of the stream handle. + // The implementation type of the serial port. typedef win_iocp_handle_service::implementation_type implementation_type; - win_iocp_serial_port_service(asio::io_service& io_service) - : handle_service_(io_service) - { - } + // Constructor. + ASIO_DECL win_iocp_serial_port_service( + asio::io_service& io_service); // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - } + ASIO_DECL void shutdown_service(); - // Construct a new handle implementation. + // Construct a new serial port implementation. void construct(implementation_type& impl) { handle_service_.construct(impl); } - // Destroy a handle implementation. + // Destroy a serial port implementation. void destroy(implementation_type& impl) { handle_service_.destroy(impl); } // Open the serial port using the specified device name. - asio::error_code open(implementation_type& impl, - const std::string& device, asio::error_code& ec) - { - if (is_open(impl)) - { - ec = asio::error::already_open; - return ec; - } + ASIO_DECL asio::error_code open(implementation_type& impl, + const std::string& device, asio::error_code& ec); - // For convenience, add a leading \\.\ sequence if not already present. - std::string name = (device[0] == '\\') ? device : "\\\\.\\" + device; - - // Open a handle to the serial port. - ::HANDLE handle = ::CreateFileA(name.c_str(), - GENERIC_READ | GENERIC_WRITE, 0, 0, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); - if (handle == INVALID_HANDLE_VALUE) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - // Determine the initial serial port parameters. - using namespace std; // For memcpy. - ::DCB dcb; - memset(&dcb, 0, sizeof(DCB)); - dcb.DCBlength = sizeof(DCB); - if (!::GetCommState(handle, &dcb)) - { - DWORD last_error = ::GetLastError(); - ::CloseHandle(handle); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - // Set some default serial port parameters. This implementation does not - // support changing these, so they might as well be in a known state. - dcb.fBinary = TRUE; // Win32 only supports binary mode. - dcb.fDsrSensitivity = FALSE; - dcb.fNull = FALSE; // Do not ignore NULL characters. - dcb.fAbortOnError = FALSE; // Ignore serial framing errors. - if (!::SetCommState(handle, &dcb)) - { - DWORD last_error = ::GetLastError(); - ::CloseHandle(handle); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - // Set up timeouts so that the serial port will behave similarly to a - // network socket. Reads wait for at least one byte, then return with - // whatever they have. Writes return once everything is out the door. - ::COMMTIMEOUTS timeouts; - timeouts.ReadIntervalTimeout = 1; - timeouts.ReadTotalTimeoutMultiplier = 0; - timeouts.ReadTotalTimeoutConstant = 0; - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; - if (!::SetCommTimeouts(handle, &timeouts)) - { - DWORD last_error = ::GetLastError(); - ::CloseHandle(handle); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - // We're done. Take ownership of the serial port handle. - if (handle_service_.assign(impl, handle, ec)) - ::CloseHandle(handle); - return ec; - } - - // Assign a native handle to a handle implementation. + // Assign a native handle to a serial port implementation. asio::error_code assign(implementation_type& impl, const native_type& native_handle, asio::error_code& ec) { return handle_service_.assign(impl, native_handle, ec); } - // Determine whether the handle is open. + // Determine whether the serial port is open. bool is_open(const implementation_type& impl) const { return handle_service_.is_open(impl); } - // Destroy a handle implementation. + // Destroy a serial port implementation. asio::error_code close(implementation_type& impl, asio::error_code& ec) { return handle_service_.close(impl, ec); } - // Get the native handle representation. + // Get the native serial port representation. native_type native(implementation_type& impl) { return handle_service_.native(impl); @@ -182,32 +101,9 @@ public: asio::error_code set_option(implementation_type& impl, const SettableSerialPortOption& option, asio::error_code& ec) { - using namespace std; // For memcpy. - - ::DCB dcb; - memset(&dcb, 0, sizeof(DCB)); - dcb.DCBlength = sizeof(DCB); - if (!::GetCommState(handle_service_.native(impl), &dcb)) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - if (option.store(dcb, ec)) - return ec; - - if (!::SetCommState(handle_service_.native(impl), &dcb)) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - ec = asio::error_code(); - return ec; + return do_set_option(impl, + &win_iocp_serial_port_service::store_option, + &option, ec); } // Get an option from the serial port. @@ -215,20 +111,9 @@ public: asio::error_code get_option(const implementation_type& impl, GettableSerialPortOption& option, asio::error_code& ec) const { - using namespace std; // For memcpy. - - ::DCB dcb; - memset(&dcb, 0, sizeof(DCB)); - dcb.DCBlength = sizeof(DCB); - if (!::GetCommState(handle_service_.native(impl), &dcb)) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return ec; - } - - return option.load(dcb, ec); + return do_get_option(impl, + &win_iocp_serial_port_service::load_option, + &option, ec); } // Send a break sequence to the serial port. @@ -274,6 +159,41 @@ public: } private: + // Function pointer type for storing a serial port option. + typedef asio::error_code (*store_function_type)( + const void*, ::DCB&, asio::error_code&); + + // Helper function template to store a serial port option. + template + static asio::error_code store_option(const void* option, + ::DCB& storage, asio::error_code& ec) + { + return static_cast(option)->store( + storage, ec); + } + + // Helper function to set a serial port option. + ASIO_DECL asio::error_code do_set_option( + implementation_type& impl, store_function_type store, + const void* option, asio::error_code& ec); + + // Function pointer type for loading a serial port option. + typedef asio::error_code (*load_function_type)( + void*, const ::DCB&, asio::error_code&); + + // Helper function template to load a serial port option. + template + static asio::error_code load_option(void* option, + const ::DCB& storage, asio::error_code& ec) + { + return static_cast(option)->load(storage, ec); + } + + // Helper function to get a serial port option. + ASIO_DECL asio::error_code do_get_option( + const implementation_type& impl, load_function_type load, + void* option, asio::error_code& ec) const; + // The implementation used for initiating asynchronous operations. win_iocp_handle_service handle_service_; }; @@ -281,8 +201,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_IOCP) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_serial_port_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) && defined(ASIO_HAS_SERIAL_PORT) + #endif // ASIO_DETAIL_WIN_IOCP_SERIAL_PORT_SERVICE_HPP diff --git a/ext/asio/asio/detail/win_iocp_socket_accept_op.hpp b/ext/asio/asio/detail/win_iocp_socket_accept_op.hpp new file mode 100644 index 0000000000..a71c7c0de5 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_socket_accept_op.hpp @@ -0,0 +1,158 @@ +// +// detail/win_iocp_socket_accept_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/win_iocp_socket_service_base.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_accept_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_accept_op); + + win_iocp_socket_accept_op(win_iocp_socket_service_base& socket_service, + socket_type socket, Socket& peer, const Protocol& protocol, + typename Protocol::endpoint* peer_endpoint, + bool enable_connection_aborted, Handler handler) + : operation(&win_iocp_socket_accept_op::do_complete), + socket_service_(socket_service), + socket_(socket), + peer_(peer), + protocol_(protocol), + peer_endpoint_(peer_endpoint), + enable_connection_aborted_(enable_connection_aborted), + handler_(handler) + { + } + + socket_holder& new_socket() + { + return new_socket_; + } + + void* output_buffer() + { + return output_buffer_; + } + + DWORD address_length() + { + return sizeof(sockaddr_storage_type) + 16; + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t /*bytes_transferred*/) + { + // Take ownership of the operation object. + win_iocp_socket_accept_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + + if (owner) + { + typename Protocol::endpoint peer_endpoint; + std::size_t addr_len = peer_endpoint.capacity(); + socket_ops::complete_iocp_accept(o->socket_, + o->output_buffer(), o->address_length(), + peer_endpoint.data(), &addr_len, + o->new_socket_.get(), ec); + + // Restart the accept operation if we got the connection_aborted error + // and the enable_connection_aborted socket option is not set. + if (ec == asio::error::connection_aborted + && !o->enable_connection_aborted_) + { + o->reset(); + o->socket_service_.restart_accept_op(o->socket_, + o->new_socket_, o->protocol_.family(), + o->protocol_.type(), o->protocol_.protocol(), + o->output_buffer(), o->address_length(), o); + p.v = p.p = 0; + return; + } + + // If the socket was successfully accepted, transfer ownership of the + // socket to the peer object. + if (!ec) + { + o->peer_.assign(o->protocol_, + typename Socket::native_type( + o->new_socket_.get(), peer_endpoint), ec); + if (!ec) + o->new_socket_.release(); + } + + // Pass endpoint back to caller. + if (o->peer_endpoint_) + *o->peer_endpoint_ = peer_endpoint; + } + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder1 + handler(o->handler_, ec); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + win_iocp_socket_service_base& socket_service_; + socket_type socket_; + socket_holder new_socket_; + Socket& peer_; + Protocol protocol_; + typename Protocol::endpoint* peer_endpoint_; + unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; + bool enable_connection_aborted_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_ACCEPT_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_socket_recv_op.hpp b/ext/asio/asio/detail/win_iocp_socket_recv_op.hpp new file mode 100644 index 0000000000..3ab4fcb4d5 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_socket_recv_op.hpp @@ -0,0 +1,108 @@ +// +// detail/win_iocp_socket_recv_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recv_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recv_op); + + win_iocp_socket_recv_op(socket_ops::state_type state, + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler handler) + : operation(&win_iocp_socket_recv_op::do_complete), + state_(state), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_socket_recv_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recv(o->state_, o->cancel_token_, + buffer_sequence_adapter::all_empty(o->buffers_), + ec, bytes_transferred); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + socket_ops::state_type state_; + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECV_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp b/ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp new file mode 100644 index 0000000000..d1e2ecec58 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_socket_recvfrom_op.hpp @@ -0,0 +1,116 @@ +// +// detail/win_iocp_socket_recvfrom_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_recvfrom_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_recvfrom_op); + + win_iocp_socket_recvfrom_op(Endpoint& endpoint, + socket_ops::weak_cancel_token_type cancel_token, + const MutableBufferSequence& buffers, Handler handler) + : operation(&win_iocp_socket_recvfrom_op::do_complete), + endpoint_(endpoint), + endpoint_size_(static_cast(endpoint.capacity())), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + int& endpoint_size() + { + return endpoint_size_; + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_socket_recvfrom_op* o( + static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_recvfrom(o->cancel_token_, ec); + + // Record the size of the endpoint returned by the operation. + o->endpoint_.resize(o->endpoint_size_); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + Endpoint& endpoint_; + int endpoint_size_; + socket_ops::weak_cancel_token_type cancel_token_; + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_RECVFROM_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_socket_send_op.hpp b/ext/asio/asio/detail/win_iocp_socket_send_op.hpp new file mode 100644 index 0000000000..56f3f2d304 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_socket_send_op.hpp @@ -0,0 +1,102 @@ +// +// detail/win_iocp_socket_send_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +template +class win_iocp_socket_send_op : public operation +{ +public: + ASIO_DEFINE_HANDLER_PTR(win_iocp_socket_send_op); + + win_iocp_socket_send_op(socket_ops::weak_cancel_token_type cancel_token, + const ConstBufferSequence& buffers, Handler handler) + : operation(&win_iocp_socket_send_op::do_complete), + cancel_token_(cancel_token), + buffers_(buffers), + handler_(handler) + { + } + + static void do_complete(io_service_impl* owner, operation* base, + asio::error_code ec, std::size_t bytes_transferred) + { + // Take ownership of the operation object. + win_iocp_socket_send_op* o(static_cast(base)); + ptr p = { boost::addressof(o->handler_), o, o }; + +#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) + // Check whether buffers are still valid. + if (owner) + { + buffer_sequence_adapter::validate(o->buffers_); + } +#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) + + socket_ops::complete_iocp_send(o->cancel_token_, ec); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + detail::binder2 + handler(o->handler_, ec, bytes_transferred); + p.h = boost::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (owner) + { + asio::detail::fenced_block b; + asio_handler_invoke_helpers::invoke(handler, handler.handler_); + } + } + +private: + socket_ops::weak_cancel_token_type cancel_token_; + ConstBufferSequence buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SEND_OP_HPP diff --git a/ext/asio/asio/detail/win_iocp_socket_service.hpp b/ext/asio/asio/detail/win_iocp_socket_service.hpp index cb1d2037de..b476f8bb2b 100644 --- a/ext/asio/asio/detail/win_iocp_socket_service.hpp +++ b/ext/asio/asio/detail/win_iocp_socket_service.hpp @@ -1,8 +1,8 @@ // -// win_iocp_socket_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// detail/win_iocp_socket_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,19 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/win_iocp_io_service_fwd.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_IOCP) -#include "asio/detail/push_options.hpp" #include -#include -#include -#include -#include "asio/detail/pop_options.hpp" - +#include #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/socket_base.hpp" @@ -37,20 +30,27 @@ #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" #include "asio/detail/mutex.hpp" -#include "asio/detail/null_buffers_op.hpp" #include "asio/detail/operation.hpp" +#include "asio/detail/reactive_socket_connect_op.hpp" #include "asio/detail/reactor.hpp" #include "asio/detail/reactor_op.hpp" #include "asio/detail/socket_holder.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/win_iocp_io_service.hpp" +#include "asio/detail/win_iocp_null_buffers_op.hpp" +#include "asio/detail/win_iocp_socket_accept_op.hpp" +#include "asio/detail/win_iocp_socket_recvfrom_op.hpp" +#include "asio/detail/win_iocp_socket_send_op.hpp" +#include "asio/detail/win_iocp_socket_service_base.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace detail { template -class win_iocp_socket_service +class win_iocp_socket_service : public win_iocp_socket_service_base { public: // The protocol type. @@ -59,10 +59,6 @@ public: // The endpoint type. typedef typename Protocol::endpoint endpoint_type; - struct noop_deleter { void operator()(void*) {} }; - typedef boost::shared_ptr shared_cancel_token_type; - typedef boost::weak_ptr weak_cancel_token_type; - // The native type of a socket. class native_type { @@ -92,11 +88,6 @@ public: return socket_; } - HANDLE as_handle() const - { - return reinterpret_cast(socket_); - } - bool have_remote_endpoint() const { return have_remote_endpoint_; @@ -114,148 +105,44 @@ public: }; // The implementation type of the socket. - class implementation_type + struct implementation_type : + win_iocp_socket_service_base::base_implementation_type { - public: // Default constructor. implementation_type() - : socket_(invalid_socket), - flags_(0), - cancel_token_(), - protocol_(endpoint_type().protocol()), - next_(0), - prev_(0) + : protocol_(endpoint_type().protocol()), + have_remote_endpoint_(false), + remote_endpoint_() { } - private: - // Only this service will have access to the internal values. - friend class win_iocp_socket_service; - - // The native socket representation. - native_type socket_; - - enum - { - enable_connection_aborted = 1, // User wants connection_aborted errors. - close_might_block = 2, // User set linger option for blocking close. - user_set_non_blocking = 4 // The user wants a non-blocking socket. - }; - - // Flags indicating the current state of the socket. - unsigned char flags_; - - // We use a shared pointer as a cancellation token here to work around the - // broken Windows support for cancellation. MSDN says that when you call - // closesocket any outstanding WSARecv or WSASend operations will complete - // with the error ERROR_OPERATION_ABORTED. In practice they complete with - // ERROR_NETNAME_DELETED, which means you can't tell the difference between - // a local cancellation and the socket being hard-closed by the peer. - shared_cancel_token_type cancel_token_; - // The protocol associated with the socket. protocol_type protocol_; - // Per-descriptor data used by the reactor. - reactor::per_descriptor_data reactor_data_; + // Whether we have a cached remote endpoint. + bool have_remote_endpoint_; -#if defined(ASIO_ENABLE_CANCELIO) - // The ID of the thread from which it is safe to cancel asynchronous - // operations. 0 means no asynchronous operations have been started yet. - // ~0 means asynchronous operations have been started from more than one - // thread, and cancellation is not supported for the socket. - DWORD safe_cancellation_thread_id_; -#endif // defined(ASIO_ENABLE_CANCELIO) - - // Pointers to adjacent socket implementations in linked list. - implementation_type* next_; - implementation_type* prev_; + // A cached remote endpoint. + endpoint_type remote_endpoint_; }; // Constructor. win_iocp_socket_service(asio::io_service& io_service) - : io_service_(io_service), - iocp_service_(use_service(io_service)), - reactor_(0), - mutex_(), - impl_list_(0) + : win_iocp_socket_service_base(io_service) { } - // Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - // Close all implementations, causing all operations to complete. - asio::detail::mutex::scoped_lock lock(mutex_); - implementation_type* impl = impl_list_; - while (impl) - { - asio::error_code ignored_ec; - close_for_destruction(*impl); - impl = impl->next_; - } - } - - // Construct a new socket implementation. - void construct(implementation_type& impl) - { - impl.socket_ = invalid_socket; - impl.flags_ = 0; - impl.cancel_token_.reset(); -#if defined(ASIO_ENABLE_CANCELIO) - impl.safe_cancellation_thread_id_ = 0; -#endif // defined(ASIO_ENABLE_CANCELIO) - - // Insert implementation into linked list of all implementations. - asio::detail::mutex::scoped_lock lock(mutex_); - impl.next_ = impl_list_; - impl.prev_ = 0; - if (impl_list_) - impl_list_->prev_ = &impl; - impl_list_ = &impl; - } - - // Destroy a socket implementation. - void destroy(implementation_type& impl) - { - close_for_destruction(impl); - - // Remove implementation from linked list of all implementations. - asio::detail::mutex::scoped_lock lock(mutex_); - if (impl_list_ == &impl) - impl_list_ = impl.next_; - if (impl.prev_) - impl.prev_->next_ = impl.next_; - if (impl.next_) - impl.next_->prev_= impl.prev_; - impl.next_ = 0; - impl.prev_ = 0; - } - // Open a new socket implementation. asio::error_code open(implementation_type& impl, const protocol_type& protocol, asio::error_code& ec) { - if (is_open(impl)) + if (!do_open(impl, protocol.family(), + protocol.type(), protocol.protocol(), ec)) { - ec = asio::error::already_open; - return ec; + impl.protocol_ = protocol; + impl.have_remote_endpoint_ = false; + impl.remote_endpoint_ = endpoint_type(); } - - socket_holder sock(socket_ops::socket(protocol.family(), protocol.type(), - protocol.protocol(), ec)); - if (sock.get() == invalid_socket) - return ec; - - HANDLE sock_as_handle = reinterpret_cast(sock.get()); - if (iocp_service_.register_handle(sock_as_handle, ec)) - return ec; - - impl.socket_ = sock.release(); - impl.flags_ = 0; - impl.cancel_token_.reset(static_cast(0), noop_deleter()); - impl.protocol_ = protocol; - ec = asio::error_code(); return ec; } @@ -264,247 +151,40 @@ public: const protocol_type& protocol, const native_type& native_socket, asio::error_code& ec) { - if (is_open(impl)) + if (!do_assign(impl, protocol.type(), native_socket, ec)) { - ec = asio::error::already_open; - return ec; + impl.protocol_ = protocol; + impl.have_remote_endpoint_ = native_socket.have_remote_endpoint(); + impl.remote_endpoint_ = native_socket.remote_endpoint(); } - - if (iocp_service_.register_handle(native_socket.as_handle(), ec)) - return ec; - - impl.socket_ = native_socket; - impl.flags_ = 0; - impl.cancel_token_.reset(static_cast(0), noop_deleter()); - impl.protocol_ = protocol; - ec = asio::error_code(); - return ec; - } - - // Determine whether the socket is open. - bool is_open(const implementation_type& impl) const - { - return impl.socket_ != invalid_socket; - } - - // Destroy a socket implementation. - asio::error_code close(implementation_type& impl, - asio::error_code& ec) - { - if (is_open(impl)) - { - // Check if the reactor was created, in which case we need to close the - // socket on the reactor as well to cancel any operations that might be - // running there. - reactor* r = static_cast( - interlocked_compare_exchange_pointer( - reinterpret_cast(&reactor_), 0, 0)); - if (r) - r->close_descriptor(impl.socket_, impl.reactor_data_); - - if (socket_ops::close(impl.socket_, ec) == socket_error_retval) - return ec; - - impl.socket_ = invalid_socket; - impl.flags_ = 0; - impl.cancel_token_.reset(); -#if defined(ASIO_ENABLE_CANCELIO) - impl.safe_cancellation_thread_id_ = 0; -#endif // defined(ASIO_ENABLE_CANCELIO) - } - - ec = asio::error_code(); return ec; } // Get the native socket representation. native_type native(implementation_type& impl) { - return impl.socket_; - } - - // Cancel all operations associated with the socket. - asio::error_code cancel(implementation_type& impl, - asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - else if (FARPROC cancel_io_ex_ptr = ::GetProcAddress( - ::GetModuleHandleA("KERNEL32"), "CancelIoEx")) - { - // The version of Windows supports cancellation from any thread. - typedef BOOL (WINAPI* cancel_io_ex_t)(HANDLE, LPOVERLAPPED); - cancel_io_ex_t cancel_io_ex = (cancel_io_ex_t)cancel_io_ex_ptr; - socket_type sock = impl.socket_; - HANDLE sock_as_handle = reinterpret_cast(sock); - if (!cancel_io_ex(sock_as_handle, 0)) - { - DWORD last_error = ::GetLastError(); - if (last_error == ERROR_NOT_FOUND) - { - // ERROR_NOT_FOUND means that there were no operations to be - // cancelled. We swallow this error to match the behaviour on other - // platforms. - ec = asio::error_code(); - } - else - { - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - } - else - { - ec = asio::error_code(); - } - } -#if defined(ASIO_ENABLE_CANCELIO) - else if (impl.safe_cancellation_thread_id_ == 0) - { - // No operations have been started, so there's nothing to cancel. - ec = asio::error_code(); - } - else if (impl.safe_cancellation_thread_id_ == ::GetCurrentThreadId()) - { - // Asynchronous operations have been started from the current thread only, - // so it is safe to try to cancel them using CancelIo. - socket_type sock = impl.socket_; - HANDLE sock_as_handle = reinterpret_cast(sock); - if (!::CancelIo(sock_as_handle)) - { - DWORD last_error = ::GetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - } - else - { - ec = asio::error_code(); - } - } - else - { - // Asynchronous operations have been started from more than one thread, - // so cancellation is not safe. - ec = asio::error::operation_not_supported; - } -#else // defined(ASIO_ENABLE_CANCELIO) - else - { - // Cancellation is not supported as CancelIo may not be used. - ec = asio::error::operation_not_supported; - } -#endif // defined(ASIO_ENABLE_CANCELIO) - - return ec; - } - - // Determine whether the socket is at the out-of-band data mark. - bool at_mark(const implementation_type& impl, - asio::error_code& ec) const - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return false; - } - - asio::detail::ioctl_arg_type value = 0; - socket_ops::ioctl(impl.socket_, SIOCATMARK, &value, ec); - return ec ? false : value != 0; - } - - // Determine the number of bytes available for reading. - std::size_t available(const implementation_type& impl, - asio::error_code& ec) const - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - asio::detail::ioctl_arg_type value = 0; - socket_ops::ioctl(impl.socket_, FIONREAD, &value, ec); - return ec ? static_cast(0) : static_cast(value); + if (impl.have_remote_endpoint_) + return native_type(impl.socket_, impl.remote_endpoint_); + return native_type(impl.socket_); } // Bind the socket to the specified local endpoint. asio::error_code bind(implementation_type& impl, const endpoint_type& endpoint, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - socket_ops::bind(impl.socket_, endpoint.data(), endpoint.size(), ec); return ec; } - // Place the socket into the state where it will listen for new connections. - asio::error_code listen(implementation_type& impl, int backlog, - asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - socket_ops::listen(impl.socket_, backlog, ec); - return ec; - } - // Set a socket option. template asio::error_code set_option(implementation_type& impl, const Option& option, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - if (option.level(impl.protocol_) == custom_socket_option_level - && option.name(impl.protocol_) == enable_connection_aborted_option) - { - if (option.size(impl.protocol_) != sizeof(int)) - { - ec = asio::error::invalid_argument; - } - else - { - if (*reinterpret_cast(option.data(impl.protocol_))) - impl.flags_ |= implementation_type::enable_connection_aborted; - else - impl.flags_ &= ~implementation_type::enable_connection_aborted; - ec = asio::error_code(); - } - return ec; - } - else - { - if (option.level(impl.protocol_) == SOL_SOCKET - && option.name(impl.protocol_) == SO_LINGER) - { - const ::linger* linger_option = - reinterpret_cast(option.data(impl.protocol_)); - if (linger_option->l_onoff != 0 && linger_option->l_linger != 0) - impl.flags_ |= implementation_type::close_might_block; - else - impl.flags_ &= ~implementation_type::close_might_block; - } - - socket_ops::setsockopt(impl.socket_, - option.level(impl.protocol_), option.name(impl.protocol_), - option.data(impl.protocol_), option.size(impl.protocol_), ec); - return ec; - } + socket_ops::setsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), option.size(impl.protocol_), ec); + return ec; } // Set a socket option. @@ -512,65 +192,12 @@ public: asio::error_code get_option(const implementation_type& impl, Option& option, asio::error_code& ec) const { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - if (option.level(impl.protocol_) == custom_socket_option_level - && option.name(impl.protocol_) == enable_connection_aborted_option) - { - if (option.size(impl.protocol_) != sizeof(int)) - { - ec = asio::error::invalid_argument; - } - else - { - int* target = reinterpret_cast(option.data(impl.protocol_)); - if (impl.flags_ & implementation_type::enable_connection_aborted) - *target = 1; - else - *target = 0; - option.resize(impl.protocol_, sizeof(int)); - ec = asio::error_code(); - } - return ec; - } - else - { - size_t size = option.size(impl.protocol_); - socket_ops::getsockopt(impl.socket_, - option.level(impl.protocol_), option.name(impl.protocol_), - option.data(impl.protocol_), &size, ec); - if (!ec) - option.resize(impl.protocol_, size); - return ec; - } - } - - // Perform an IO control command on the socket. - template - asio::error_code io_control(implementation_type& impl, - IO_Control_Command& command, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - socket_ops::ioctl(impl.socket_, command.name(), - static_cast(command.data()), ec); - - if (!ec && command.name() == static_cast(FIONBIO)) - { - if (*static_cast(command.data())) - impl.flags_ |= implementation_type::user_set_non_blocking; - else - impl.flags_ &= ~implementation_type::user_set_non_blocking; - } - + std::size_t size = option.size(impl.protocol_); + socket_ops::getsockopt(impl.socket_, impl.state_, + option.level(impl.protocol_), option.name(impl.protocol_), + option.data(impl.protocol_), &size, ec); + if (!ec) + option.resize(impl.protocol_, size); return ec; } @@ -578,12 +205,6 @@ public: endpoint_type local_endpoint(const implementation_type& impl, asio::error_code& ec) const { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return endpoint_type(); - } - endpoint_type endpoint; std::size_t addr_len = endpoint.capacity(); if (socket_ops::getsockname(impl.socket_, endpoint.data(), &addr_len, ec)) @@ -596,210 +217,13 @@ public: endpoint_type remote_endpoint(const implementation_type& impl, asio::error_code& ec) const { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; + endpoint_type endpoint = impl.remote_endpoint_; + std::size_t addr_len = endpoint.capacity(); + if (socket_ops::getpeername(impl.socket_, endpoint.data(), + &addr_len, impl.have_remote_endpoint_, ec)) return endpoint_type(); - } - - if (impl.socket_.have_remote_endpoint()) - { - // Check if socket is still connected. - DWORD connect_time = 0; - size_t connect_time_len = sizeof(connect_time); - if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_CONNECT_TIME, - &connect_time, &connect_time_len, ec) == socket_error_retval) - { - return endpoint_type(); - } - if (connect_time == 0xFFFFFFFF) - { - ec = asio::error::not_connected; - return endpoint_type(); - } - - ec = asio::error_code(); - return impl.socket_.remote_endpoint(); - } - else - { - endpoint_type endpoint; - std::size_t addr_len = endpoint.capacity(); - if (socket_ops::getpeername(impl.socket_, endpoint.data(), &addr_len, ec)) - return endpoint_type(); - endpoint.resize(addr_len); - return endpoint; - } - } - - /// Disable sends or receives on the socket. - asio::error_code shutdown(implementation_type& impl, - socket_base::shutdown_type what, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - socket_ops::shutdown(impl.socket_, what, ec); - return ec; - } - - // Send the given data to the peer. Returns the number of bytes sent. - template - size_t send(implementation_type& impl, const ConstBufferSequence& buffers, - socket_base::message_flags flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - buffer_sequence_adapter bufs(buffers); - - // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) - { - ec = asio::error_code(); - return 0; - } - - // Send the data. - DWORD bytes_transferred = 0; - int result = ::WSASend(impl.socket_, bufs.buffers(), - bufs.count(), &bytes_transferred, flags, 0, 0); - if (result != 0) - { - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_NETNAME_DELETED) - last_error = WSAECONNRESET; - else if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return 0; - } - - ec = asio::error_code(); - return bytes_transferred; - } - - // Wait until data can be sent without blocking. - size_t send(implementation_type& impl, const null_buffers&, - socket_base::message_flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - // Wait for socket to become ready. - socket_ops::poll_write(impl.socket_, ec); - - return 0; - } - - template - class send_op : public operation - { - public: - send_op(weak_cancel_token_type cancel_token, - const ConstBufferSequence& buffers, Handler handler) - : operation(&send_op::do_complete), - cancel_token_(cancel_token), - buffers_(buffers), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - send_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { -#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - buffer_sequence_adapter::validate(o->buffers_); -#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) - - // Map non-portable errors to their portable counterparts. - if (ec.value() == ERROR_NETNAME_DELETED) - { - if (o->cancel_token_.expired()) - ec = asio::error::operation_aborted; - else - ec = asio::error::connection_reset; - } - else if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = asio::error::connection_refused; - } - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, ec, bytes_transferred); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - weak_cancel_token_type cancel_token_; - ConstBufferSequence buffers_; - Handler handler_; - }; - - // Start an asynchronous send. The data being sent must be valid for the - // lifetime of the asynchronous operation. - template - void async_send(implementation_type& impl, const ConstBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef send_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, - impl.cancel_token_, buffers, handler); - - buffer_sequence_adapter bufs(buffers); - - start_send_op(impl, bufs.buffers(), bufs.count(), flags, - impl.protocol_.type() == SOCK_STREAM && bufs.all_empty(), ptr.get()); - ptr.release(); - } - - // Start an asynchronous wait until data can be sent without blocking. - template - void async_send(implementation_type& impl, const null_buffers&, - socket_base::message_flags, Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - start_reactor_op(impl, reactor::write_op, ptr.get()); - ptr.release(); + endpoint.resize(addr_len); + return endpoint; } // Send a datagram to the specified endpoint. Returns the number of bytes @@ -809,107 +233,25 @@ public: const endpoint_type& destination, socket_base::message_flags flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - buffer_sequence_adapter bufs(buffers); - // Send the data. - DWORD bytes_transferred = 0; - int result = ::WSASendTo(impl.socket_, bufs.buffers(), bufs.count(), - &bytes_transferred, flags, destination.data(), - static_cast(destination.size()), 0, 0); - if (result != 0) - { - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return 0; - } - - ec = asio::error_code(); - return bytes_transferred; + return socket_ops::sync_sendto(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, + destination.data(), destination.size(), ec); } // Wait until data can be sent without blocking. size_t send_to(implementation_type& impl, const null_buffers&, - socket_base::message_flags, const endpoint_type&, + const endpoint_type&, socket_base::message_flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - // Wait for socket to become ready. socket_ops::poll_write(impl.socket_, ec); return 0; } - template - class send_to_op : public operation - { - public: - send_to_op(weak_cancel_token_type cancel_token, - const ConstBufferSequence& buffers, Handler handler) - : operation(&send_to_op::do_complete), - cancel_token_(cancel_token), - buffers_(buffers), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - send_to_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { -#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - buffer_sequence_adapter::validate(o->buffers_); -#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) - - // Map non-portable errors to their portable counterparts. - if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = asio::error::connection_refused; - } - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, ec, bytes_transferred); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - weak_cancel_token_type cancel_token_; - ConstBufferSequence buffers_; - Handler handler_; - }; - // Start an asynchronous send. The data being sent must be valid for the // lifetime of the asynchronous operation. template @@ -918,233 +260,35 @@ public: socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef send_to_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, - impl.cancel_token_, buffers, handler); + typedef win_iocp_socket_send_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, buffers, handler); buffer_sequence_adapter bufs(buffers); - start_send_to_op(impl, bufs.buffers(), - bufs.count(), destination, flags, ptr.get()); - ptr.release(); + start_send_to_op(impl, bufs.buffers(), bufs.count(), + destination.data(), static_cast(destination.size()), + flags, p.p); + p.v = p.p = 0; } // Start an asynchronous wait until data can be sent without blocking. template void async_send_to(implementation_type& impl, const null_buffers&, - socket_base::message_flags, const endpoint_type&, Handler handler) + const endpoint_type&, socket_base::message_flags, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); - start_reactor_op(impl, reactor::write_op, ptr.get()); - ptr.release(); - } - - // Receive some data from the peer. Returns the number of bytes received. - template - size_t receive(implementation_type& impl, - const MutableBufferSequence& buffers, - socket_base::message_flags flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - buffer_sequence_adapter bufs(buffers); - - // A request to receive 0 bytes on a stream socket is a no-op. - if (impl.protocol_.type() == SOCK_STREAM && bufs.all_empty()) - { - ec = asio::error_code(); - return 0; - } - - // Receive some data. - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int result = ::WSARecv(impl.socket_, bufs.buffers(), - bufs.count(), &bytes_transferred, &recv_flags, 0, 0); - if (result != 0) - { - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_NETNAME_DELETED) - last_error = WSAECONNRESET; - else if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return 0; - } - if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM) - { - ec = asio::error::eof; - return 0; - } - - ec = asio::error_code(); - return bytes_transferred; - } - - // Wait until data can be received without blocking. - size_t receive(implementation_type& impl, const null_buffers&, - socket_base::message_flags, asio::error_code& ec) - { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - - // Wait for socket to become ready. - socket_ops::poll_read(impl.socket_, ec); - - return 0; - } - - template - class receive_op : public operation - { - public: - receive_op(int protocol_type, weak_cancel_token_type cancel_token, - const MutableBufferSequence& buffers, Handler handler) - : operation(&receive_op::do_complete), - protocol_type_(protocol_type), - cancel_token_(cancel_token), - buffers_(buffers), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - receive_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { -#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - buffer_sequence_adapter::validate(o->buffers_); -#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) - - // Map non-portable errors to their portable counterparts. - if (ec.value() == ERROR_NETNAME_DELETED) - { - if (o->cancel_token_.expired()) - ec = asio::error::operation_aborted; - else - ec = asio::error::connection_reset; - } - else if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = asio::error::connection_refused; - } - - // Check for connection closed. - else if (!ec && bytes_transferred == 0 - && o->protocol_type_ == SOCK_STREAM - && !buffer_sequence_adapter::all_empty(o->buffers_) - && !boost::is_same::value) - { - ec = asio::error::eof; - } - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, ec, bytes_transferred); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - int protocol_type_; - weak_cancel_token_type cancel_token_; - MutableBufferSequence buffers_; - Handler handler_; - }; - - // Start an asynchronous receive. The buffer for the data being received - // must be valid for the lifetime of the asynchronous operation. - template - void async_receive(implementation_type& impl, - const MutableBufferSequence& buffers, - socket_base::message_flags flags, Handler handler) - { - // Allocate and construct an operation to wrap the handler. - typedef receive_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - int protocol_type = impl.protocol_.type(); - handler_ptr ptr(raw_ptr, protocol_type, - impl.cancel_token_, buffers, handler); - - buffer_sequence_adapter bufs(buffers); - - start_receive_op(impl, bufs.buffers(), bufs.count(), flags, - protocol_type == SOCK_STREAM && bufs.all_empty(), ptr.get()); - ptr.release(); - } - - // Wait until data can be received without blocking. - template - void async_receive(implementation_type& impl, const null_buffers& buffers, - socket_base::message_flags flags, Handler handler) - { - if (impl.protocol_.type() == SOCK_STREAM) - { - // For stream sockets on Windows, we may issue a 0-byte overlapped - // WSARecv to wait until there is data available on the socket. - - // Allocate and construct an operation to wrap the handler. - typedef receive_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - int protocol_type = impl.protocol_.type(); - handler_ptr ptr(raw_ptr, protocol_type, - impl.cancel_token_, buffers, handler); - - ::WSABUF buf = { 0, 0 }; - start_receive_op(impl, &buf, 1, flags, false, ptr.get()); - ptr.release(); - } - else - { - // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); - - start_reactor_op(impl, - (flags & socket_base::message_out_of_band) - ? reactor::except_op : reactor::read_op, - ptr.get()); - ptr.release(); - } + start_reactor_op(impl, reactor::write_op, p.p); + p.v = p.p = 0; } // Receive a datagram with the endpoint of the sender. Returns the number of @@ -1155,41 +299,18 @@ public: endpoint_type& sender_endpoint, socket_base::message_flags flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - buffer_sequence_adapter bufs(buffers); - // Receive some data. - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int endpoint_size = static_cast(sender_endpoint.capacity()); - int result = ::WSARecvFrom(impl.socket_, bufs.buffers(), - bufs.count(), &bytes_transferred, &recv_flags, - sender_endpoint.data(), &endpoint_size, 0, 0); - if (result != 0) - { - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - ec = asio::error_code(last_error, - asio::error::get_system_category()); - return 0; - } - if (bytes_transferred == 0 && impl.protocol_.type() == SOCK_STREAM) - { - ec = asio::error::eof; - return 0; - } + std::size_t addr_len = sender_endpoint.capacity(); + std::size_t bytes_recvd = socket_ops::sync_recvfrom( + impl.socket_, impl.state_, bufs.buffers(), bufs.count(), + flags, sender_endpoint.data(), &addr_len, ec); - sender_endpoint.resize(static_cast(endpoint_size)); + if (!ec) + sender_endpoint.resize(addr_len); - ec = asio::error_code(); - return bytes_transferred; + return bytes_recvd; } // Wait until data can be received without blocking. @@ -1197,12 +318,6 @@ public: const null_buffers&, endpoint_type& sender_endpoint, socket_base::message_flags, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return 0; - } - // Wait for socket to become ready. socket_ops::poll_read(impl.socket_, ec); @@ -1212,75 +327,6 @@ public: return 0; } - template - class receive_from_op : public operation - { - public: - receive_from_op(int protocol_type, endpoint_type& endpoint, - const MutableBufferSequence& buffers, Handler handler) - : operation(&receive_from_op::do_complete), - protocol_type_(protocol_type), - endpoint_(endpoint), - endpoint_size_(static_cast(endpoint.capacity())), - buffers_(buffers), - handler_(handler) - { - } - - int& endpoint_size() - { - return endpoint_size_; - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t bytes_transferred) - { - // Take ownership of the operation object. - receive_from_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { -#if defined(ASIO_ENABLE_BUFFER_DEBUGGING) - // Check whether buffers are still valid. - buffer_sequence_adapter::validate(o->buffers_); -#endif // defined(ASIO_ENABLE_BUFFER_DEBUGGING) - - // Map non-portable errors to their portable counterparts. - if (ec.value() == ERROR_PORT_UNREACHABLE) - { - ec = asio::error::connection_refused; - } - - // Record the size of the endpoint returned by the operation. - o->endpoint_.resize(o->endpoint_size_); - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder2 - handler(o->handler_, ec, bytes_transferred); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - int protocol_type_; - endpoint_type& endpoint_; - int endpoint_size_; - weak_cancel_token_type cancel_token_; - MutableBufferSequence buffers_; - Handler handler_; - }; - // Start an asynchronous receive. The buffer for the data being received and // the sender_endpoint object must both be valid for the lifetime of the // asynchronous operation. @@ -1290,19 +336,19 @@ public: socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef receive_from_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - int protocol_type = impl.protocol_.type(); - handler_ptr ptr(raw_ptr, - protocol_type, sender_endp, buffers, handler); + typedef win_iocp_socket_recvfrom_op< + MutableBufferSequence, endpoint_type, Handler> op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(sender_endp, impl.cancel_token_, buffers, handler); buffer_sequence_adapter bufs(buffers); start_receive_from_op(impl, bufs.buffers(), bufs.count(), - sender_endp, flags, &ptr.get()->endpoint_size(), ptr.get()); - ptr.release(); + sender_endp.data(), flags, &p.p->endpoint_size(), p.p); + p.v = p.p = 0; } // Wait until data can be received without blocking. @@ -1312,19 +358,17 @@ public: socket_base::message_flags flags, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef null_buffers_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, handler); + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); // Reset endpoint since it can be given no sensible value at this time. sender_endpoint = endpoint_type(); - start_reactor_op(impl, - (flags & socket_base::message_out_of_band) - ? reactor::except_op : reactor::read_op, - ptr.get()); - ptr.release(); + start_null_buffers_receive_op(impl, flags, p.p); + p.v = p.p = 0; } // Accept a new connection. @@ -1332,12 +376,6 @@ public: asio::error_code accept(implementation_type& impl, Socket& peer, endpoint_type* peer_endpoint, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - // We cannot accept a socket that is already open. if (peer.is_open()) { @@ -1345,223 +383,23 @@ public: return ec; } - for (;;) + std::size_t addr_len = peer_endpoint ? peer_endpoint->capacity() : 0; + socket_holder new_socket(socket_ops::sync_accept(impl.socket_, + impl.state_, peer_endpoint ? peer_endpoint->data() : 0, + peer_endpoint ? &addr_len : 0, ec)); + + // On success, assign new connection to peer socket object. + if (new_socket.get() >= 0) { - socket_holder new_socket; - std::size_t addr_len = 0; - if (peer_endpoint) - { - addr_len = peer_endpoint->capacity(); - new_socket.reset(socket_ops::accept(impl.socket_, - peer_endpoint->data(), &addr_len, ec)); - } - else - { - new_socket.reset(socket_ops::accept(impl.socket_, 0, 0, ec)); - } - - if (ec) - { - if (ec == asio::error::connection_aborted - && !(impl.flags_ & implementation_type::enable_connection_aborted)) - { - // Retry accept operation. - continue; - } - else - { - return ec; - } - } - if (peer_endpoint) peer_endpoint->resize(addr_len); - - peer.assign(impl.protocol_, new_socket.get(), ec); - if (!ec) + if (!peer.assign(impl.protocol_, new_socket.get(), ec)) new_socket.release(); - return ec; } + + return ec; } - template - class accept_op : public operation - { - public: - accept_op(win_iocp_io_service& iocp_service, socket_type socket, - Socket& peer, const protocol_type& protocol, - endpoint_type* peer_endpoint, bool enable_connection_aborted, - Handler handler) - : operation(&accept_op::do_complete), - iocp_service_(iocp_service), - socket_(socket), - peer_(peer), - protocol_(protocol), - peer_endpoint_(peer_endpoint), - enable_connection_aborted_(enable_connection_aborted), - handler_(handler) - { - } - - socket_holder& new_socket() - { - return new_socket_; - } - - void* output_buffer() - { - return output_buffer_; - } - - DWORD address_length() - { - return sizeof(sockaddr_storage_type) + 16; - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code ec, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - accept_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Map Windows error ERROR_NETNAME_DELETED to connection_aborted. - if (ec.value() == ERROR_NETNAME_DELETED) - { - ec = asio::error::connection_aborted; - } - - // Restart the accept operation if we got the connection_aborted error - // and the enable_connection_aborted socket option is not set. - if (ec == asio::error::connection_aborted - && !o->enable_connection_aborted_) - { - // Reset OVERLAPPED structure. - o->reset(); - - // Create a new socket for the next connection, since the AcceptEx - // call fails with WSAEINVAL if we try to reuse the same socket. - o->new_socket_.reset(); - o->new_socket_.reset(socket_ops::socket(o->protocol_.family(), - o->protocol_.type(), o->protocol_.protocol(), ec)); - if (o->new_socket_.get() != invalid_socket) - { - // Accept a connection. - DWORD bytes_read = 0; - BOOL result = ::AcceptEx(o->socket_, o->new_socket_.get(), - o->output_buffer(), 0, o->address_length(), - o->address_length(), &bytes_read, o); - DWORD last_error = ::WSAGetLastError(); - ec = asio::error_code(last_error, - asio::error::get_system_category()); - - // Check if the operation completed immediately. - if (!result && last_error != WSA_IO_PENDING) - { - if (last_error == ERROR_NETNAME_DELETED - || last_error == WSAECONNABORTED) - { - // Post this handler so that operation will be restarted again. - o->iocp_service_.work_started(); - o->iocp_service_.on_completion(o, ec); - ptr.release(); - return; - } - else - { - // Operation already complete. Continue with rest of this - // handler. - } - } - else - { - // Asynchronous operation has been successfully restarted. - o->iocp_service_.work_started(); - o->iocp_service_.on_pending(o); - ptr.release(); - return; - } - } - } - - // Get the address of the peer. - endpoint_type peer_endpoint; - if (!ec) - { - LPSOCKADDR local_addr = 0; - int local_addr_length = 0; - LPSOCKADDR remote_addr = 0; - int remote_addr_length = 0; - GetAcceptExSockaddrs(o->output_buffer(), 0, o->address_length(), - o->address_length(), &local_addr, &local_addr_length, - &remote_addr, &remote_addr_length); - if (static_cast(remote_addr_length) - > peer_endpoint.capacity()) - { - ec = asio::error::invalid_argument; - } - else - { - using namespace std; // For memcpy. - memcpy(peer_endpoint.data(), remote_addr, remote_addr_length); - peer_endpoint.resize(static_cast(remote_addr_length)); - } - } - - // Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname - // and getpeername will work on the accepted socket. - if (!ec) - { - SOCKET update_ctx_param = o->socket_; - socket_ops::setsockopt(o->new_socket_.get(), - SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, - &update_ctx_param, sizeof(SOCKET), ec); - } - - // If the socket was successfully accepted, transfer ownership of the - // socket to the peer object. - if (!ec) - { - o->peer_.assign(o->protocol_, - native_type(o->new_socket_.get(), peer_endpoint), ec); - if (!ec) - o->new_socket_.release(); - } - - // Pass endpoint back to caller. - if (o->peer_endpoint_) - *o->peer_endpoint_ = peer_endpoint; - - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder1 - handler(o->handler_, ec); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - win_iocp_io_service& iocp_service_; - socket_type socket_; - socket_holder new_socket_; - Socket& peer_; - protocol_type protocol_; - endpoint_type* peer_endpoint_; - unsigned char output_buffer_[(sizeof(sockaddr_storage_type) + 16) * 2]; - bool enable_connection_aborted_; - Handler handler_; - }; - // Start an asynchronous accept. The peer and peer_endpoint objects // must be valid until the accept's handler is invoked. template @@ -1569,442 +407,54 @@ public: endpoint_type* peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef accept_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); + typedef win_iocp_socket_accept_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; bool enable_connection_aborted = - (impl.flags_ & implementation_type::enable_connection_aborted); - handler_ptr ptr(raw_ptr, iocp_service_, impl.socket_, peer, - impl.protocol_, peer_endpoint, enable_connection_aborted, handler); + (impl.state_ & socket_ops::enable_connection_aborted) != 0; + p.p = new (p.v) op(*this, impl.socket_, peer, impl.protocol_, + peer_endpoint, enable_connection_aborted, handler); - start_accept_op(impl, peer.is_open(), ptr.get()->new_socket(), - ptr.get()->output_buffer(), ptr.get()->address_length(), ptr.get()); - ptr.release(); + start_accept_op(impl, peer.is_open(), p.p->new_socket(), + impl.protocol_.family(), impl.protocol_.type(), + impl.protocol_.protocol(), p.p->output_buffer(), + p.p->address_length(), p.p); + p.v = p.p = 0; } // Connect the socket to the specified endpoint. asio::error_code connect(implementation_type& impl, const endpoint_type& peer_endpoint, asio::error_code& ec) { - if (!is_open(impl)) - { - ec = asio::error::bad_descriptor; - return ec; - } - - // Perform the connect operation. - socket_ops::connect(impl.socket_, + socket_ops::sync_connect(impl.socket_, peer_endpoint.data(), peer_endpoint.size(), ec); return ec; } - class connect_op_base : public reactor_op - { - public: - connect_op_base(socket_type socket, func_type complete_func) - : reactor_op(&connect_op_base::do_perform, complete_func), - socket_(socket) - { - } - - static bool do_perform(reactor_op* base) - { - connect_op_base* o(static_cast(base)); - - // Get the error code from the connect operation. - int connect_error = 0; - size_t connect_error_len = sizeof(connect_error); - if (socket_ops::getsockopt(o->socket_, SOL_SOCKET, SO_ERROR, - &connect_error, &connect_error_len, o->ec_) == socket_error_retval) - return true; - - // The connection failed so the handler will be posted with an error code. - if (connect_error) - { - o->ec_ = asio::error_code(connect_error, - asio::error::get_system_category()); - } - - return true; - } - - private: - socket_type socket_; - }; - - template - class connect_op : public connect_op_base - { - public: - connect_op(socket_type socket, Handler handler) - : connect_op_base(socket, &connect_op::do_complete), - handler_(handler) - { - } - - static void do_complete(io_service_impl* owner, operation* base, - asio::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - // Take ownership of the handler object. - connect_op* o(static_cast(base)); - typedef handler_alloc_traits alloc_traits; - handler_ptr ptr(o->handler_, o); - - // Make the upcall if required. - if (owner) - { - // Make a copy of the handler so that the memory can be deallocated - // before the upcall is made. Even if we're not about to make an - // upcall, a sub-object of the handler may be the true owner of the - // memory associated with the handler. Consequently, a local copy of - // the handler is required to ensure that any owning sub-object remains - // valid until after we have deallocated the memory here. - detail::binder1 - handler(o->handler_, o->ec_); - ptr.reset(); - asio::detail::fenced_block b; - asio_handler_invoke_helpers::invoke(handler, handler); - } - } - - private: - Handler handler_; - }; - // Start an asynchronous connect. template void async_connect(implementation_type& impl, const endpoint_type& peer_endpoint, Handler handler) { // Allocate and construct an operation to wrap the handler. - typedef connect_op value_type; - typedef handler_alloc_traits alloc_traits; - raw_handler_ptr raw_ptr(handler); - handler_ptr ptr(raw_ptr, impl.socket_, handler); + typedef reactive_socket_connect_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.socket_, handler); - start_connect_op(impl, ptr.get(), peer_endpoint); - ptr.release(); + start_connect_op(impl, p.p, peer_endpoint.data(), + static_cast(peer_endpoint.size())); + p.v = p.p = 0; } - -private: - // Helper function to start an asynchronous send operation. - void start_send_op(implementation_type& impl, WSABUF* buffers, - std::size_t buffer_count, socket_base::message_flags flags, - bool noop, operation* op) - { - update_cancellation_thread_id(impl); - iocp_service_.work_started(); - - if (noop) - iocp_service_.on_completion(op); - else if (!is_open(impl)) - iocp_service_.on_completion(op, asio::error::bad_descriptor); - else - { - DWORD bytes_transferred = 0; - int result = ::WSASend(impl.socket_, buffers, - buffer_count, &bytes_transferred, flags, op, 0); - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - if (result != 0 && last_error != WSA_IO_PENDING) - iocp_service_.on_completion(op, last_error, bytes_transferred); - else - iocp_service_.on_pending(op); - } - } - - // Helper function to start an asynchronous send_to operation. - void start_send_to_op(implementation_type& impl, WSABUF* buffers, - std::size_t buffer_count, const endpoint_type& destination, - socket_base::message_flags flags, operation* op) - { - update_cancellation_thread_id(impl); - iocp_service_.work_started(); - - if (!is_open(impl)) - iocp_service_.on_completion(op, asio::error::bad_descriptor); - else - { - DWORD bytes_transferred = 0; - int result = ::WSASendTo(impl.socket_, buffers, buffer_count, - &bytes_transferred, flags, destination.data(), - static_cast(destination.size()), op, 0); - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - if (result != 0 && last_error != WSA_IO_PENDING) - iocp_service_.on_completion(op, last_error, bytes_transferred); - else - iocp_service_.on_pending(op); - } - } - - // Helper function to start an asynchronous receive operation. - void start_receive_op(implementation_type& impl, WSABUF* buffers, - std::size_t buffer_count, socket_base::message_flags flags, - bool noop, operation* op) - { - update_cancellation_thread_id(impl); - iocp_service_.work_started(); - - if (noop) - iocp_service_.on_completion(op); - else if (!is_open(impl)) - iocp_service_.on_completion(op, asio::error::bad_descriptor); - else - { - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int result = ::WSARecv(impl.socket_, buffers, buffer_count, - &bytes_transferred, &recv_flags, op, 0); - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_NETNAME_DELETED) - last_error = WSAECONNRESET; - else if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - if (result != 0 && last_error != WSA_IO_PENDING) - iocp_service_.on_completion(op, last_error, bytes_transferred); - else - iocp_service_.on_pending(op); - } - } - - // Helper function to start an asynchronous receive_from operation. - void start_receive_from_op(implementation_type& impl, WSABUF* buffers, - std::size_t buffer_count, endpoint_type& sender_endpoint, - socket_base::message_flags flags, int* endpoint_size, operation* op) - { - update_cancellation_thread_id(impl); - iocp_service_.work_started(); - - if (!is_open(impl)) - iocp_service_.on_completion(op, asio::error::bad_descriptor); - else - { - DWORD bytes_transferred = 0; - DWORD recv_flags = flags; - int result = ::WSARecvFrom(impl.socket_, buffers, - buffer_count, &bytes_transferred, &recv_flags, - sender_endpoint.data(), endpoint_size, op, 0); - DWORD last_error = ::WSAGetLastError(); - if (last_error == ERROR_PORT_UNREACHABLE) - last_error = WSAECONNREFUSED; - if (result != 0 && last_error != WSA_IO_PENDING) - iocp_service_.on_completion(op, last_error, bytes_transferred); - else - iocp_service_.on_pending(op); - } - } - - // Helper function to start an asynchronous receive_from operation. - void start_accept_op(implementation_type& impl, - bool peer_is_open, socket_holder& new_socket, - void* output_buffer, DWORD address_length, operation* op) - { - update_cancellation_thread_id(impl); - iocp_service_.work_started(); - - if (!is_open(impl)) - iocp_service_.on_completion(op, asio::error::bad_descriptor); - else if (peer_is_open) - iocp_service_.on_completion(op, asio::error::already_open); - else - { - asio::error_code ec; - new_socket.reset(socket_ops::socket(impl.protocol_.family(), - impl.protocol_.type(), impl.protocol_.protocol(), ec)); - if (new_socket.get() == invalid_socket) - iocp_service_.on_completion(op, ec); - else - { - DWORD bytes_read = 0; - BOOL result = ::AcceptEx(impl.socket_, new_socket.get(), output_buffer, - 0, address_length, address_length, &bytes_read, op); - DWORD last_error = ::WSAGetLastError(); - if (!result && last_error != WSA_IO_PENDING) - iocp_service_.on_completion(op, last_error); - else - iocp_service_.on_pending(op); - } - } - } - - // Start an asynchronous read or write operation using the the reactor. - void start_reactor_op(implementation_type& impl, int op_type, reactor_op* op) - { - reactor& r = get_reactor(); - update_cancellation_thread_id(impl); - - if (is_open(impl)) - { - r.start_op(op_type, impl.socket_, impl.reactor_data_, op, false); - return; - } - else - op->ec_ = asio::error::bad_descriptor; - - iocp_service_.post_immediate_completion(op); - } - - // Start the asynchronous connect operation using the reactor. - void start_connect_op(implementation_type& impl, - reactor_op* op, const endpoint_type& peer_endpoint) - { - reactor& r = get_reactor(); - update_cancellation_thread_id(impl); - - if (is_open(impl)) - { - ioctl_arg_type non_blocking = 1; - if (!socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, op->ec_)) - { - if (socket_ops::connect(impl.socket_, peer_endpoint.data(), - peer_endpoint.size(), op->ec_) != 0) - { - if (!op->ec_ - && !(impl.flags_ & implementation_type::user_set_non_blocking)) - { - non_blocking = 0; - socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, op->ec_); - } - - if (op->ec_ == asio::error::in_progress - || op->ec_ == asio::error::would_block) - { - op->ec_ = asio::error_code(); - r.start_op(reactor::connect_op, impl.socket_, - impl.reactor_data_, op, true); - return; - } - } - } - } - else - op->ec_ = asio::error::bad_descriptor; - - iocp_service_.post_immediate_completion(op); - } - - // Helper function to close a socket when the associated object is being - // destroyed. - void close_for_destruction(implementation_type& impl) - { - if (is_open(impl)) - { - // Check if the reactor was created, in which case we need to close the - // socket on the reactor as well to cancel any operations that might be - // running there. - reactor* r = static_cast( - interlocked_compare_exchange_pointer( - reinterpret_cast(&reactor_), 0, 0)); - if (r) - r->close_descriptor(impl.socket_, impl.reactor_data_); - - // The socket destructor must not block. If the user has changed the - // linger option to block in the foreground, we will change it back to the - // default so that the closure is performed in the background. - if (impl.flags_ & implementation_type::close_might_block) - { - ::linger opt; - opt.l_onoff = 0; - opt.l_linger = 0; - asio::error_code ignored_ec; - socket_ops::setsockopt(impl.socket_, - SOL_SOCKET, SO_LINGER, &opt, sizeof(opt), ignored_ec); - } - - asio::error_code ignored_ec; - socket_ops::close(impl.socket_, ignored_ec); - impl.socket_ = invalid_socket; - impl.flags_ = 0; - impl.cancel_token_.reset(); -#if defined(ASIO_ENABLE_CANCELIO) - impl.safe_cancellation_thread_id_ = 0; -#endif // defined(ASIO_ENABLE_CANCELIO) - } - } - - // Update the ID of the thread from which cancellation is safe. - void update_cancellation_thread_id(implementation_type& impl) - { -#if defined(ASIO_ENABLE_CANCELIO) - if (impl.safe_cancellation_thread_id_ == 0) - impl.safe_cancellation_thread_id_ = ::GetCurrentThreadId(); - else if (impl.safe_cancellation_thread_id_ != ::GetCurrentThreadId()) - impl.safe_cancellation_thread_id_ = ~DWORD(0); -#else // defined(ASIO_ENABLE_CANCELIO) - (void)impl; -#endif // defined(ASIO_ENABLE_CANCELIO) - } - - // Helper function to get the reactor. If no reactor has been created yet, a - // new one is obtained from the io_service and a pointer to it is cached in - // this service. - reactor& get_reactor() - { - reactor* r = static_cast( - interlocked_compare_exchange_pointer( - reinterpret_cast(&reactor_), 0, 0)); - if (!r) - { - r = &(use_service(io_service_)); - interlocked_exchange_pointer(reinterpret_cast(&reactor_), r); - } - return *r; - } - - // Helper function to emulate InterlockedCompareExchangePointer functionality - // for: - // - very old Platform SDKs; and - // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. - void* interlocked_compare_exchange_pointer(void** dest, void* exch, void* cmp) - { -#if defined(_M_IX86) - return reinterpret_cast(InterlockedCompareExchange( - reinterpret_cast(dest), reinterpret_cast(exch), - reinterpret_cast(cmp))); -#else - return InterlockedCompareExchangePointer(dest, exch, cmp); -#endif - } - - // Helper function to emulate InterlockedExchangePointer functionality for: - // - very old Platform SDKs; and - // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. - void* interlocked_exchange_pointer(void** dest, void* val) - { -#if defined(_M_IX86) - return reinterpret_cast(InterlockedExchange( - reinterpret_cast(dest), reinterpret_cast(val))); -#else - return InterlockedExchangePointer(dest, val); -#endif - } - - // The io_service used to obtain the reactor, if required. - asio::io_service& io_service_; - - // The IOCP service used for running asynchronous operations and dispatching - // handlers. - win_iocp_io_service& iocp_service_; - - // The reactor used for performing connect operations. This object is created - // only if needed. - reactor* reactor_; - - // Mutex to protect access to the linked list of implementations. - asio::detail::mutex mutex_; - - // The head of a linked list of all implementations. - implementation_type* impl_list_; }; } // namespace detail } // namespace asio -#endif // defined(ASIO_HAS_IOCP) - #include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_IOCP) + #endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_HPP diff --git a/ext/asio/asio/detail/win_iocp_socket_service_base.hpp b/ext/asio/asio/detail/win_iocp_socket_service_base.hpp new file mode 100644 index 0000000000..62010b3d14 --- /dev/null +++ b/ext/asio/asio/detail/win_iocp_socket_service_base.hpp @@ -0,0 +1,385 @@ +// +// detail/win_iocp_socket_service_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP +#define ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_IOCP) + +#include +#include +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/socket_base.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/buffer_sequence_adapter.hpp" +#include "asio/detail/fenced_block.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/mutex.hpp" +#include "asio/detail/operation.hpp" +#include "asio/detail/reactor.hpp" +#include "asio/detail/reactor_op.hpp" +#include "asio/detail/socket_holder.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/detail/win_iocp_io_service.hpp" +#include "asio/detail/win_iocp_null_buffers_op.hpp" +#include "asio/detail/win_iocp_socket_send_op.hpp" +#include "asio/detail/win_iocp_socket_recv_op.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace detail { + +class win_iocp_socket_service_base +{ +public: + // The implementation type of the socket. + struct base_implementation_type + { + // The native socket representation. + socket_type socket_; + + // The current state of the socket. + socket_ops::state_type state_; + + // We use a shared pointer as a cancellation token here to work around the + // broken Windows support for cancellation. MSDN says that when you call + // closesocket any outstanding WSARecv or WSASend operations will complete + // with the error ERROR_OPERATION_ABORTED. In practice they complete with + // ERROR_NETNAME_DELETED, which means you can't tell the difference between + // a local cancellation and the socket being hard-closed by the peer. + socket_ops::shared_cancel_token_type cancel_token_; + + // Per-descriptor data used by the reactor. + reactor::per_descriptor_data reactor_data_; + +#if defined(ASIO_ENABLE_CANCELIO) + // The ID of the thread from which it is safe to cancel asynchronous + // operations. 0 means no asynchronous operations have been started yet. + // ~0 means asynchronous operations have been started from more than one + // thread, and cancellation is not supported for the socket. + DWORD safe_cancellation_thread_id_; +#endif // defined(ASIO_ENABLE_CANCELIO) + + // Pointers to adjacent socket implementations in linked list. + base_implementation_type* next_; + base_implementation_type* prev_; + }; + + // Constructor. + ASIO_DECL win_iocp_socket_service_base( + asio::io_service& io_service); + + // Destroy all user-defined handler objects owned by the service. + ASIO_DECL void shutdown_service(); + + // Construct a new socket implementation. + ASIO_DECL void construct(base_implementation_type& impl); + + // Destroy a socket implementation. + ASIO_DECL void destroy(base_implementation_type& impl); + + // Determine whether the socket is open. + bool is_open(const base_implementation_type& impl) const + { + return impl.socket_ != invalid_socket; + } + + // Destroy a socket implementation. + ASIO_DECL asio::error_code close( + base_implementation_type& impl, asio::error_code& ec); + + // Cancel all operations associated with the socket. + ASIO_DECL asio::error_code cancel( + base_implementation_type& impl, asio::error_code& ec); + + // Determine whether the socket is at the out-of-band data mark. + bool at_mark(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::sockatmark(impl.socket_, ec); + } + + // Determine the number of bytes available for reading. + std::size_t available(const base_implementation_type& impl, + asio::error_code& ec) const + { + return socket_ops::available(impl.socket_, ec); + } + + // Place the socket into the state where it will listen for new connections. + asio::error_code listen(base_implementation_type& impl, + int backlog, asio::error_code& ec) + { + socket_ops::listen(impl.socket_, backlog, ec); + return ec; + } + + // Perform an IO control command on the socket. + template + asio::error_code io_control(base_implementation_type& impl, + IO_Control_Command& command, asio::error_code& ec) + { + socket_ops::ioctl(impl.socket_, impl.state_, command.name(), + static_cast(command.data()), ec); + return ec; + } + + /// Disable sends or receives on the socket. + asio::error_code shutdown(base_implementation_type& impl, + socket_base::shutdown_type what, asio::error_code& ec) + { + socket_ops::shutdown(impl.socket_, what, ec); + return ec; + } + + // Send the given data to the peer. Returns the number of bytes sent. + template + size_t send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_send(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be sent without blocking. + size_t send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_write(impl.socket_, ec); + + return 0; + } + + // Start an asynchronous send. The data being sent must be valid for the + // lifetime of the asynchronous operation. + template + void async_send(base_implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_send_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, buffers, handler); + + buffer_sequence_adapter bufs(buffers); + + start_send_op(impl, bufs.buffers(), bufs.count(), flags, + (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), + p.p); + p.v = p.p = 0; + } + + // Start an asynchronous wait until data can be sent without blocking. + template + void async_send(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + start_reactor_op(impl, reactor::write_op, p.p); + p.v = p.p = 0; + } + + // Receive some data from the peer. Returns the number of bytes received. + template + size_t receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, asio::error_code& ec) + { + buffer_sequence_adapter bufs(buffers); + + return socket_ops::sync_recv(impl.socket_, impl.state_, + bufs.buffers(), bufs.count(), flags, bufs.all_empty(), ec); + } + + // Wait until data can be received without blocking. + size_t receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags, asio::error_code& ec) + { + // Wait for socket to become ready. + socket_ops::poll_read(impl.socket_, ec); + + return 0; + } + + // Start an asynchronous receive. The buffer for the data being received + // must be valid for the lifetime of the asynchronous operation. + template + void async_receive(base_implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_socket_recv_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.state_, impl.cancel_token_, buffers, handler); + + buffer_sequence_adapter bufs(buffers); + + start_receive_op(impl, bufs.buffers(), bufs.count(), flags, + (impl.state_ & socket_ops::stream_oriented) != 0 && bufs.all_empty(), + p.p); + p.v = p.p = 0; + } + + // Wait until data can be received without blocking. + template + void async_receive(base_implementation_type& impl, const null_buffers&, + socket_base::message_flags flags, Handler handler) + { + // Allocate and construct an operation to wrap the handler. + typedef win_iocp_null_buffers_op op; + typename op::ptr p = { boost::addressof(handler), + asio_handler_alloc_helpers::allocate( + sizeof(op), handler), 0 }; + p.p = new (p.v) op(impl.cancel_token_, handler); + + start_null_buffers_receive_op(impl, flags, p.p); + p.v = p.p = 0; + } + + // Helper function to restart an asynchronous accept operation. + ASIO_DECL void restart_accept_op(socket_type s, + socket_holder& new_socket, int family, int type, int protocol, + void* output_buffer, DWORD address_length, operation* op); + +protected: + // Open a new socket implementation. + ASIO_DECL asio::error_code do_open( + base_implementation_type& impl, int family, int type, + int protocol, asio::error_code& ec); + + // Assign a native socket to a socket implementation. + ASIO_DECL asio::error_code do_assign( + base_implementation_type& impl, int type, + socket_type native_socket, asio::error_code& ec); + + // Helper function to start an asynchronous send operation. + ASIO_DECL void start_send_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op); + + // Helper function to start an asynchronous send_to operation. + ASIO_DECL void start_send_to_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + const socket_addr_type* addr, int addrlen, + socket_base::message_flags flags, operation* op); + + // Helper function to start an asynchronous receive operation. + ASIO_DECL void start_receive_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, + socket_base::message_flags flags, bool noop, operation* op); + + // Helper function to start an asynchronous null_buffers receive operation. + ASIO_DECL void start_null_buffers_receive_op( + base_implementation_type& impl, + socket_base::message_flags flags, reactor_op* op); + + // Helper function to start an asynchronous receive_from operation. + ASIO_DECL void start_receive_from_op(base_implementation_type& impl, + WSABUF* buffers, std::size_t buffer_count, socket_addr_type* addr, + socket_base::message_flags flags, int* addrlen, operation* op); + + // Helper function to start an asynchronous accept operation. + ASIO_DECL void start_accept_op(base_implementation_type& impl, + bool peer_is_open, socket_holder& new_socket, int family, int type, + int protocol, void* output_buffer, DWORD address_length, operation* op); + + // Start an asynchronous read or write operation using the the reactor. + ASIO_DECL void start_reactor_op(base_implementation_type& impl, + int op_type, reactor_op* op); + + // Start the asynchronous connect operation using the reactor. + ASIO_DECL void start_connect_op(base_implementation_type& impl, + reactor_op* op, const socket_addr_type* addr, std::size_t addrlen); + + // Helper function to close a socket when the associated object is being + // destroyed. + ASIO_DECL void close_for_destruction(base_implementation_type& impl); + + // Update the ID of the thread from which cancellation is safe. + ASIO_DECL void update_cancellation_thread_id( + base_implementation_type& impl); + + // Helper function to get the reactor. If no reactor has been created yet, a + // new one is obtained from the io_service and a pointer to it is cached in + // this service. + ASIO_DECL reactor& get_reactor(); + + // Helper function to emulate InterlockedCompareExchangePointer functionality + // for: + // - very old Platform SDKs; and + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + ASIO_DECL void* interlocked_compare_exchange_pointer( + void** dest, void* exch, void* cmp); + + // Helper function to emulate InterlockedExchangePointer functionality for: + // - very old Platform SDKs; and + // - platform SDKs where MSVC's /Wp64 option causes spurious warnings. + ASIO_DECL void* interlocked_exchange_pointer(void** dest, void* val); + + // The io_service used to obtain the reactor, if required. + asio::io_service& io_service_; + + // The IOCP service used for running asynchronous operations and dispatching + // handlers. + win_iocp_io_service& iocp_service_; + + // The reactor used for performing connect operations. This object is created + // only if needed. + reactor* reactor_; + + // Mutex to protect access to the linked list of implementations. + asio::detail::mutex mutex_; + + // The head of a linked list of all implementations. + base_implementation_type* impl_list_; +}; + +} // namespace detail +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_iocp_socket_service_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_IOCP) + +#endif // ASIO_DETAIL_WIN_IOCP_SOCKET_SERVICE_BASE_HPP diff --git a/ext/asio/asio/detail/win_mutex.hpp b/ext/asio/asio/detail/win_mutex.hpp index 1280a4e402..59ad6976cd 100644 --- a/ext/asio/asio/detail/win_mutex.hpp +++ b/ext/asio/asio/detail/win_mutex.hpp @@ -1,8 +1,8 @@ // -// win_mutex.hpp -// ~~~~~~~~~~~~~ +// detail/win_mutex.hpp +// ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,23 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" -#include "asio/detail/socket_types.hpp" #include "asio/detail/scoped_lock.hpp" +#include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace detail { @@ -43,18 +35,7 @@ public: typedef asio::detail::scoped_lock scoped_lock; // Constructor. - win_mutex() - { - int error = do_init(); - if (error != 0) - { - asio::system_error e( - asio::error_code(error, - asio::error::get_system_category()), - "mutex"); - boost::throw_exception(e); - } - } + ASIO_DECL win_mutex(); // Destructor. ~win_mutex() @@ -78,35 +59,7 @@ private: // Initialisation must be performed in a separate function to the constructor // since the compiler does not support the use of structured exceptions and // C++ exceptions in the same function. - int do_init() - { -#if defined(__MINGW32__) - // Not sure if MinGW supports structured exception handling, so for now - // we'll just call the Windows API and hope. -# if defined(UNDER_CE) - ::InitializeCriticalSection(&crit_section_); -# else - ::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000); -# endif - return 0; -#else - __try - { -# if defined(UNDER_CE) - ::InitializeCriticalSection(&crit_section_); -# else - ::InitializeCriticalSectionAndSpinCount(&crit_section_, 0x80000000); -# endif - } - __except(GetExceptionCode() == STATUS_NO_MEMORY - ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) - { - return ERROR_OUTOFMEMORY; - } - - return 0; -#endif - } + ASIO_DECL int do_init(); ::CRITICAL_SECTION crit_section_; }; @@ -114,8 +67,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_mutex.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_WINDOWS) + #endif // ASIO_DETAIL_WIN_MUTEX_HPP diff --git a/ext/asio/asio/detail/win_thread.hpp b/ext/asio/asio/detail/win_thread.hpp index 9bf0665c5e..e73d1a4965 100644 --- a/ext/asio/asio/detail/win_thread.hpp +++ b/ext/asio/asio/detail/win_thread.hpp @@ -1,8 +1,8 @@ // -// win_thread.hpp -// ~~~~~~~~~~~~~~ +// detail/win_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,34 +15,24 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) && !defined(UNDER_CE) -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" -#include -#include -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace detail { -unsigned int __stdcall win_thread_function(void* arg); +ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); #if defined(WINVER) && (WINVER < 0x0500) -void __stdcall apc_function(ULONG data); +ASIO_DECL void __stdcall apc_function(ULONG data); #else -void __stdcall apc_function(ULONG_PTR data); +ASIO_DECL void __stdcall apc_function(ULONG_PTR data); #endif template @@ -73,92 +63,26 @@ class win_thread public: // Constructor. template - win_thread(Function f) - : exit_event_(0) + win_thread(Function f, unsigned int stack_size = 0) + : thread_(0), + exit_event_(0) { - std::auto_ptr arg(new func(f)); - - ::HANDLE entry_event = 0; - arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0); - if (!entry_event) - { - DWORD last_error = ::GetLastError(); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "thread.entry_event"); - boost::throw_exception(e); - } - - arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0); - if (!exit_event_) - { - DWORD last_error = ::GetLastError(); - ::CloseHandle(entry_event); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "thread.exit_event"); - boost::throw_exception(e); - } - - unsigned int thread_id = 0; - thread_ = reinterpret_cast(::_beginthreadex(0, 0, - win_thread_function, arg.get(), 0, &thread_id)); - if (!thread_) - { - DWORD last_error = ::GetLastError(); - if (entry_event) - ::CloseHandle(entry_event); - if (exit_event_) - ::CloseHandle(exit_event_); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "thread"); - boost::throw_exception(e); - } - arg.release(); - - if (entry_event) - { - ::WaitForSingleObject(entry_event, INFINITE); - ::CloseHandle(entry_event); - } + start_thread(new func(f), stack_size); } // Destructor. - ~win_thread() - { - ::CloseHandle(thread_); - - // The exit_event_ handle is deliberately allowed to leak here since it - // is an error for the owner of an internal thread not to join() it. - } + ASIO_DECL ~win_thread(); // Wait for the thread to exit. - void join() - { - ::WaitForSingleObject(exit_event_, INFINITE); - ::CloseHandle(exit_event_); - if (terminate_threads()) - { - ::TerminateThread(thread_, 0); - } - else - { - ::QueueUserAPC(apc_function, thread_, 0); - ::WaitForSingleObject(thread_, INFINITE); - } - } + ASIO_DECL void join(); private: - friend unsigned int __stdcall win_thread_function(void* arg); + friend ASIO_DECL unsigned int __stdcall win_thread_function(void* arg); #if defined(WINVER) && (WINVER < 0x0500) - friend void __stdcall apc_function(ULONG); + friend ASIO_DECL void __stdcall apc_function(ULONG); #else - friend void __stdcall apc_function(ULONG_PTR); + friend ASIO_DECL void __stdcall apc_function(ULONG_PTR); #endif class func_base @@ -170,6 +94,12 @@ private: ::HANDLE exit_event_; }; + struct auto_func_base_ptr + { + func_base* ptr; + ~auto_func_base_ptr() { delete ptr; } + }; + template class func : public func_base @@ -189,44 +119,21 @@ private: Function f_; }; + ASIO_DECL void start_thread(func_base* arg, unsigned int stack_size); + ::HANDLE thread_; ::HANDLE exit_event_; }; -inline unsigned int __stdcall win_thread_function(void* arg) -{ - std::auto_ptr func( - static_cast(arg)); - - ::SetEvent(func->entry_event_); - - func->run(); - - // Signal that the thread has finished its work, but rather than returning go - // to sleep to put the thread into a well known state. If the thread is being - // joined during global object destruction then it may be killed using - // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx - // call will be interrupted using QueueUserAPC and the thread will shut down - // cleanly. - HANDLE exit_event = func->exit_event_; - func.reset(); - ::SetEvent(exit_event); - ::SleepEx(INFINITE, TRUE); - - return 0; -} - -#if defined(WINVER) && (WINVER < 0x0500) -inline void __stdcall apc_function(ULONG) {} -#else -inline void __stdcall apc_function(ULONG_PTR) {} -#endif - } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_thread.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_WINDOWS) && !defined(UNDER_CE) + #endif // ASIO_DETAIL_WIN_THREAD_HPP diff --git a/ext/asio/asio/detail/win_tss_ptr.hpp b/ext/asio/asio/detail/win_tss_ptr.hpp index 5a4ed33cc2..fb055f6c43 100644 --- a/ext/asio/asio/detail/win_tss_ptr.hpp +++ b/ext/asio/asio/detail/win_tss_ptr.hpp @@ -1,8 +1,8 @@ // -// win_tss_ptr.hpp -// ~~~~~~~~~~~~~~~ +// detail/win_tss_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,50 +15,30 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) -#include "asio/error.hpp" -#include "asio/system_error.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" #include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace detail { +// Helper function to create thread-specific storage. +ASIO_DECL DWORD win_tss_ptr_create(); + template class win_tss_ptr : private noncopyable { public: -#if defined(UNDER_CE) - enum { out_of_indexes = 0xFFFFFFFF }; -#else - enum { out_of_indexes = TLS_OUT_OF_INDEXES }; -#endif - // Constructor. win_tss_ptr() + : tss_key_(win_tss_ptr_create()) { - tss_key_ = ::TlsAlloc(); - if (tss_key_ == out_of_indexes) - { - DWORD last_error = ::GetLastError(); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "tss"); - boost::throw_exception(e); - } } // Destructor. @@ -88,8 +68,12 @@ private: } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/win_tss_ptr.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_WINDOWS) + #endif // ASIO_DETAIL_WIN_TSS_PTR_HPP diff --git a/ext/asio/asio/detail/wince_thread.hpp b/ext/asio/asio/detail/wince_thread.hpp index 0b6de488a2..1dc999075c 100644 --- a/ext/asio/asio/detail/wince_thread.hpp +++ b/ext/asio/asio/detail/wince_thread.hpp @@ -1,8 +1,8 @@ // -// wince_thread.hpp -// ~~~~~~~~~~~~~~~~ +// detail/wince_thread.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,23 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) && defined(UNDER_CE) -#include "asio/error.hpp" -#include "asio/system_error.hpp" +#include #include "asio/detail/noncopyable.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" #include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace detail { @@ -53,11 +47,9 @@ public: if (!thread_) { DWORD last_error = ::GetLastError(); - asio::system_error e( - asio::error_code(last_error, - asio::error::get_system_category()), - "thread"); - boost::throw_exception(e); + asio::error_code ec(last_error, + asio::error::get_system_category()); + asio::detail::throw_error(ec, "thread"); } arg.release(); } @@ -117,8 +109,8 @@ inline DWORD WINAPI wince_thread_function(LPVOID arg) } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) && defined(UNDER_CE) - #include "asio/detail/pop_options.hpp" +#endif // defined(BOOST_WINDOWS) && defined(UNDER_CE) + #endif // ASIO_DETAIL_WINCE_THREAD_HPP diff --git a/ext/asio/asio/detail/winsock_init.hpp b/ext/asio/asio/detail/winsock_init.hpp index ae5c4bf598..a410859f3b 100644 --- a/ext/asio/asio/detail/winsock_init.hpp +++ b/ext/asio/asio/detail/winsock_init.hpp @@ -1,8 +1,8 @@ // -// winsock_init.hpp -// ~~~~~~~~~~~~~~~~ +// detail/winsock_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,106 +15,76 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) #include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/noncopyable.hpp" -#include "asio/detail/socket_types.hpp" namespace asio { namespace detail { -template -class winsock_init - : private noncopyable +class winsock_init_base { -private: - // Structure to perform the actual initialisation. - struct do_init +protected: + // Structure to track result of initialisation and number of uses. POD is used + // to ensure that the values are zero-initialised prior to any code being run. + struct data { - do_init() - { - WSADATA wsa_data; - result_ = ::WSAStartup(MAKEWORD(Major, Minor), &wsa_data); - } - - ~do_init() - { - ::WSACleanup(); - } - - int result() const - { - return result_; - } - - // Helper function to manage a do_init singleton. The static instance of the - // winsock_init object ensures that this function is always called before - // main, and therefore before any other threads can get started. The do_init - // instance must be static in this function to ensure that it gets - // initialised before any other global objects try to use it. - static boost::shared_ptr instance() - { - static boost::shared_ptr init(new do_init); - return init; - } - - private: - int result_; + long init_count_; + long result_; }; + ASIO_DECL static void startup(data& d, + unsigned char major, unsigned char minor); + + ASIO_DECL static void cleanup(data& d); + + ASIO_DECL static void throw_on_error(data& d); +}; + +template +class winsock_init : private winsock_init_base +{ public: - // Constructor. - winsock_init() - : ref_(do_init::instance()) + winsock_init(bool allow_throw = true) { - // Check whether winsock was successfully initialised. This check is not - // performed for the global instance since there will be nobody around to - // catch the exception. - if (this != &instance_ && ref_->result() != 0) - { - asio::system_error e( - asio::error_code(ref_->result(), - asio::error::get_system_category()), - "winsock"); - boost::throw_exception(e); - } + startup(data_, Major, Minor); + if (allow_throw) + throw_on_error(data_); + } + + winsock_init(const winsock_init&) + { + startup(data_, Major, Minor); + throw_on_error(data_); } - // Destructor. ~winsock_init() { + cleanup(data_); } private: - // Instance to force initialisation of winsock at global scope. - static winsock_init instance_; - - // Reference to singleton do_init object to ensure that winsock does not get - // cleaned up until the last user has finished with it. - boost::shared_ptr ref_; + static data data_; }; template -winsock_init winsock_init::instance_; +winsock_init_base::data winsock_init::data_; + +// Static variable to ensure that winsock is initialised before main, and +// therefore before any other threads can get started. +static const winsock_init<>& winsock_init_instance = winsock_init<>(false); } // namespace detail } // namespace asio -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/detail/impl/winsock_init.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + #endif // ASIO_DETAIL_WINSOCK_INIT_HPP diff --git a/ext/asio/asio/detail/wrapped_handler.hpp b/ext/asio/asio/detail/wrapped_handler.hpp index e40a7f1585..bcebf6ee1d 100644 --- a/ext/asio/asio/detail/wrapped_handler.hpp +++ b/ext/asio/asio/detail/wrapped_handler.hpp @@ -1,8 +1,8 @@ // -// wrapped_handler.hpp -// ~~~~~~~~~~~~~~~~~~~ +// detail/wrapped_handler.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/bind_handler.hpp" #include "asio/detail/handler_alloc_helpers.hpp" #include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { @@ -34,9 +30,7 @@ class wrapped_handler public: typedef void result_type; - wrapped_handler( - typename boost::add_reference::type dispatcher, - Handler handler) + wrapped_handler(Dispatcher dispatcher, Handler handler) : dispatcher_(dispatcher), handler_(handler) { diff --git a/ext/asio/asio/error.hpp b/ext/asio/asio/error.hpp index 73caac6abd..138b5d789c 100644 --- a/ext/asio/asio/error.hpp +++ b/ext/asio/asio/error.hpp @@ -2,7 +2,7 @@ // error.hpp // ~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error_code.hpp" -#include "asio/detail/socket_types.hpp" +#include "asio/detail/config.hpp" +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# include +#else +# include +# include +#endif #if defined(GENERATING_DOCUMENTATION) /// INTERNAL ONLY. @@ -50,6 +48,8 @@ # define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix #endif +#include "asio/detail/push_options.hpp" + namespace asio { namespace error { @@ -212,7 +212,21 @@ enum ssl_errors { }; -// boostify: error category definitions go here. +// boostify: error category definitions start here. + +} // namespace error +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace error { + +// boostify: error category definitions end here. inline asio::error_code make_error_code(basic_errors e) { @@ -247,14 +261,16 @@ inline asio::error_code make_error_code(ssl_errors e) } // namespace error } // namespace asio +#include "asio/detail/pop_options.hpp" + #undef ASIO_NATIVE_ERROR #undef ASIO_SOCKET_ERROR #undef ASIO_NETDB_ERROR #undef ASIO_GETADDRINFO_ERROR #undef ASIO_WIN_OR_POSIX -#include "asio/impl/error_code.ipp" - -#include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/error.ipp" +#endif // defined(ASIO_HEADER_ONLY) #endif // ASIO_ERROR_HPP diff --git a/ext/asio/asio/error_code.hpp b/ext/asio/asio/error_code.hpp index 6657f3f2ee..1af449bda1 100644 --- a/ext/asio/asio/error_code.hpp +++ b/ext/asio/asio/error_code.hpp @@ -2,7 +2,7 @@ // error_code.hpp // ~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,8 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" #if defined(GENERATING_DOCUMENTATION) # define ASIO_WIN_OR_POSIX(e_win, e_posix) implementation_defined @@ -30,6 +26,8 @@ # define ASIO_WIN_OR_POSIX(e_win, e_posix) e_posix #endif +#include "asio/detail/push_options.hpp" + namespace asio { namespace error @@ -106,7 +104,7 @@ public: } /// Get the message associated with the error. - std::string message() const; + ASIO_DECL std::string message() const; struct unspecified_bool_type_t { @@ -114,14 +112,12 @@ public: typedef void (*unspecified_bool_type)(unspecified_bool_type_t); - static void unspecified_bool_true(unspecified_bool_type_t) - { - } + static void unspecified_bool_true(unspecified_bool_type_t) {} /// Operator returns non-null if there is a non-success error code. operator unspecified_bool_type() const { - if (value_ == 0) + if (!value_) return 0; else return &error_code::unspecified_bool_true; @@ -130,7 +126,7 @@ public: /// Operator to test if the error represents success. bool operator!() const { - return value_ == 0; + return !value_; } /// Equality operator to compare two error objects. @@ -155,10 +151,12 @@ private: } // namespace asio -#undef ASIO_WIN_OR_POSIX - -#include "asio/error.hpp" - #include "asio/detail/pop_options.hpp" +#undef ASIO_WIN_OR_POSIX + +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/error_code.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_ERROR_CODE_HPP diff --git a/ext/asio/asio/handler_alloc_hook.hpp b/ext/asio/asio/handler_alloc_hook.hpp index 87783cdfa5..dd930b6936 100644 --- a/ext/asio/asio/handler_alloc_hook.hpp +++ b/ext/asio/asio/handler_alloc_hook.hpp @@ -2,7 +2,7 @@ // handler_alloc_hook.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include #include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" namespace asio { diff --git a/ext/asio/asio/handler_invoke_hook.hpp b/ext/asio/asio/handler_invoke_hook.hpp index b3d7e45440..ef22359b13 100644 --- a/ext/asio/asio/handler_invoke_hook.hpp +++ b/ext/asio/asio/handler_invoke_hook.hpp @@ -2,7 +2,7 @@ // handler_invoke_hook.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,6 +15,8 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) +#include "asio/detail/config.hpp" + #include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/impl/error.ipp b/ext/asio/asio/impl/error.ipp new file mode 100644 index 0000000000..54388b31ae --- /dev/null +++ b/ext/asio/asio/impl/error.ipp @@ -0,0 +1,33 @@ +// +// impl/error.ipp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_ERROR_IPP +#define ASIO_IMPL_ERROR_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace error { + +// boostify: error category function definitions go here. + +} // namespace error +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_ERROR_IPP diff --git a/ext/asio/asio/impl/error_code.ipp b/ext/asio/asio/impl/error_code.ipp index 614925dd41..ed37a17dd3 100644 --- a/ext/asio/asio/impl/error_code.ipp +++ b/ext/asio/asio/impl/error_code.ipp @@ -1,50 +1,55 @@ // -// error_code.ipp -// ~~~~~~~~~~~~~~ +// impl/error_code.ipp +// ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef ASIO_ERROR_CODE_IPP -#define ASIO_ERROR_CODE_IPP +#ifndef ASIO_IMPL_ERROR_CODE_IPP +#define ASIO_IMPL_ERROR_CODE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" +#include "asio/detail/config.hpp" #include "asio/detail/local_free_on_block_exit.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/error.hpp" +#include "asio/error_code.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { -inline std::string error_code::message() const +std::string error_code::message() const { - if (*this == error::already_open) - return "Already open."; - if (*this == error::not_found) - return "Not found."; - if (*this == error::fd_set_failure) - return "The descriptor does not fit into the select call's fd_set."; + if (category_ == error::get_misc_category()) + { + if (value_ == error::already_open) + return "Already open."; + if (value_ == error::not_found) + return "Not found."; + if (value_ == error::fd_set_failure) + return "The descriptor does not fit into the select call's fd_set."; + if (value_ == error::not_found) + return "Element not found."; +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + if (value_ == error::eof) + return "End of file."; +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + } if (category_ == error::get_ssl_category()) return "SSL error."; #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) value_type value = value_; - if (category() != error::get_system_category() && *this != error::eof) - return "asio error"; - if (*this == error::eof) + if (category_ == error::get_misc_category() && value_ == error::eof) value = ERROR_HANDLE_EOF; + else if (category_ != error::get_system_category()) + return "asio error"; char* msg = 0; DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM @@ -60,29 +65,31 @@ inline std::string error_code::message() const else return "asio error"; #else // defined(BOOST_WINDOWS) - if (*this == error::eof) - return "End of file."; - if (*this == error::host_not_found) - return "Host not found (authoritative)."; - if (*this == error::host_not_found_try_again) - return "Host not found (non-authoritative), try again later."; - if (*this == error::no_recovery) - return "A non-recoverable error occurred during database lookup."; - if (*this == error::no_data) - return "The query is valid, but it does not have associated data."; - if (*this == error::not_found) - return "Element not found."; + if (category_ == error::get_netdb_category()) + { + if (value_ == error::host_not_found) + return "Host not found (authoritative)."; + if (value_ == error::host_not_found_try_again) + return "Host not found (non-authoritative), try again later."; + if (value_ == error::no_recovery) + return "A non-recoverable error occurred during database lookup."; + if (value_ == error::no_data) + return "The query is valid, but it does not have associated data."; + } + if (category_ == error::get_addrinfo_category()) + { + if (value_ == error::service_not_found) + return "Service not found."; + if (value_ == error::socket_type_not_supported) + return "Socket type not supported."; + } + if (category_ != error::get_system_category()) + return "asio error"; #if !defined(__sun) - if (*this == error::operation_aborted) + if (value_ == error::operation_aborted) return "Operation aborted."; #endif // !defined(__sun) - if (*this == error::service_not_found) - return "Service not found."; - if (*this == error::socket_type_not_supported) - return "Socket type not supported."; - if (category() != error::get_system_category()) - return "asio error"; -#if defined(__sun) || defined(__QNX__) +#if defined(__sun) || defined(__QNX__) || defined(__SYMBIAN32__) using namespace std; return strerror(value_); #elif defined(__MACH__) && defined(__APPLE__) \ @@ -102,4 +109,4 @@ inline std::string error_code::message() const #include "asio/detail/pop_options.hpp" -#endif // ASIO_ERROR_CODE_IPP +#endif // ASIO_IMPL_ERROR_CODE_IPP diff --git a/ext/asio/asio/impl/io_service.hpp b/ext/asio/asio/impl/io_service.hpp new file mode 100644 index 0000000000..f0c1730692 --- /dev/null +++ b/ext/asio/asio/impl/io_service.hpp @@ -0,0 +1,132 @@ +// +// impl/io_service.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_IO_SERVICE_HPP +#define ASIO_IMPL_IO_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/service_registry.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +inline Service& use_service(io_service& ios) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + (void)static_cast(&Service::id); + + return ios.service_registry_->template use_service(); +} + +template +inline void add_service(io_service& ios, Service* svc) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + (void)static_cast(&Service::id); + + ios.service_registry_->template add_service(svc); +} + +template +inline bool has_service(io_service& ios) +{ + // Check that Service meets the necessary type requirements. + (void)static_cast(static_cast(0)); + (void)static_cast(&Service::id); + + return ios.service_registry_->template has_service(); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_service.hpp" +#else +# include "asio/detail/task_io_service.hpp" +#endif + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +inline void io_service::dispatch(Handler handler) +{ + impl_.dispatch(handler); +} + +template +inline void io_service::post(Handler handler) +{ + impl_.post(handler); +} + +template +#if defined(GENERATING_DOCUMENTATION) +unspecified +#else +inline detail::wrapped_handler +#endif +io_service::wrap(Handler handler) +{ + return detail::wrapped_handler(*this, handler); +} + +inline io_service::work::work(asio::io_service& io_service) + : io_service_(io_service) +{ + io_service_.impl_.work_started(); +} + +inline io_service::work::work(const work& other) + : io_service_(other.io_service_) +{ + io_service_.impl_.work_started(); +} + +inline io_service::work::~work() +{ + io_service_.impl_.work_finished(); +} + +inline asio::io_service& io_service::work::io_service() +{ + return io_service_; +} + +inline asio::io_service& io_service::work::get_io_service() +{ + return io_service_; +} + +inline asio::io_service& io_service::service::io_service() +{ + return owner_; +} + +inline asio::io_service& io_service::service::get_io_service() +{ + return owner_; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_IO_SERVICE_HPP diff --git a/ext/asio/asio/impl/io_service.ipp b/ext/asio/asio/impl/io_service.ipp index c3fed3b820..eb19b8ffd0 100644 --- a/ext/asio/asio/impl/io_service.ipp +++ b/ext/asio/asio/impl/io_service.ipp @@ -1,26 +1,23 @@ // -// io_service.ipp -// ~~~~~~~~~~~~~~ +// impl/io_service.ipp +// ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef ASIO_IO_SERVICE_IPP -#define ASIO_IO_SERVICE_IPP +#ifndef ASIO_IMPL_IO_SERVICE_IPP +#define ASIO_IMPL_IO_SERVICE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" - +#include "asio/io_service.hpp" #include "asio/detail/service_registry.hpp" #include "asio/detail/throw_error.hpp" @@ -28,31 +25,32 @@ # include "asio/detail/win_iocp_io_service.hpp" #else # include "asio/detail/task_io_service.hpp" -# include "asio/detail/reactor.hpp" #endif +#include "asio/detail/push_options.hpp" + namespace asio { -inline io_service::io_service() +io_service::io_service() : service_registry_(new asio::detail::service_registry(*this)), impl_(service_registry_->use_service()) { impl_.init((std::numeric_limits::max)()); } -inline io_service::io_service(std::size_t concurrency_hint) +io_service::io_service(std::size_t concurrency_hint) : service_registry_(new asio::detail::service_registry(*this)), impl_(service_registry_->use_service()) { impl_.init(concurrency_hint); } -inline io_service::~io_service() +io_service::~io_service() { delete service_registry_; } -inline std::size_t io_service::run() +std::size_t io_service::run() { asio::error_code ec; std::size_t s = impl_.run(ec); @@ -60,12 +58,12 @@ inline std::size_t io_service::run() return s; } -inline std::size_t io_service::run(asio::error_code& ec) +std::size_t io_service::run(asio::error_code& ec) { return impl_.run(ec); } -inline std::size_t io_service::run_one() +std::size_t io_service::run_one() { asio::error_code ec; std::size_t s = impl_.run_one(ec); @@ -73,12 +71,12 @@ inline std::size_t io_service::run_one() return s; } -inline std::size_t io_service::run_one(asio::error_code& ec) +std::size_t io_service::run_one(asio::error_code& ec) { return impl_.run_one(ec); } -inline std::size_t io_service::poll() +std::size_t io_service::poll() { asio::error_code ec; std::size_t s = impl_.poll(ec); @@ -86,12 +84,12 @@ inline std::size_t io_service::poll() return s; } -inline std::size_t io_service::poll(asio::error_code& ec) +std::size_t io_service::poll(asio::error_code& ec) { return impl_.poll(ec); } -inline std::size_t io_service::poll_one() +std::size_t io_service::poll_one() { asio::error_code ec; std::size_t s = impl_.poll_one(ec); @@ -99,126 +97,43 @@ inline std::size_t io_service::poll_one() return s; } -inline std::size_t io_service::poll_one(asio::error_code& ec) +std::size_t io_service::poll_one(asio::error_code& ec) { return impl_.poll_one(ec); } -inline void io_service::stop() +void io_service::stop() { impl_.stop(); } -inline void io_service::reset() +void io_service::reset() { impl_.reset(); } -template -inline void io_service::dispatch(Handler handler) -{ - impl_.dispatch(handler); -} - -template -inline void io_service::post(Handler handler) -{ - impl_.post(handler); -} - -template -#if defined(GENERATING_DOCUMENTATION) -unspecified -#else -inline detail::wrapped_handler -#endif -io_service::wrap(Handler handler) -{ - return detail::wrapped_handler(*this, handler); -} - -inline io_service::work::work(asio::io_service& io_service) - : io_service_(io_service) -{ - io_service_.impl_.work_started(); -} - -inline io_service::work::work(const work& other) - : io_service_(other.io_service_) -{ - io_service_.impl_.work_started(); -} - -inline io_service::work::~work() -{ - io_service_.impl_.work_finished(); -} - -inline asio::io_service& io_service::work::io_service() -{ - return io_service_; -} - -inline asio::io_service& io_service::work::get_io_service() -{ - return io_service_; -} - -inline io_service::service::service(asio::io_service& owner) +io_service::service::service(asio::io_service& owner) : owner_(owner), next_(0) { } -inline io_service::service::~service() +io_service::service::~service() { } -inline asio::io_service& io_service::service::io_service() +service_already_exists::service_already_exists() + : std::logic_error("Service already exists.") { - return owner_; } -inline asio::io_service& io_service::service::get_io_service() +invalid_service_owner::invalid_service_owner() + : std::logic_error("Invalid service owner.") { - return owner_; -} - -template -inline Service& use_service(io_service& ios) -{ - // Check that Service meets the necessary type requirements. - (void)static_cast(static_cast(0)); - (void)static_cast(&Service::id); - - return ios.service_registry_->template use_service(); -} - -template -void add_service(io_service& ios, Service* svc) -{ - // Check that Service meets the necessary type requirements. - (void)static_cast(static_cast(0)); - (void)static_cast(&Service::id); - - if (&ios != &svc->io_service()) - boost::throw_exception(invalid_service_owner()); - if (!ios.service_registry_->template add_service(svc)) - boost::throw_exception(service_already_exists()); -} - -template -bool has_service(io_service& ios) -{ - // Check that Service meets the necessary type requirements. - (void)static_cast(static_cast(0)); - (void)static_cast(&Service::id); - - return ios.service_registry_->template has_service(); } } // namespace asio #include "asio/detail/pop_options.hpp" -#endif // ASIO_IO_SERVICE_IPP +#endif // ASIO_IMPL_IO_SERVICE_IPP diff --git a/ext/asio/asio/impl/read.hpp b/ext/asio/asio/impl/read.hpp new file mode 100644 index 0000000000..bd5d7b533e --- /dev/null +++ b/ext/asio/asio/impl/read.hpp @@ -0,0 +1,386 @@ +// +// impl/read.hpp +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_READ_HPP +#define ASIO_IMPL_READ_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + ec = asio::error_code(); + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = s.read_some(tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + } + return total_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#if !defined(BOOST_NO_IOSTREAM) + +template +std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + ec = asio::error_code(); + std::size_t total_transferred = 0; + std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + std::size_t bytes_available = read_size_helper(b, max_size); + while (bytes_available > 0) + { + std::size_t bytes_transferred = s.read_some(b.prepare(bytes_available), ec); + b.commit(bytes_transferred); + total_transferred += bytes_transferred; + max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + bytes_available = read_size_helper(b, max_size); + } + return total_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read(SyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read(s, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class read_op + : detail::base_from_completion_cond + { + public: + read_op(AsyncReadStream& stream, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffers_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + switch (start) + { + case 1: + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + for (;;) + { + stream_.async_read_some(buffers_, *this); + return; default: + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + if ((!ec && bytes_transferred == 0) + || buffers_.begin() == buffers_.end()) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncReadStream& stream_; + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> buffers_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + class read_op + : detail::base_from_completion_cond + { + public: + read_op(AsyncReadStream& stream, + const asio::mutable_buffers_1& buffers, + CompletionCondition completion_condition, + ReadHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffer_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t n = 0; + switch (start) + { + case 1: + n = this->check_for_completion(ec, total_transferred_); + for (;;) + { + stream_.async_read_some(asio::buffer( + buffer_ + total_transferred_, n), *this); + return; default: + total_transferred_ += bytes_transferred; + if ((!ec && bytes_transferred == 0) + || (n = this->check_for_completion(ec, total_transferred_)) == 0 + || total_transferred_ == asio::buffer_size(buffer_)) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncReadStream& stream_; + asio::mutable_buffer buffer_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler) +{ + detail::read_op( + s, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); +} + +template +inline void async_read(AsyncReadStream& s, const MutableBufferSequence& buffers, + ReadHandler handler) +{ + async_read(s, buffers, transfer_all(), handler); +} + +#if !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class read_streambuf_op + : detail::base_from_completion_cond + { + public: + read_streambuf_op(AsyncReadStream& stream, + basic_streambuf& streambuf, + CompletionCondition completion_condition, ReadHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + streambuf_(streambuf), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size, bytes_available; + switch (start) + { + case 1: + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = read_size_helper(streambuf_, max_size); + for (;;) + { + stream_.async_read_some(streambuf_.prepare(bytes_available), *this); + return; default: + total_transferred_ += bytes_transferred; + streambuf_.commit(bytes_transferred); + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = read_size_helper(streambuf_, max_size); + if ((!ec && bytes_transferred == 0) || bytes_available == 0) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_streambuf_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_streambuf_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_streambuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_read(AsyncReadStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, ReadHandler handler) +{ + detail::read_streambuf_op( + s, b, completion_condition, handler)( + asio::error_code(), 0, 1); +} + +template +inline void async_read(AsyncReadStream& s, + asio::basic_streambuf& b, ReadHandler handler) +{ + async_read(s, b, transfer_all(), handler); +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_READ_HPP diff --git a/ext/asio/asio/impl/read_at.hpp b/ext/asio/asio/impl/read_at.hpp new file mode 100644 index 0000000000..da57fe36aa --- /dev/null +++ b/ext/asio/asio/impl/read_at.hpp @@ -0,0 +1,410 @@ +// +// impl/read_at.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_READ_AT_HPP +#define ASIO_IMPL_READ_AT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + ec = asio::error_code(); + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = d.read_some_at( + offset + total_transferred, tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + } + return total_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#if !defined(BOOST_NO_IOSTREAM) + +template +std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + ec = asio::error_code(); + std::size_t total_transferred = 0; + std::size_t max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + std::size_t bytes_available = read_size_helper(b, max_size); + while (bytes_available > 0) + { + std::size_t bytes_transferred = d.read_some_at( + offset + total_transferred, b.prepare(bytes_available), ec); + b.commit(bytes_transferred); + total_transferred += bytes_transferred; + max_size = detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred)); + bytes_available = read_size_helper(b, max_size); + } + return total_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t read_at(SyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_at( + d, offset, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class read_at_op + : detail::base_from_completion_cond + { + public: + read_at_op(AsyncRandomAccessReadDevice& device, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + buffers_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + switch (start) + { + case 1: + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + for (;;) + { + device_.async_read_some_at( + offset_ + total_transferred_, buffers_, *this); + return; default: + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + if ((!ec && bytes_transferred == 0) + || buffers_.begin() == buffers_.end()) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncRandomAccessReadDevice& device_; + boost::uint64_t offset_; + asio::detail::consuming_buffers< + mutable_buffer, MutableBufferSequence> buffers_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + class read_at_op + : detail::base_from_completion_cond + { + public: + read_at_op(AsyncRandomAccessReadDevice& device, + boost::uint64_t offset, const asio::mutable_buffers_1& buffers, + CompletionCondition completion_condition, ReadHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + buffer_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t n = 0; + switch (start) + { + case 1: + n = this->check_for_completion(ec, total_transferred_); + for (;;) + { + device_.async_read_some_at(offset_ + total_transferred_, + asio::buffer(buffer_ + total_transferred_, n), *this); + return; default: + total_transferred_ += bytes_transferred; + if ((!ec && bytes_transferred == 0) + || (n = this->check_for_completion(ec, total_transferred_)) == 0 + || total_transferred_ == asio::buffer_size(buffer_)) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncRandomAccessReadDevice& device_; + boost::uint64_t offset_; + asio::mutable_buffer buffer_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_at_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_at_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_at_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + CompletionCondition completion_condition, ReadHandler handler) +{ + detail::read_at_op( + d, offset, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); +} + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, const MutableBufferSequence& buffers, + ReadHandler handler) +{ + async_read_at(d, offset, buffers, transfer_all(), handler); +} + +#if !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class read_at_streambuf_op + : detail::base_from_completion_cond + { + public: + read_at_streambuf_op(AsyncRandomAccessReadDevice& device, + boost::uint64_t offset, basic_streambuf& streambuf, + CompletionCondition completion_condition, ReadHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + streambuf_(streambuf), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t max_size, bytes_available; + switch (start) + { + case 1: + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = read_size_helper(streambuf_, max_size); + for (;;) + { + device_.async_read_some_at(offset_ + total_transferred_, + streambuf_.prepare(bytes_available), *this); + return; default: + total_transferred_ += bytes_transferred; + streambuf_.commit(bytes_transferred); + max_size = this->check_for_completion(ec, total_transferred_); + bytes_available = read_size_helper(streambuf_, max_size); + if ((!ec && bytes_transferred == 0) || bytes_available == 0) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncRandomAccessReadDevice& device_; + boost::uint64_t offset_; + asio::basic_streambuf& streambuf_; + std::size_t total_transferred_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_at_streambuf_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_at_streambuf_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_at_streambuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, ReadHandler handler) +{ + detail::read_at_streambuf_op( + d, offset, b, completion_condition, handler)( + asio::error_code(), 0, 1); +} + +template +inline void async_read_at(AsyncRandomAccessReadDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + ReadHandler handler) +{ + async_read_at(d, offset, b, transfer_all(), handler); +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_READ_AT_HPP diff --git a/ext/asio/asio/impl/read_until.hpp b/ext/asio/asio/impl/read_until.hpp new file mode 100644 index 0000000000..ceacca7fdd --- /dev/null +++ b/ext/asio/asio/impl/read_until.hpp @@ -0,0 +1,902 @@ +// +// impl/read_until.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_READ_UNTIL_HPP +#define ASIO_IMPL_READ_UNTIL_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include +#include "asio/buffer.hpp" +#include "asio/buffers_iterator.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, delim, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, char delim, + asio::error_code& ec) +{ + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = b.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position; + iterator end = iterator::end(buffers); + + // Look for a match. + iterator iter = std::find(start, end, delim); + if (iter != end) + { + // Found a match. We're done. + ec = asio::error_code(); + return iter - begin + 1; + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = read_size_helper(b, 65536); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, delim, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +namespace detail +{ + // Algorithm that finds a subsequence of equal values in a sequence. Returns + // (iterator,true) if a full match was found, in which case the iterator + // points to the beginning of the match. Returns (iterator,false) if a + // partial match was found at the end of the first sequence, in which case + // the iterator points to the beginning of the partial match. Returns + // (last1,false) if no full or partial match was found. + template + std::pair partial_search( + Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) + { + for (Iterator1 iter1 = first1; iter1 != last1; ++iter1) + { + Iterator1 test_iter1 = iter1; + Iterator2 test_iter2 = first2; + for (;; ++test_iter1, ++test_iter2) + { + if (test_iter2 == last2) + return std::make_pair(iter1, true); + if (test_iter1 == last1) + { + if (test_iter2 != first2) + return std::make_pair(iter1, false); + else + break; + } + if (*test_iter1 != *test_iter2) + break; + } + } + return std::make_pair(last1, false); + } +} // namespace detail + +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim, + asio::error_code& ec) +{ + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = b.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position; + iterator end = iterator::end(buffers); + + // Look for a match. + std::pair result = detail::partial_search( + start, end, delim.begin(), delim.end()); + if (result.first != end) + { + if (result.second) + { + // Full match. We're done. + ec = asio::error_code(); + return result.first - begin + delim.length(); + } + else + { + // Partial match. Next search needs to start from beginning of match. + search_position = result.first - begin; + } + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = read_size_helper(b, 65536); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, expr, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + asio::error_code& ec) +{ + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = b.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position; + iterator end = iterator::end(buffers); + + // Look for a match. + boost::match_results >::allocator_type> + match_results; + if (regex_search(start, end, match_results, expr, + boost::match_default | boost::match_partial)) + { + if (match_results[0].matched) + { + // Full match. We're done. + ec = asio::error_code(); + return match_results[0].second - begin; + } + else + { + // Partial match. Next search needs to start from beginning of match. + search_position = match_results[0].first - begin; + } + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = read_size_helper(b, 65536); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +template +std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, asio::error_code& ec, + typename boost::enable_if >::type*) +{ + std::size_t search_position = 0; + for (;;) + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = b.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position; + iterator end = iterator::end(buffers); + + // Look for a match. + std::pair result = match_condition(start, end); + if (result.second) + { + // Full match. We're done. + ec = asio::error_code(); + return result.first - begin; + } + else if (result.first != end) + { + // Partial match. Next search needs to start from beginning of match. + search_position = result.first - begin; + } + else + { + // No match. Next search can start with the new data. + search_position = end - begin; + } + + // Check if buffer is full. + if (b.size() == b.max_size()) + { + ec = error::not_found; + return 0; + } + + // Need more data. + std::size_t bytes_to_read = read_size_helper(b, 65536); + b.commit(s.read_some(b.prepare(bytes_to_read), ec)); + if (ec) + return 0; + } +} + +template +inline std::size_t read_until(SyncReadStream& s, + asio::basic_streambuf& b, MatchCondition match_condition, + typename boost::enable_if >::type*) +{ + asio::error_code ec; + std::size_t bytes_transferred = read_until(s, b, match_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +namespace detail +{ + template + class read_until_delim_op + { + public: + read_until_delim_op(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, + char delim, ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + delim_(delim), + search_position_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position_; + iterator end = iterator::end(buffers); + + // Look for a match. + iterator iter = std::find(start, end, delim_); + if (iter != end) + { + // Found a match. We're done. + search_position_ = iter - begin + 1; + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (streambuf_.size() == streambuf_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + // Next search can start with the new data. + search_position_ = end - begin; + bytes_to_read = read_size_helper(streambuf_, 65536); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); + return; default: + streambuf_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + char delim_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, char delim, ReadHandler handler) +{ + detail::read_until_delim_op< + AsyncReadStream, Allocator, ReadHandler>( + s, b, delim, handler)( + asio::error_code(), 0, 1); +} + +namespace detail +{ + template + class read_until_delim_string_op + { + public: + read_until_delim_string_op(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, + const std::string& delim, ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + delim_(delim), + search_position_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position_; + iterator end = iterator::end(buffers); + + // Look for a match. + std::pair result = detail::partial_search( + start, end, delim_.begin(), delim_.end()); + if (result.first != end && result.second) + { + // Full match. We're done. + search_position_ = result.first - begin + delim_.length(); + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (streambuf_.size() == streambuf_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + if (result.first != end) + { + // Partial match. Next search needs to start from beginning of + // match. + search_position_ = result.first - begin; + } + else + { + // Next search can start with the new data. + search_position_ = end - begin; + } + + bytes_to_read = read_size_helper(streambuf_, 65536); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); + return; default: + streambuf_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + std::string delim_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_delim_string_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_delim_string_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_delim_string_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const std::string& delim, + ReadHandler handler) +{ + detail::read_until_delim_string_op< + AsyncReadStream, Allocator, ReadHandler>( + s, b, delim, handler)( + asio::error_code(), 0, 1); +} + +namespace detail +{ + template + class read_until_expr_op + { + public: + read_until_expr_op(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, + const boost::regex& expr, ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + expr_(expr), + search_position_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position_; + iterator end = iterator::end(buffers); + + // Look for a match. + boost::match_results >::allocator_type> + match_results; + bool match = regex_search(start, end, match_results, expr_, + boost::match_default | boost::match_partial); + if (match && match_results[0].matched) + { + // Full match. We're done. + search_position_ = match_results[0].second - begin; + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (streambuf_.size() == streambuf_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + if (match) + { + // Partial match. Next search needs to start from beginning of + // match. + search_position_ = match_results[0].first - begin; + } + else + { + // Next search can start with the new data. + search_position_ = end - begin; + } + + bytes_to_read = read_size_helper(streambuf_, 65536); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); + return; default: + streambuf_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + RegEx expr_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_expr_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_expr_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_expr_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, const boost::regex& expr, + ReadHandler handler) +{ + detail::read_until_expr_op( + s, b, expr, handler)( + asio::error_code(), 0, 1); +} + +namespace detail +{ + template + class read_until_match_op + { + public: + read_until_match_op(AsyncReadStream& stream, + asio::basic_streambuf& streambuf, + MatchCondition match_condition, ReadHandler handler) + : stream_(stream), + streambuf_(streambuf), + match_condition_(match_condition), + search_position_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + const std::size_t not_found = (std::numeric_limits::max)(); + std::size_t bytes_to_read; + switch (start) + { + case 1: + for (;;) + { + { + // Determine the range of the data to be searched. + typedef typename asio::basic_streambuf< + Allocator>::const_buffers_type const_buffers_type; + typedef asio::buffers_iterator iterator; + const_buffers_type buffers = streambuf_.data(); + iterator begin = iterator::begin(buffers); + iterator start = begin + search_position_; + iterator end = iterator::end(buffers); + + // Look for a match. + std::pair result = match_condition_(start, end); + if (result.second) + { + // Full match. We're done. + search_position_ = result.first - begin; + bytes_to_read = 0; + } + + // No match yet. Check if buffer is full. + else if (streambuf_.size() == streambuf_.max_size()) + { + search_position_ = not_found; + bytes_to_read = 0; + } + + // Need to read some more data. + else + { + if (result.first != end) + { + // Partial match. Next search needs to start from beginning of + // match. + search_position_ = result.first - begin; + } + else + { + // Next search can start with the new data. + search_position_ = end - begin; + } + + bytes_to_read = read_size_helper(streambuf_, 65536); + } + } + + // Check if we're done. + if (!start && bytes_to_read == 0) + break; + + // Start a new asynchronous read operation to obtain more data. + stream_.async_read_some(streambuf_.prepare(bytes_to_read), *this); + return; default: + streambuf_.commit(bytes_transferred); + if (ec || bytes_transferred == 0) + break; + } + + const asio::error_code result_ec = + (search_position_ == not_found) + ? error::not_found : ec; + + const std::size_t result_n = + (ec || search_position_ == not_found) + ? 0 : search_position_; + + handler_(result_ec, result_n); + } + } + + //private: + AsyncReadStream& stream_; + asio::basic_streambuf& streambuf_; + MatchCondition match_condition_; + std::size_t search_position_; + ReadHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + read_until_match_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + read_until_match_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + read_until_match_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +void async_read_until(AsyncReadStream& s, + asio::basic_streambuf& b, + MatchCondition match_condition, ReadHandler handler, + typename boost::enable_if >::type*) +{ + detail::read_until_match_op< + AsyncReadStream, Allocator, MatchCondition, ReadHandler>( + s, b, match_condition, handler)( + asio::error_code(), 0, 1); +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_READ_UNTIL_HPP diff --git a/ext/asio/asio/impl/serial_port_base.hpp b/ext/asio/asio/impl/serial_port_base.hpp new file mode 100644 index 0000000000..758e65251e --- /dev/null +++ b/ext/asio/asio/impl/serial_port_base.hpp @@ -0,0 +1,59 @@ +// +// impl/serial_port_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SERIAL_PORT_BASE_HPP +#define ASIO_IMPL_SERIAL_PORT_BASE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/push_options.hpp" + +namespace asio { + +inline serial_port_base::baud_rate::baud_rate(unsigned int rate) + : value_(rate) +{ +} + +inline unsigned int serial_port_base::baud_rate::value() const +{ + return value_; +} + +inline serial_port_base::flow_control::type +serial_port_base::flow_control::value() const +{ + return value_; +} + +inline serial_port_base::parity::type serial_port_base::parity::value() const +{ + return value_; +} + +inline serial_port_base::stop_bits::type +serial_port_base::stop_bits::value() const +{ + return value_; +} + +inline unsigned int serial_port_base::character_size::value() const +{ + return value_; +} + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_SERIAL_PORT_BASE_HPP diff --git a/ext/asio/asio/impl/serial_port_base.ipp b/ext/asio/asio/impl/serial_port_base.ipp index 64ab6a45a0..4d3204dab3 100644 --- a/ext/asio/asio/impl/serial_port_base.ipp +++ b/ext/asio/asio/impl/serial_port_base.ipp @@ -1,40 +1,43 @@ // -// serial_port_base.ipp -// ~~~~~~~~~~~~~~~~~~~~ +// impl/serial_port_base.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef ASIO_SERIAL_PORT_BASE_IPP -#define ASIO_SERIAL_PORT_BASE_IPP +#ifndef ASIO_IMPL_SERIAL_PORT_BASE_IPP +#define ASIO_IMPL_SERIAL_PORT_BASE_IPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_SERIAL_PORT) + +#include +#include +#include "asio/error.hpp" +#include "asio/serial_port_base.hpp" + +#if defined(GENERATING_DOCUMENTATION) +# define ASIO_OPTION_STORAGE implementation_defined +#elif defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# define ASIO_OPTION_STORAGE DCB +#else +# define ASIO_OPTION_STORAGE termios +#endif #include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" namespace asio { -inline serial_port_base::baud_rate::baud_rate(unsigned int rate) - : value_(rate) -{ -} - -inline unsigned int serial_port_base::baud_rate::value() const -{ - return value_; -} - -inline asio::error_code serial_port_base::baud_rate::store( +asio::error_code serial_port_base::baud_rate::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -122,7 +125,7 @@ inline asio::error_code serial_port_base::baud_rate::store( return ec; } -inline asio::error_code serial_port_base::baud_rate::load( +asio::error_code serial_port_base::baud_rate::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -204,7 +207,7 @@ inline asio::error_code serial_port_base::baud_rate::load( return ec; } -inline serial_port_base::flow_control::flow_control( +serial_port_base::flow_control::flow_control( serial_port_base::flow_control::type t) : value_(t) { @@ -215,13 +218,7 @@ inline serial_port_base::flow_control::flow_control( } } -inline serial_port_base::flow_control::type -serial_port_base::flow_control::value() const -{ - return value_; -} - -inline asio::error_code serial_port_base::flow_control::store( +asio::error_code serial_port_base::flow_control::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -255,12 +252,16 @@ inline asio::error_code serial_port_base::flow_control::store( storage.c_iflag &= ~(IXOFF | IXON); # if defined(_BSD_SOURCE) storage.c_cflag &= ~CRTSCTS; +# elif defined(__QNXNTO__) + storage.c_cflag &= ~(IHFLOW | OHFLOW); # endif break; case software: storage.c_iflag |= IXOFF | IXON; # if defined(_BSD_SOURCE) storage.c_cflag &= ~CRTSCTS; +# elif defined(__QNXNTO__) + storage.c_cflag &= ~(IHFLOW | OHFLOW); # endif break; case hardware: @@ -268,6 +269,10 @@ inline asio::error_code serial_port_base::flow_control::store( storage.c_iflag &= ~(IXOFF | IXON); storage.c_cflag |= CRTSCTS; break; +# elif defined(__QNXNTO__) + storage.c_iflag &= ~(IXOFF | IXON); + storage.c_cflag |= (IHFLOW | OHFLOW); + break; # else ec = asio::error::operation_not_supported; return ec; @@ -280,7 +285,7 @@ inline asio::error_code serial_port_base::flow_control::store( return ec; } -inline asio::error_code serial_port_base::flow_control::load( +asio::error_code serial_port_base::flow_control::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -306,6 +311,11 @@ inline asio::error_code serial_port_base::flow_control::load( { value_ = hardware; } +# elif defined(__QNXNTO__) + else if (storage.c_cflag & IHFLOW && storage.c_cflag & OHFLOW) + { + value_ = hardware; + } # endif else { @@ -316,7 +326,7 @@ inline asio::error_code serial_port_base::flow_control::load( return ec; } -inline serial_port_base::parity::parity(serial_port_base::parity::type t) +serial_port_base::parity::parity(serial_port_base::parity::type t) : value_(t) { if (t != none && t != odd && t != even) @@ -326,12 +336,7 @@ inline serial_port_base::parity::parity(serial_port_base::parity::type t) } } -inline serial_port_base::parity::type serial_port_base::parity::value() const -{ - return value_; -} - -inline asio::error_code serial_port_base::parity::store( +asio::error_code serial_port_base::parity::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -378,7 +383,7 @@ inline asio::error_code serial_port_base::parity::store( return ec; } -inline asio::error_code serial_port_base::parity::load( +asio::error_code serial_port_base::parity::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -415,7 +420,7 @@ inline asio::error_code serial_port_base::parity::load( return ec; } -inline serial_port_base::stop_bits::stop_bits( +serial_port_base::stop_bits::stop_bits( serial_port_base::stop_bits::type t) : value_(t) { @@ -426,13 +431,7 @@ inline serial_port_base::stop_bits::stop_bits( } } -inline serial_port_base::stop_bits::type -serial_port_base::stop_bits::value() const -{ - return value_; -} - -inline asio::error_code serial_port_base::stop_bits::store( +asio::error_code serial_port_base::stop_bits::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -468,7 +467,7 @@ inline asio::error_code serial_port_base::stop_bits::store( return ec; } -inline asio::error_code serial_port_base::stop_bits::load( +asio::error_code serial_port_base::stop_bits::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -495,7 +494,7 @@ inline asio::error_code serial_port_base::stop_bits::load( return ec; } -inline serial_port_base::character_size::character_size(unsigned int t) +serial_port_base::character_size::character_size(unsigned int t) : value_(t) { if (t < 5 || t > 8) @@ -505,12 +504,7 @@ inline serial_port_base::character_size::character_size(unsigned int t) } } -inline unsigned int serial_port_base::character_size::value() const -{ - return value_; -} - -inline asio::error_code serial_port_base::character_size::store( +asio::error_code serial_port_base::character_size::store( ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -530,7 +524,7 @@ inline asio::error_code serial_port_base::character_size::store( return ec; } -inline asio::error_code serial_port_base::character_size::load( +asio::error_code serial_port_base::character_size::load( const ASIO_OPTION_STORAGE& storage, asio::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) @@ -554,4 +548,8 @@ inline asio::error_code serial_port_base::character_size::load( #include "asio/detail/pop_options.hpp" -#endif // ASIO_SERIAL_PORT_BASE_IPP +#undef ASIO_OPTION_STORAGE + +#endif // defined(ASIO_HAS_SERIAL_PORT) + +#endif // ASIO_IMPL_SERIAL_PORT_BASE_IPP diff --git a/ext/asio/asio/impl/src.cpp b/ext/asio/asio/impl/src.cpp new file mode 100644 index 0000000000..1dac400b66 --- /dev/null +++ b/ext/asio/asio/impl/src.cpp @@ -0,0 +1,25 @@ +// +// impl/src.cpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#if defined(_MSC_VER) \ + || defined(__BORLANDC__) \ + || defined(__DMC__) +# pragma message ( \ + "This file is deprecated. " \ + "Please #include instead.") +#elif defined(__GNUC__) \ + || defined(__HP_aCC) \ + || defined(__SUNPRO_CC) \ + || defined(__IBMCPP__) +# warning "This file is deprecated." +# warning "Please #include instead." +#endif + +#include "asio/impl/src.hpp" diff --git a/ext/asio/asio/impl/src.hpp b/ext/asio/asio/impl/src.hpp new file mode 100644 index 0000000000..b37eabddb8 --- /dev/null +++ b/ext/asio/asio/impl/src.hpp @@ -0,0 +1,65 @@ +// +// impl/src.hpp +// ~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_SRC_HPP +#define ASIO_IMPL_SRC_HPP + +#define ASIO_SOURCE + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HEADER_ONLY) +# error Do not compile Asio library source with ASIO_HEADER_ONLY defined +#endif + +#include "asio/impl/error.ipp" +#include "asio/impl/error_code.ipp" +#include "asio/impl/io_service.ipp" +#include "asio/impl/serial_port_base.ipp" +#include "asio/detail/impl/descriptor_ops.ipp" +#include "asio/detail/impl/dev_poll_reactor.ipp" +#include "asio/detail/impl/epoll_reactor.ipp" +#include "asio/detail/impl/eventfd_select_interrupter.ipp" +#include "asio/detail/impl/kqueue_reactor.ipp" +#include "asio/detail/impl/pipe_select_interrupter.ipp" +#include "asio/detail/impl/posix_event.ipp" +#include "asio/detail/impl/posix_mutex.ipp" +#include "asio/detail/impl/posix_thread.ipp" +#include "asio/detail/impl/posix_tss_ptr.ipp" +#include "asio/detail/impl/reactive_descriptor_service.ipp" +#include "asio/detail/impl/reactive_serial_port_service.ipp" +#include "asio/detail/impl/reactive_socket_service_base.ipp" +#include "asio/detail/impl/resolver_service_base.ipp" +#include "asio/detail/impl/select_reactor.ipp" +#include "asio/detail/impl/service_registry.ipp" +#include "asio/detail/impl/socket_ops.ipp" +#include "asio/detail/impl/socket_select_interrupter.ipp" +#include "asio/detail/impl/strand_service.ipp" +#include "asio/detail/impl/task_io_service.ipp" +#include "asio/detail/impl/throw_error.ipp" +#include "asio/detail/impl/timer_queue.ipp" +#include "asio/detail/impl/timer_queue_set.ipp" +#include "asio/detail/impl/win_iocp_handle_service.ipp" +#include "asio/detail/impl/win_iocp_io_service.ipp" +#include "asio/detail/impl/win_iocp_serial_port_service.ipp" +#include "asio/detail/impl/win_iocp_socket_service_base.ipp" +#include "asio/detail/impl/win_event.ipp" +#include "asio/detail/impl/win_mutex.ipp" +#include "asio/detail/impl/win_thread.ipp" +#include "asio/detail/impl/win_tss_ptr.ipp" +#include "asio/detail/impl/winsock_init.ipp" +#include "asio/ip/impl/address.ipp" +#include "asio/ip/impl/address_v4.ipp" +#include "asio/ip/impl/address_v6.ipp" +#include "asio/ip/impl/host_name.ipp" +#include "asio/ip/detail/impl/endpoint.ipp" +#include "asio/local/detail/impl/endpoint.ipp" + +#endif // ASIO_IMPL_SRC_HPP diff --git a/ext/asio/asio/impl/write.hpp b/ext/asio/asio/impl/write.hpp new file mode 100644 index 0000000000..91cb65ecbf --- /dev/null +++ b/ext/asio/asio/impl/write.hpp @@ -0,0 +1,396 @@ +// +// impl/write.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_WRITE_HPP +#define ASIO_IMPL_WRITE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + ec = asio::error_code(); + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = s.write_some(tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + } + return total_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#if !defined(BOOST_NO_IOSTREAM) + +template +std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + std::size_t bytes_transferred = write(s, b.data(), completion_condition, ec); + b.consume(bytes_transferred); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write(SyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write(s, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class write_op + : detail::base_from_completion_cond + { + public: + write_op(AsyncWriteStream& stream, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffers_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + switch (start) + { + case 1: + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + for (;;) + { + stream_.async_write_some(buffers_, *this); + return; default: + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + if ((!ec && bytes_transferred == 0) + || buffers_.begin() == buffers_.end()) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncWriteStream& stream_; + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> buffers_; + std::size_t total_transferred_; + WriteHandler handler_; + }; + + template + class write_op + : detail::base_from_completion_cond + { + public: + write_op(AsyncWriteStream& stream, + const asio::mutable_buffers_1& buffers, + CompletionCondition completion_condition, + WriteHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffer_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t n = 0; + switch (start) + { + case 1: + n = this->check_for_completion(ec, total_transferred_); + for (;;) + { + stream_.async_write_some(asio::buffer( + buffer_ + total_transferred_, n), *this); + return; default: + total_transferred_ += bytes_transferred; + if ((!ec && bytes_transferred == 0) + || (n = this->check_for_completion(ec, total_transferred_)) == 0 + || total_transferred_ == asio::buffer_size(buffer_)) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncWriteStream& stream_; + asio::mutable_buffer buffer_; + std::size_t total_transferred_; + WriteHandler handler_; + }; + + template + class write_op + : detail::base_from_completion_cond + { + public: + write_op(AsyncWriteStream& stream, + const asio::const_buffers_1& buffers, + CompletionCondition completion_condition, + WriteHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + stream_(stream), + buffer_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t n = 0; + switch (start) + { + case 1: + n = this->check_for_completion(ec, total_transferred_); + for (;;) + { + stream_.async_write_some(asio::buffer( + buffer_ + total_transferred_, n), *this); + return; default: + total_transferred_ += bytes_transferred; + if ((!ec && bytes_transferred == 0) + || (n = this->check_for_completion(ec, total_transferred_)) == 0 + || total_transferred_ == asio::buffer_size(buffer_)) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncWriteStream& stream_; + asio::const_buffer buffer_; + std::size_t total_transferred_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler) +{ + detail::write_op( + s, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); +} + +template +inline void async_write(AsyncWriteStream& s, const ConstBufferSequence& buffers, + WriteHandler handler) +{ + async_write(s, buffers, transfer_all(), handler); +} + +#if !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class write_streambuf_handler + { + public: + write_streambuf_handler(asio::basic_streambuf& streambuf, + WriteHandler handler) + : streambuf_(streambuf), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + const std::size_t bytes_transferred) + { + streambuf_.consume(bytes_transferred); + handler_(ec, bytes_transferred); + } + + //private: + asio::basic_streambuf& streambuf_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_streambuf_handler* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_streambuf_handler* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_streambuf_handler* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_write(AsyncWriteStream& s, + asio::basic_streambuf& b, + CompletionCondition completion_condition, WriteHandler handler) +{ + async_write(s, b.data(), completion_condition, + detail::write_streambuf_handler< + AsyncWriteStream, Allocator, WriteHandler>(b, handler)); +} + +template +inline void async_write(AsyncWriteStream& s, + asio::basic_streambuf& b, WriteHandler handler) +{ + async_write(s, b, transfer_all(), handler); +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_WRITE_HPP diff --git a/ext/asio/asio/impl/write_at.hpp b/ext/asio/asio/impl/write_at.hpp new file mode 100644 index 0000000000..1de6800801 --- /dev/null +++ b/ext/asio/asio/impl/write_at.hpp @@ -0,0 +1,417 @@ +// +// impl/write_at.hpp +// ~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IMPL_WRITE_AT_HPP +#define ASIO_IMPL_WRITE_AT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/buffer.hpp" +#include "asio/completion_condition.hpp" +#include "asio/detail/base_from_completion_cond.hpp" +#include "asio/detail/bind_handler.hpp" +#include "asio/detail/consuming_buffers.hpp" +#include "asio/detail/handler_alloc_helpers.hpp" +#include "asio/detail/handler_invoke_helpers.hpp" +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { + +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, asio::error_code& ec) +{ + ec = asio::error_code(); + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> tmp(buffers); + std::size_t total_transferred = 0; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + while (tmp.begin() != tmp.end()) + { + std::size_t bytes_transferred = d.write_some_at( + offset + total_transferred, tmp, ec); + tmp.consume(bytes_transferred); + total_transferred += bytes_transferred; + tmp.prepare(detail::adapt_completion_condition_result( + completion_condition(ec, total_transferred))); + } + return total_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, buffers, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, buffers, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#if !defined(BOOST_NO_IOSTREAM) + +template +std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, asio::error_code& ec) +{ + std::size_t bytes_transferred = write_at( + d, offset, b.data(), completion_condition, ec); + b.consume(bytes_transferred); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +template +inline std::size_t write_at(SyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition) +{ + asio::error_code ec; + std::size_t bytes_transferred = write_at( + d, offset, b, completion_condition, ec); + asio::detail::throw_error(ec); + return bytes_transferred; +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class write_at_op + : detail::base_from_completion_cond + { + public: + write_at_op(AsyncRandomAccessWriteDevice& device, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + buffers_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + switch (start) + { + case 1: + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + for (;;) + { + device_.async_write_some_at( + offset_ + total_transferred_, buffers_, *this); + return; default: + total_transferred_ += bytes_transferred; + buffers_.consume(bytes_transferred); + buffers_.prepare(this->check_for_completion(ec, total_transferred_)); + if ((!ec && bytes_transferred == 0) + || buffers_.begin() == buffers_.end()) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncRandomAccessWriteDevice& device_; + boost::uint64_t offset_; + asio::detail::consuming_buffers< + const_buffer, ConstBufferSequence> buffers_; + std::size_t total_transferred_; + WriteHandler handler_; + }; + + template + class write_at_op + : detail::base_from_completion_cond + { + public: + write_at_op(AsyncRandomAccessWriteDevice& device, + boost::uint64_t offset, const asio::mutable_buffers_1& buffers, + CompletionCondition completion_condition, + WriteHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + buffer_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t n = 0; + switch (start) + { + case 1: + n = this->check_for_completion(ec, total_transferred_); + for (;;) + { + device_.async_write_some_at(offset_ + total_transferred_, + asio::buffer(buffer_ + total_transferred_, n), *this); + return; default: + total_transferred_ += bytes_transferred; + if ((!ec && bytes_transferred == 0) + || (n = this->check_for_completion(ec, total_transferred_)) == 0 + || total_transferred_ == asio::buffer_size(buffer_)) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncRandomAccessWriteDevice& device_; + boost::uint64_t offset_; + asio::mutable_buffer buffer_; + std::size_t total_transferred_; + WriteHandler handler_; + }; + + template + class write_at_op + : detail::base_from_completion_cond + { + public: + write_at_op(AsyncRandomAccessWriteDevice& device, + boost::uint64_t offset, const asio::const_buffers_1& buffers, + CompletionCondition completion_condition, + WriteHandler handler) + : detail::base_from_completion_cond< + CompletionCondition>(completion_condition), + device_(device), + offset_(offset), + buffer_(buffers), + total_transferred_(0), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + std::size_t bytes_transferred, int start = 0) + { + std::size_t n = 0; + switch (start) + { + case 1: + n = this->check_for_completion(ec, total_transferred_); + for (;;) + { + device_.async_write_some_at(offset_ + total_transferred_, + asio::buffer(buffer_ + total_transferred_, n), *this); + return; default: + total_transferred_ += bytes_transferred; + if ((!ec && bytes_transferred == 0) + || (n = this->check_for_completion(ec, total_transferred_)) == 0 + || total_transferred_ == asio::buffer_size(buffer_)) + break; + } + + handler_(ec, static_cast(total_transferred_)); + } + } + + //private: + AsyncRandomAccessWriteDevice& device_; + boost::uint64_t offset_; + asio::const_buffer buffer_; + std::size_t total_transferred_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_at_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_at_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_at_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + CompletionCondition completion_condition, WriteHandler handler) +{ + detail::write_at_op( + d, offset, buffers, completion_condition, handler)( + asio::error_code(), 0, 1); +} + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, const ConstBufferSequence& buffers, + WriteHandler handler) +{ + async_write_at(d, offset, buffers, transfer_all(), handler); +} + +#if !defined(BOOST_NO_IOSTREAM) + +namespace detail +{ + template + class write_at_streambuf_op + { + public: + write_at_streambuf_op( + asio::basic_streambuf& streambuf, + WriteHandler handler) + : streambuf_(streambuf), + handler_(handler) + { + } + + void operator()(const asio::error_code& ec, + const std::size_t bytes_transferred) + { + streambuf_.consume(bytes_transferred); + handler_(ec, bytes_transferred); + } + + //private: + asio::basic_streambuf& streambuf_; + WriteHandler handler_; + }; + + template + inline void* asio_handler_allocate(std::size_t size, + write_at_streambuf_op* this_handler) + { + return asio_handler_alloc_helpers::allocate( + size, this_handler->handler_); + } + + template + inline void asio_handler_deallocate(void* pointer, std::size_t size, + write_at_streambuf_op* this_handler) + { + asio_handler_alloc_helpers::deallocate( + pointer, size, this_handler->handler_); + } + + template + inline void asio_handler_invoke(const Function& function, + write_at_streambuf_op* this_handler) + { + asio_handler_invoke_helpers::invoke( + function, this_handler->handler_); + } +} // namespace detail + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + CompletionCondition completion_condition, WriteHandler handler) +{ + async_write_at(d, offset, b.data(), completion_condition, + detail::write_at_streambuf_op< + AsyncRandomAccessWriteDevice, Allocator, WriteHandler>(b, handler)); +} + +template +inline void async_write_at(AsyncRandomAccessWriteDevice& d, + boost::uint64_t offset, asio::basic_streambuf& b, + WriteHandler handler) +{ + async_write_at(d, offset, b, transfer_all(), handler); +} + +#endif // !defined(BOOST_NO_IOSTREAM) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IMPL_WRITE_AT_HPP diff --git a/ext/asio/asio/io_service.hpp b/ext/asio/asio/io_service.hpp index 62d9e54f9c..4fc3e05fb5 100644 --- a/ext/asio/asio/io_service.hpp +++ b/ext/asio/asio/io_service.hpp @@ -2,7 +2,7 @@ // io_service.hpp // ~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,25 +15,29 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error_code.hpp" #include "asio/detail/noncopyable.hpp" -#include "asio/detail/reactor_fwd.hpp" #include "asio/detail/service_registry_fwd.hpp" -#include "asio/detail/signal_init.hpp" -#include "asio/detail/task_io_service_fwd.hpp" -#include "asio/detail/win_iocp_io_service_fwd.hpp" -#include "asio/detail/winsock_init.hpp" #include "asio/detail/wrapped_handler.hpp" +#include "asio/error_code.hpp" + +#if defined(ASIO_HAS_IOCP) +# include "asio/detail/win_iocp_io_service_fwd.hpp" +#else +# include "asio/detail/task_io_service_fwd.hpp" +#endif + +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# include "asio/detail/winsock_init.hpp" +#elif defined(__sun) || defined(__QNX__) || defined(__hpux) || defined(_AIX) \ + || defined(__osf__) +# include "asio/detail/signal_init.hpp" +#endif + +#include "asio/detail/push_options.hpp" namespace asio { @@ -45,7 +49,7 @@ template bool has_service(io_service& ios); #if defined(ASIO_HAS_IOCP) namespace detail { typedef win_iocp_io_service io_service_impl; } #else -namespace detail { typedef task_io_service io_service_impl; } +namespace detail { typedef task_io_service io_service_impl; } #endif /// Provides core I/O functionality. @@ -196,7 +200,7 @@ public: class strand; /// Constructor. - io_service(); + ASIO_DECL io_service(); /// Constructor. /** @@ -205,7 +209,7 @@ public: * @param concurrency_hint A suggestion to the implementation on how many * threads it should allow to run simultaneously. */ - explicit io_service(std::size_t concurrency_hint); + ASIO_DECL explicit io_service(std::size_t concurrency_hint); /// Destructor. /** @@ -239,7 +243,7 @@ public: * destructor defined above destroys all handlers, causing all @c shared_ptr * references to all connection objects to be destroyed. */ - ~io_service(); + ASIO_DECL ~io_service(); /// Run the io_service object's event processing loop. /** @@ -265,7 +269,7 @@ public: * The poll() function may also be used to dispatch ready handlers, but * without blocking. */ - std::size_t run(); + ASIO_DECL std::size_t run(); /// Run the io_service object's event processing loop. /** @@ -291,7 +295,7 @@ public: * The poll() function may also be used to dispatch ready handlers, but * without blocking. */ - std::size_t run(asio::error_code& ec); + ASIO_DECL std::size_t run(asio::error_code& ec); /// Run the io_service object's event processing loop to execute at most one /// handler. @@ -303,7 +307,7 @@ public: * * @throws asio::system_error Thrown on failure. */ - std::size_t run_one(); + ASIO_DECL std::size_t run_one(); /// Run the io_service object's event processing loop to execute at most one /// handler. @@ -315,7 +319,7 @@ public: * * @return The number of handlers that were executed. */ - std::size_t run_one(asio::error_code& ec); + ASIO_DECL std::size_t run_one(asio::error_code& ec); /// Run the io_service object's event processing loop to execute ready /// handlers. @@ -327,7 +331,7 @@ public: * * @throws asio::system_error Thrown on failure. */ - std::size_t poll(); + ASIO_DECL std::size_t poll(); /// Run the io_service object's event processing loop to execute ready /// handlers. @@ -339,7 +343,7 @@ public: * * @return The number of handlers that were executed. */ - std::size_t poll(asio::error_code& ec); + ASIO_DECL std::size_t poll(asio::error_code& ec); /// Run the io_service object's event processing loop to execute one ready /// handler. @@ -351,7 +355,7 @@ public: * * @throws asio::system_error Thrown on failure. */ - std::size_t poll_one(); + ASIO_DECL std::size_t poll_one(); /// Run the io_service object's event processing loop to execute one ready /// handler. @@ -363,7 +367,7 @@ public: * * @return The number of handlers that were executed. */ - std::size_t poll_one(asio::error_code& ec); + ASIO_DECL std::size_t poll_one(asio::error_code& ec); /// Stop the io_service object's event processing loop. /** @@ -372,7 +376,7 @@ public: * return as soon as possible. Subsequent calls to run(), run_one(), poll() * or poll_one() will return immediately until reset() is called. */ - void stop(); + ASIO_DECL void stop(); /// Reset the io_service in preparation for a subsequent run() invocation. /** @@ -385,7 +389,7 @@ public: * This function must not be called while there are any unfinished calls to * the run(), run_one(), poll() or poll_one() functions. */ - void reset(); + ASIO_DECL void reset(); /// Request the io_service to invoke the given handler. /** @@ -605,10 +609,10 @@ protected: /** * @param owner The io_service object that owns the service. */ - service(asio::io_service& owner); + ASIO_DECL service(asio::io_service& owner); /// Destructor. - virtual ~service(); + ASIO_DECL virtual ~service(); private: /// Destroy all user-defined handler objects owned by the service. @@ -631,10 +635,7 @@ class service_already_exists : public std::logic_error { public: - service_already_exists() - : std::logic_error("Service already exists.") - { - } + ASIO_DECL service_already_exists(); }; /// Exception thrown when trying to add a service object to an io_service where @@ -643,16 +644,44 @@ class invalid_service_owner : public std::logic_error { public: - invalid_service_owner() - : std::logic_error("Invalid service owner.") + ASIO_DECL invalid_service_owner(); +}; + +namespace detail { + +// Special derived service id type to keep classes header-file only. +template +class service_id + : public asio::io_service::id +{ +}; + +// Special service base class to keep classes header-file only. +template +class service_base + : public asio::io_service::service +{ +public: + static asio::detail::service_id id; + + // Constructor. + service_base(asio::io_service& io_service) + : asio::io_service::service(io_service) { } }; -} // namespace asio +template +asio::detail::service_id service_base::id; -#include "asio/impl/io_service.ipp" +} // namespace detail +} // namespace asio #include "asio/detail/pop_options.hpp" +#include "asio/impl/io_service.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/io_service.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_IO_SERVICE_HPP diff --git a/ext/asio/asio/ip/address.hpp b/ext/asio/asio/ip/address.hpp index 92ee4ae43d..47683dbda6 100644 --- a/ext/asio/asio/ip/address.hpp +++ b/ext/asio/asio/ip/address.hpp @@ -1,8 +1,8 @@ // -// address.hpp -// ~~~~~~~~~~~ +// ip/address.hpp +// ~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,21 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include +#include "asio/error_code.hpp" +#include "asio/ip/address_v4.hpp" +#include "asio/ip/address_v6.hpp" -#include "asio/detail/push_options.hpp" -#include #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) -#include -#include -#include "asio/detail/pop_options.hpp" -#include "asio/error.hpp" -#include "asio/ip/address_v4.hpp" -#include "asio/ip/address_v6.hpp" -#include "asio/detail/throw_error.hpp" +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { @@ -47,63 +43,27 @@ class address { public: /// Default constructor. - address() - : type_(ipv4), - ipv4_address_(), - ipv6_address_() - { - } + ASIO_DECL address(); /// Construct an address from an IPv4 address. - address(const asio::ip::address_v4& ipv4_address) - : type_(ipv4), - ipv4_address_(ipv4_address), - ipv6_address_() - { - } + ASIO_DECL address(const asio::ip::address_v4& ipv4_address); /// Construct an address from an IPv6 address. - address(const asio::ip::address_v6& ipv6_address) - : type_(ipv6), - ipv4_address_(), - ipv6_address_(ipv6_address) - { - } + ASIO_DECL address(const asio::ip::address_v6& ipv6_address); /// Copy constructor. - address(const address& other) - : type_(other.type_), - ipv4_address_(other.ipv4_address_), - ipv6_address_(other.ipv6_address_) - { - } + ASIO_DECL address(const address& other); /// Assign from another address. - address& operator=(const address& other) - { - type_ = other.type_; - ipv4_address_ = other.ipv4_address_; - ipv6_address_ = other.ipv6_address_; - return *this; - } + ASIO_DECL address& operator=(const address& other); /// Assign from an IPv4 address. - address& operator=(const asio::ip::address_v4& ipv4_address) - { - type_ = ipv4; - ipv4_address_ = ipv4_address; - ipv6_address_ = asio::ip::address_v6(); - return *this; - } + ASIO_DECL address& operator=( + const asio::ip::address_v4& ipv4_address); /// Assign from an IPv6 address. - address& operator=(const asio::ip::address_v6& ipv6_address) - { - type_ = ipv6; - ipv4_address_ = asio::ip::address_v4(); - ipv6_address_ = ipv6_address; - return *this; - } + ASIO_DECL address& operator=( + const asio::ip::address_v6& ipv6_address); /// Get whether the address is an IP version 4 address. bool is_v4() const @@ -118,127 +78,63 @@ public: } /// Get the address as an IP version 4 address. - asio::ip::address_v4 to_v4() const - { - if (type_ != ipv4) - { - asio::system_error e( - asio::error::address_family_not_supported); - boost::throw_exception(e); - } - return ipv4_address_; - } + ASIO_DECL asio::ip::address_v4 to_v4() const; /// Get the address as an IP version 6 address. - asio::ip::address_v6 to_v6() const - { - if (type_ != ipv6) - { - asio::system_error e( - asio::error::address_family_not_supported); - boost::throw_exception(e); - } - return ipv6_address_; - } + ASIO_DECL asio::ip::address_v6 to_v6() const; /// Get the address as a string in dotted decimal format. - std::string to_string() const - { - if (type_ == ipv6) - return ipv6_address_.to_string(); - return ipv4_address_.to_string(); - } + ASIO_DECL std::string to_string() const; /// Get the address as a string in dotted decimal format. - std::string to_string(asio::error_code& ec) const - { - if (type_ == ipv6) - return ipv6_address_.to_string(ec); - return ipv4_address_.to_string(ec); - } + ASIO_DECL std::string to_string(asio::error_code& ec) const; /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. - static address from_string(const char* str) - { - asio::error_code ec; - address addr = from_string(str, ec); - asio::detail::throw_error(ec); - return addr; - } + ASIO_DECL static address from_string(const char* str); /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. - static address from_string(const char* str, asio::error_code& ec) - { - asio::ip::address_v6 ipv6_address = - asio::ip::address_v6::from_string(str, ec); - if (!ec) - { - address tmp; - tmp.type_ = ipv6; - tmp.ipv6_address_ = ipv6_address; - return tmp; - } - - asio::ip::address_v4 ipv4_address = - asio::ip::address_v4::from_string(str, ec); - if (!ec) - { - address tmp; - tmp.type_ = ipv4; - tmp.ipv4_address_ = ipv4_address; - return tmp; - } - - return address(); - } + ASIO_DECL static address from_string( + const char* str, asio::error_code& ec); /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. - static address from_string(const std::string& str) - { - return from_string(str.c_str()); - } + ASIO_DECL static address from_string(const std::string& str); /// Create an address from an IPv4 address string in dotted decimal form, /// or from an IPv6 address in hexadecimal notation. - static address from_string(const std::string& str, - asio::error_code& ec) - { - return from_string(str.c_str(), ec); - } + ASIO_DECL static address from_string( + const std::string& str, asio::error_code& ec); /// Compare two addresses for equality. - friend bool operator==(const address& a1, const address& a2) - { - if (a1.type_ != a2.type_) - return false; - if (a1.type_ == ipv6) - return a1.ipv6_address_ == a2.ipv6_address_; - return a1.ipv4_address_ == a2.ipv4_address_; - } + ASIO_DECL friend bool operator==(const address& a1, const address& a2); /// Compare two addresses for inequality. friend bool operator!=(const address& a1, const address& a2) { - if (a1.type_ != a2.type_) - return true; - if (a1.type_ == ipv6) - return a1.ipv6_address_ != a2.ipv6_address_; - return a1.ipv4_address_ != a2.ipv4_address_; + return !(a1 == a2); } /// Compare addresses for ordering. - friend bool operator<(const address& a1, const address& a2) + ASIO_DECL friend bool operator<(const address& a1, const address& a2); + + /// Compare addresses for ordering. + friend bool operator>(const address& a1, const address& a2) { - if (a1.type_ < a2.type_) - return true; - if (a1.type_ > a2.type_) - return false; - if (a1.type_ == ipv6) - return a1.ipv6_address_ < a2.ipv6_address_; - return a1.ipv4_address_ < a2.ipv4_address_; + return a2 < a1; + } + + /// Compare addresses for ordering. + friend bool operator<=(const address& a1, const address& a2) + { + return !(a2 < a1); + } + + /// Compare addresses for ordering. + friend bool operator>=(const address& a1, const address& a2) + { + return !(a1 < a2); } private: @@ -268,11 +164,7 @@ private: */ template std::basic_ostream& operator<<( - std::basic_ostream& os, const address& addr) -{ - os << addr.to_string(); - return os; -} + std::basic_ostream& os, const address& addr); #endif // !defined(BOOST_NO_IOSTREAM) @@ -281,4 +173,9 @@ std::basic_ostream& operator<<( #include "asio/detail/pop_options.hpp" +#include "asio/ip/impl/address.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/address.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_IP_ADDRESS_HPP diff --git a/ext/asio/asio/ip/address_v4.hpp b/ext/asio/asio/ip/address_v4.hpp index 1b495560fe..7567ef9b42 100644 --- a/ext/asio/asio/ip/address_v4.hpp +++ b/ext/asio/asio/ip/address_v4.hpp @@ -1,8 +1,8 @@ // -// address_v4.hpp -// ~~~~~~~~~~~~~~ +// ip/address_v4.hpp +// ~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,24 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/error_code.hpp" -#include "asio/detail/push_options.hpp" -#include #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) -#include -#include -#include -#include -#include -#include "asio/detail/pop_options.hpp" -#include "asio/error.hpp" -#include "asio/detail/socket_ops.hpp" -#include "asio/detail/socket_types.hpp" -#include "asio/detail/throw_error.hpp" +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { @@ -59,34 +53,10 @@ public: } /// Construct an address from raw bytes. - explicit address_v4(const bytes_type& bytes) - { -#if UCHAR_MAX > 0xFF - if (bytes[0] > 0xFF || bytes[1] > 0xFF - || bytes[2] > 0xFF || bytes[3] > 0xFF) - { - std::out_of_range ex("address_v4 from bytes_type"); - boost::throw_exception(ex); - } -#endif // UCHAR_MAX > 0xFF - - using namespace std; // For memcpy. - memcpy(&addr_.s_addr, bytes.elems, 4); - } + ASIO_DECL explicit address_v4(const bytes_type& bytes); /// Construct an address from a unsigned long in host byte order. - explicit address_v4(unsigned long addr) - { -#if ULONG_MAX > 0xFFFFFFFF - if (addr > 0xFFFFFFFF) - { - std::out_of_range ex("address_v4 from unsigned long"); - boost::throw_exception(ex); - } -#endif // ULONG_MAX > 0xFFFFFFFF - - addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr); - } + ASIO_DECL explicit address_v4(unsigned long addr); /// Copy constructor. address_v4(const address_v4& other) @@ -102,96 +72,42 @@ public: } /// Get the address in bytes, in network byte order. - bytes_type to_bytes() const - { - using namespace std; // For memcpy. - bytes_type bytes; - memcpy(bytes.elems, &addr_.s_addr, 4); - return bytes; - } + ASIO_DECL bytes_type to_bytes() const; /// Get the address as an unsigned long in host byte order - unsigned long to_ulong() const - { - return asio::detail::socket_ops::network_to_host_long(addr_.s_addr); - } + ASIO_DECL unsigned long to_ulong() const; /// Get the address as a string in dotted decimal format. - std::string to_string() const - { - asio::error_code ec; - std::string addr = to_string(ec); - asio::detail::throw_error(ec); - return addr; - } + ASIO_DECL std::string to_string() const; /// Get the address as a string in dotted decimal format. - std::string to_string(asio::error_code& ec) const - { - char addr_str[asio::detail::max_addr_v4_str_len]; - const char* addr = - asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str, - asio::detail::max_addr_v4_str_len, 0, ec); - if (addr == 0) - return std::string(); - return addr; - } + ASIO_DECL std::string to_string(asio::error_code& ec) const; /// Create an address from an IP address string in dotted decimal form. - static address_v4 from_string(const char* str) - { - asio::error_code ec; - address_v4 addr = from_string(str, ec); - asio::detail::throw_error(ec); - return addr; - } + ASIO_DECL static address_v4 from_string(const char* str); /// Create an address from an IP address string in dotted decimal form. - static address_v4 from_string(const char* str, asio::error_code& ec) - { - address_v4 tmp; - if (asio::detail::socket_ops::inet_pton( - AF_INET, str, &tmp.addr_, 0, ec) <= 0) - return address_v4(); - return tmp; - } + ASIO_DECL static address_v4 from_string( + const char* str, asio::error_code& ec); /// Create an address from an IP address string in dotted decimal form. - static address_v4 from_string(const std::string& str) - { - return from_string(str.c_str()); - } + ASIO_DECL static address_v4 from_string(const std::string& str); /// Create an address from an IP address string in dotted decimal form. - static address_v4 from_string(const std::string& str, - asio::error_code& ec) - { - return from_string(str.c_str(), ec); - } + ASIO_DECL static address_v4 from_string( + const std::string& str, asio::error_code& ec); /// Determine whether the address is a class A address. - bool is_class_a() const - { - return IN_CLASSA(to_ulong()); - } + ASIO_DECL bool is_class_a() const; /// Determine whether the address is a class B address. - bool is_class_b() const - { - return IN_CLASSB(to_ulong()); - } + ASIO_DECL bool is_class_b() const; /// Determine whether the address is a class C address. - bool is_class_c() const - { - return IN_CLASSC(to_ulong()); - } + ASIO_DECL bool is_class_c() const; /// Determine whether the address is a multicast address. - bool is_multicast() const - { - return IN_MULTICAST(to_ulong()); - } + ASIO_DECL bool is_multicast() const; /// Compare two addresses for equality. friend bool operator==(const address_v4& a1, const address_v4& a2) @@ -249,23 +165,12 @@ public: /// Obtain an address object that represents the broadcast address that /// corresponds to the specified address and netmask. - static address_v4 broadcast(const address_v4& addr, const address_v4& mask) - { - return address_v4(addr.to_ulong() | ~mask.to_ulong()); - } + ASIO_DECL static address_v4 broadcast( + const address_v4& addr, const address_v4& mask); /// Obtain the netmask that corresponds to the address, based on its address /// class. - static address_v4 netmask(const address_v4& addr) - { - if (addr.is_class_a()) - return address_v4(0xFF000000); - if (addr.is_class_b()) - return address_v4(0xFFFF0000); - if (addr.is_class_c()) - return address_v4(0xFFFFFF00); - return address_v4(0xFFFFFFFF); - } + ASIO_DECL static address_v4 netmask(const address_v4& addr); private: // The underlying IPv4 address. @@ -288,22 +193,7 @@ private: */ template std::basic_ostream& operator<<( - std::basic_ostream& os, const address_v4& addr) -{ - asio::error_code ec; - std::string s = addr.to_string(ec); - if (ec) - { - if (os.exceptions() & std::ios::failbit) - asio::detail::throw_error(ec); - else - os.setstate(std::ios_base::failbit); - } - else - for (std::string::iterator i = s.begin(); i != s.end(); ++i) - os << os.widen(*i); - return os; -} + std::basic_ostream& os, const address_v4& addr); #endif // !defined(BOOST_NO_IOSTREAM) @@ -312,4 +202,9 @@ std::basic_ostream& operator<<( #include "asio/detail/pop_options.hpp" +#include "asio/ip/impl/address_v4.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/address_v4.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_IP_ADDRESS_V4_HPP diff --git a/ext/asio/asio/ip/address_v6.hpp b/ext/asio/asio/ip/address_v6.hpp index 8d2c08393d..261c6514eb 100644 --- a/ext/asio/asio/ip/address_v6.hpp +++ b/ext/asio/asio/ip/address_v6.hpp @@ -1,8 +1,8 @@ // -// address_v6.hpp -// ~~~~~~~~~~~~~~ +// ip/address_v6.hpp +// ~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,26 +15,19 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/error_code.hpp" +#include "asio/ip/address_v4.hpp" -#include "asio/detail/push_options.hpp" -#include #if !defined(BOOST_NO_IOSTREAM) # include #endif // !defined(BOOST_NO_IOSTREAM) -#include -#include -#include -#include -#include -#include -#include "asio/detail/pop_options.hpp" -#include "asio/error.hpp" -#include "asio/detail/socket_ops.hpp" -#include "asio/detail/socket_types.hpp" -#include "asio/detail/throw_error.hpp" -#include "asio/ip/address_v4.hpp" +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { @@ -55,46 +48,17 @@ public: typedef boost::array bytes_type; /// Default constructor. - address_v6() - : scope_id_(0) - { - asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; - addr_ = tmp_addr; - } + ASIO_DECL address_v6(); /// Construct an address from raw bytes and scope ID. - explicit address_v6(const bytes_type& bytes, unsigned long scope_id = 0) - : scope_id_(scope_id) - { -#if UCHAR_MAX > 0xFF - for (std::size_t i = 0; i < bytes.size(); ++i) - { - if (bytes[i] > 0xFF) - { - std::out_of_range ex("address_v6 from bytes_type"); - boost::throw_exception(ex); - } - } -#endif // UCHAR_MAX > 0xFF - - using namespace std; // For memcpy. - memcpy(addr_.s6_addr, bytes.elems, 16); - } + ASIO_DECL explicit address_v6(const bytes_type& bytes, + unsigned long scope_id = 0); /// Copy constructor. - address_v6(const address_v6& other) - : addr_(other.addr_), - scope_id_(other.scope_id_) - { - } + ASIO_DECL address_v6(const address_v6& other); /// Assign from another address. - address_v6& operator=(const address_v6& other) - { - addr_ = other.addr_; - scope_id_ = other.scope_id_; - return *this; - } + ASIO_DECL address_v6& operator=(const address_v6& other); /// The scope ID of the address. /** @@ -115,217 +79,80 @@ public: } /// Get the address in bytes, in network byte order. - bytes_type to_bytes() const - { - using namespace std; // For memcpy. - bytes_type bytes; - memcpy(bytes.elems, addr_.s6_addr, 16); - return bytes; - } + ASIO_DECL bytes_type to_bytes() const; /// Get the address as a string. - std::string to_string() const - { - asio::error_code ec; - std::string addr = to_string(ec); - asio::detail::throw_error(ec); - return addr; - } + ASIO_DECL std::string to_string() const; /// Get the address as a string. - std::string to_string(asio::error_code& ec) const - { - char addr_str[asio::detail::max_addr_v6_str_len]; - const char* addr = - asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str, - asio::detail::max_addr_v6_str_len, scope_id_, ec); - if (addr == 0) - return std::string(); - return addr; - } + ASIO_DECL std::string to_string(asio::error_code& ec) const; /// Create an address from an IP address string. - static address_v6 from_string(const char* str) - { - asio::error_code ec; - address_v6 addr = from_string(str, ec); - asio::detail::throw_error(ec); - return addr; - } + ASIO_DECL static address_v6 from_string(const char* str); /// Create an address from an IP address string. - static address_v6 from_string(const char* str, asio::error_code& ec) - { - address_v6 tmp; - if (asio::detail::socket_ops::inet_pton( - AF_INET6, str, &tmp.addr_, &tmp.scope_id_, ec) <= 0) - return address_v6(); - return tmp; - } + ASIO_DECL static address_v6 from_string( + const char* str, asio::error_code& ec); /// Create an address from an IP address string. - static address_v6 from_string(const std::string& str) - { - return from_string(str.c_str()); - } + ASIO_DECL static address_v6 from_string(const std::string& str); /// Create an address from an IP address string. - static address_v6 from_string(const std::string& str, - asio::error_code& ec) - { - return from_string(str.c_str(), ec); - } + ASIO_DECL static address_v6 from_string( + const std::string& str, asio::error_code& ec); /// Converts an IPv4-mapped or IPv4-compatible address to an IPv4 address. - address_v4 to_v4() const - { - if (!is_v4_mapped() && !is_v4_compatible()) - { - std::bad_cast ex; - boost::throw_exception(ex); - } - - address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12], - addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } }; - return address_v4(v4_bytes); - } + ASIO_DECL address_v4 to_v4() const; /// Determine whether the address is a loopback address. - bool is_loopback() const - { -#if defined(__BORLANDC__) - return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) - && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) - && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) - && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) - && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) - && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) - && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) - && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1)); -#else - using namespace asio::detail; - return IN6_IS_ADDR_LOOPBACK(&addr_) != 0; -#endif - } + ASIO_DECL bool is_loopback() const; /// Determine whether the address is unspecified. - bool is_unspecified() const - { -#if defined(__BORLANDC__) - return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) - && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) - && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) - && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) - && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) - && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) - && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) - && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0)); -#else - using namespace asio::detail; - return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0; -#endif - } + ASIO_DECL bool is_unspecified() const; /// Determine whether the address is link local. - bool is_link_local() const - { - using namespace asio::detail; - return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0; - } + ASIO_DECL bool is_link_local() const; /// Determine whether the address is site local. - bool is_site_local() const - { - using namespace asio::detail; - return IN6_IS_ADDR_SITELOCAL(&addr_) != 0; - } + ASIO_DECL bool is_site_local() const; /// Determine whether the address is a mapped IPv4 address. - bool is_v4_mapped() const - { - using namespace asio::detail; - return IN6_IS_ADDR_V4MAPPED(&addr_) != 0; - } + ASIO_DECL bool is_v4_mapped() const; /// Determine whether the address is an IPv4-compatible address. - bool is_v4_compatible() const - { - using namespace asio::detail; - return IN6_IS_ADDR_V4COMPAT(&addr_) != 0; - } + ASIO_DECL bool is_v4_compatible() const; /// Determine whether the address is a multicast address. - bool is_multicast() const - { - using namespace asio::detail; - return IN6_IS_ADDR_MULTICAST(&addr_) != 0; - } + ASIO_DECL bool is_multicast() const; /// Determine whether the address is a global multicast address. - bool is_multicast_global() const - { - using namespace asio::detail; - return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0; - } + ASIO_DECL bool is_multicast_global() const; /// Determine whether the address is a link-local multicast address. - bool is_multicast_link_local() const - { - using namespace asio::detail; - return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0; - } + ASIO_DECL bool is_multicast_link_local() const; /// Determine whether the address is a node-local multicast address. - bool is_multicast_node_local() const - { - using namespace asio::detail; - return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0; - } + ASIO_DECL bool is_multicast_node_local() const; /// Determine whether the address is a org-local multicast address. - bool is_multicast_org_local() const - { - using namespace asio::detail; - return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0; - } + ASIO_DECL bool is_multicast_org_local() const; /// Determine whether the address is a site-local multicast address. - bool is_multicast_site_local() const - { - using namespace asio::detail; - return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0; - } + ASIO_DECL bool is_multicast_site_local() const; /// Compare two addresses for equality. - friend bool operator==(const address_v6& a1, const address_v6& a2) - { - using namespace std; // For memcmp. - return memcmp(&a1.addr_, &a2.addr_, - sizeof(asio::detail::in6_addr_type)) == 0 - && a1.scope_id_ == a2.scope_id_; - } + ASIO_DECL friend bool operator==( + const address_v6& a1, const address_v6& a2); /// Compare two addresses for inequality. friend bool operator!=(const address_v6& a1, const address_v6& a2) { - using namespace std; // For memcmp. - return memcmp(&a1.addr_, &a2.addr_, - sizeof(asio::detail::in6_addr_type)) != 0 - || a1.scope_id_ != a2.scope_id_; + return !(a1 == a2); } /// Compare addresses for ordering. - friend bool operator<(const address_v6& a1, const address_v6& a2) - { - using namespace std; // For memcmp. - int memcmp_result = memcmp(&a1.addr_, &a2.addr_, - sizeof(asio::detail::in6_addr_type)); - if (memcmp_result < 0) - return true; - if (memcmp_result > 0) - return false; - return a1.scope_id_ < a2.scope_id_; - } + ASIO_DECL friend bool operator<( + const address_v6& a1, const address_v6& a2); /// Compare addresses for ordering. friend bool operator>(const address_v6& a1, const address_v6& a2) @@ -352,31 +179,13 @@ public: } /// Obtain an address object that represents the loopback address. - static address_v6 loopback() - { - address_v6 tmp; - asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT; - tmp.addr_ = tmp_addr; - return tmp; - } + ASIO_DECL static address_v6 loopback(); /// Create an IPv4-mapped IPv6 address. - static address_v6 v4_mapped(const address_v4& addr) - { - address_v4::bytes_type v4_bytes = addr.to_bytes(); - bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, - v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; - return address_v6(v6_bytes); - } + ASIO_DECL static address_v6 v4_mapped(const address_v4& addr); /// Create an IPv4-compatible IPv6 address. - static address_v6 v4_compatible(const address_v4& addr) - { - address_v4::bytes_type v4_bytes = addr.to_bytes(); - bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; - return address_v6(v6_bytes); - } + ASIO_DECL static address_v6 v4_compatible(const address_v4& addr); private: // The underlying IPv6 address. @@ -402,22 +211,7 @@ private: */ template std::basic_ostream& operator<<( - std::basic_ostream& os, const address_v6& addr) -{ - asio::error_code ec; - std::string s = addr.to_string(ec); - if (ec) - { - if (os.exceptions() & std::ios::failbit) - asio::detail::throw_error(ec); - else - os.setstate(std::ios_base::failbit); - } - else - for (std::string::iterator i = s.begin(); i != s.end(); ++i) - os << os.widen(*i); - return os; -} + std::basic_ostream& os, const address_v6& addr); #endif // !defined(BOOST_NO_IOSTREAM) @@ -426,4 +220,9 @@ std::basic_ostream& operator<<( #include "asio/detail/pop_options.hpp" +#include "asio/ip/impl/address_v6.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/address_v6.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_IP_ADDRESS_V6_HPP diff --git a/ext/asio/asio/ip/basic_endpoint.hpp b/ext/asio/asio/ip/basic_endpoint.hpp index 4ad3f682b7..5e5ef0b01c 100644 --- a/ext/asio/asio/ip/basic_endpoint.hpp +++ b/ext/asio/asio/ip/basic_endpoint.hpp @@ -1,8 +1,8 @@ // -// basic_endpoint.hpp -// ~~~~~~~~~~~~~~~~~~ +// ip/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,25 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include -#if !defined(BOOST_NO_IOSTREAM) -# if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) -# include -# endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) -# include -#endif // !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" +#include "asio/detail/config.hpp" #include "asio/ip/address.hpp" -#include "asio/detail/socket_ops.hpp" -#include "asio/detail/socket_types.hpp" +#include "asio/ip/detail/endpoint.hpp" + +#if !defined(BOOST_NO_IOSTREAM) +# include +#endif // !defined(BOOST_NO_IOSTREAM) + +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { @@ -67,11 +57,8 @@ public: /// Default constructor. basic_endpoint() - : data_() + : impl_() { - data_.v4.sin_family = AF_INET; - data_.v4.sin_port = 0; - data_.v4.sin_addr.s_addr = INADDR_ANY; } /// Construct an endpoint using a port number, specified in the host's byte @@ -91,74 +78,35 @@ public: * @endcode */ basic_endpoint(const InternetProtocol& protocol, unsigned short port_num) - : data_() + : impl_(protocol.family(), port_num) { - using namespace std; // For memcpy. - if (protocol.family() == PF_INET) - { - data_.v4.sin_family = AF_INET; - data_.v4.sin_port = - asio::detail::socket_ops::host_to_network_short(port_num); - data_.v4.sin_addr.s_addr = INADDR_ANY; - } - else - { - data_.v6.sin6_family = AF_INET6; - data_.v6.sin6_port = - asio::detail::socket_ops::host_to_network_short(port_num); - data_.v6.sin6_flowinfo = 0; - asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; - data_.v6.sin6_addr = tmp_addr; - data_.v6.sin6_scope_id = 0; - } } /// Construct an endpoint using a port number and an IP address. This /// constructor may be used for accepting connections on a specific interface /// or for making a connection to a remote endpoint. basic_endpoint(const asio::ip::address& addr, unsigned short port_num) - : data_() + : impl_(addr, port_num) { - using namespace std; // For memcpy. - if (addr.is_v4()) - { - data_.v4.sin_family = AF_INET; - data_.v4.sin_port = - asio::detail::socket_ops::host_to_network_short(port_num); - data_.v4.sin_addr.s_addr = - asio::detail::socket_ops::host_to_network_long( - addr.to_v4().to_ulong()); - } - else - { - data_.v6.sin6_family = AF_INET6; - data_.v6.sin6_port = - asio::detail::socket_ops::host_to_network_short(port_num); - data_.v6.sin6_flowinfo = 0; - asio::ip::address_v6 v6_addr = addr.to_v6(); - asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes(); - memcpy(data_.v6.sin6_addr.s6_addr, bytes.elems, 16); - data_.v6.sin6_scope_id = v6_addr.scope_id(); - } } /// Copy constructor. basic_endpoint(const basic_endpoint& other) - : data_(other.data_) + : impl_(other.impl_) { } /// Assign from another endpoint. basic_endpoint& operator=(const basic_endpoint& other) { - data_ = other.data_; + impl_ = other.impl_; return *this; } /// The protocol associated with the endpoint. protocol_type protocol() const { - if (is_v4()) + if (impl_.is_v4()) return InternetProtocol::v4(); return InternetProtocol::v6(); } @@ -166,137 +114,104 @@ public: /// Get the underlying endpoint in the native type. data_type* data() { - return &data_.base; + return impl_.data(); } /// Get the underlying endpoint in the native type. const data_type* data() const { - return &data_.base; + return impl_.data(); } /// Get the underlying size of the endpoint in the native type. std::size_t size() const { - if (is_v4()) - return sizeof(asio::detail::sockaddr_in4_type); - else - return sizeof(asio::detail::sockaddr_in6_type); + return impl_.size(); } /// Set the underlying size of the endpoint in the native type. void resize(std::size_t size) { - if (size > sizeof(asio::detail::sockaddr_storage_type)) - { - asio::system_error e(asio::error::invalid_argument); - boost::throw_exception(e); - } + impl_.resize(size); } /// Get the capacity of the endpoint in the native type. std::size_t capacity() const { - return sizeof(asio::detail::sockaddr_storage_type); + return impl_.capacity(); } /// Get the port associated with the endpoint. The port number is always in /// the host's byte order. unsigned short port() const { - if (is_v4()) - { - return asio::detail::socket_ops::network_to_host_short( - data_.v4.sin_port); - } - else - { - return asio::detail::socket_ops::network_to_host_short( - data_.v6.sin6_port); - } + return impl_.port(); } /// Set the port associated with the endpoint. The port number is always in /// the host's byte order. void port(unsigned short port_num) { - if (is_v4()) - { - data_.v4.sin_port - = asio::detail::socket_ops::host_to_network_short(port_num); - } - else - { - data_.v6.sin6_port - = asio::detail::socket_ops::host_to_network_short(port_num); - } + impl_.port(port_num); } /// Get the IP address associated with the endpoint. asio::ip::address address() const { - using namespace std; // For memcpy. - if (is_v4()) - { - return asio::ip::address_v4( - asio::detail::socket_ops::network_to_host_long( - data_.v4.sin_addr.s_addr)); - } - else - { - asio::ip::address_v6::bytes_type bytes; - memcpy(bytes.elems, data_.v6.sin6_addr.s6_addr, 16); - return asio::ip::address_v6(bytes, data_.v6.sin6_scope_id); - } + return impl_.address(); } /// Set the IP address associated with the endpoint. void address(const asio::ip::address& addr) { - basic_endpoint tmp_endpoint(addr, port()); - data_ = tmp_endpoint.data_; + impl_.address(addr); } /// Compare two endpoints for equality. friend bool operator==(const basic_endpoint& e1, const basic_endpoint& e2) { - return e1.address() == e2.address() && e1.port() == e2.port(); + return e1.impl_ == e2.impl_; } /// Compare two endpoints for inequality. friend bool operator!=(const basic_endpoint& e1, const basic_endpoint& e2) { - return e1.address() != e2.address() || e1.port() != e2.port(); + return !(e1 == e2); } /// Compare endpoints for ordering. friend bool operator<(const basic_endpoint& e1, const basic_endpoint& e2) { - if (e1.address() < e2.address()) - return true; - if (e1.address() != e2.address()) - return false; - return e1.port() < e2.port(); + return e1.impl_ < e2.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator>(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e2.impl_ < e1.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator<=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e2 < e1); + } + + /// Compare endpoints for ordering. + friend bool operator>=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1 < e2); } private: - // Helper function to determine whether the endpoint is IPv4. - bool is_v4() const - { - return data_.base.sa_family == AF_INET; - } - - // The underlying IP socket address. - union data_union - { - asio::detail::socket_addr_type base; - asio::detail::sockaddr_storage_type storage; - asio::detail::sockaddr_in4_type v4; - asio::detail::sockaddr_in6_type v6; - } data_; + // The underlying IP endpoint. + asio::ip::detail::endpoint impl_; }; #if !defined(BOOST_NO_IOSTREAM) @@ -313,64 +228,10 @@ private: * * @relates asio::ip::basic_endpoint */ -#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) -template -std::ostream& operator<<(std::ostream& os, - const basic_endpoint& endpoint) -{ - const address& addr = endpoint.address(); - asio::error_code ec; - std::string a = addr.to_string(ec); - if (ec) - { - if (os.exceptions() & std::ios::failbit) - asio::detail::throw_error(ec); - else - os.setstate(std::ios_base::failbit); - } - else - { - std::ostringstream tmp_os; - tmp_os.imbue(std::locale::classic()); - if (addr.is_v4()) - tmp_os << a; - else - tmp_os << '[' << a << ']'; - tmp_os << ':' << endpoint.port(); - os << tmp_os.str(); - } - return os; -} -#else // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) template std::basic_ostream& operator<<( std::basic_ostream& os, - const basic_endpoint& endpoint) -{ - const address& addr = endpoint.address(); - asio::error_code ec; - std::string a = addr.to_string(ec); - if (ec) - { - if (os.exceptions() & std::ios::failbit) - asio::detail::throw_error(ec); - else - os.setstate(std::ios_base::failbit); - } - else - { - std::ostringstream tmp_os; - tmp_os.imbue(std::locale::classic()); - if (addr.is_v4()) - tmp_os << a; - else - tmp_os << '[' << a << ']'; - tmp_os << ':' << endpoint.port(); - os << tmp_os.str(); - } - return os; -} -#endif // BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564)) + const basic_endpoint& endpoint); #endif // !defined(BOOST_NO_IOSTREAM) @@ -379,4 +240,6 @@ std::basic_ostream& operator<<( #include "asio/detail/pop_options.hpp" +#include "asio/ip/impl/basic_endpoint.hpp" + #endif // ASIO_IP_BASIC_ENDPOINT_HPP diff --git a/ext/asio/asio/ip/basic_resolver.hpp b/ext/asio/asio/ip/basic_resolver.hpp index 43a37af392..c8847e15e6 100644 --- a/ext/asio/asio/ip/basic_resolver.hpp +++ b/ext/asio/asio/ip/basic_resolver.hpp @@ -1,8 +1,8 @@ // -// basic_resolver.hpp -// ~~~~~~~~~~~~~~~~~~ +// ip/basic_resolver.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" #include "asio/ip/resolver_service.hpp" -#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/basic_resolver_entry.hpp b/ext/asio/asio/ip/basic_resolver_entry.hpp index 09b144b117..4117b7bc65 100644 --- a/ext/asio/asio/ip/basic_resolver_entry.hpp +++ b/ext/asio/asio/ip/basic_resolver_entry.hpp @@ -1,8 +1,8 @@ // -// basic_resolver_entry.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// ip/basic_resolver_entry.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include #include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/basic_resolver_iterator.hpp b/ext/asio/asio/ip/basic_resolver_iterator.hpp index 1982d62512..033b7e0b7d 100644 --- a/ext/asio/asio/ip/basic_resolver_iterator.hpp +++ b/ext/asio/asio/ip/basic_resolver_iterator.hpp @@ -1,8 +1,8 @@ // -// basic_resolver_iterator.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ip/basic_resolver_iterator.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,20 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include #include #include #include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/shared_ptr.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" #include "asio/ip/basic_resolver_entry.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ip { @@ -176,7 +174,7 @@ private: } typedef std::vector > values_type; - boost::shared_ptr values_; + asio::detail::shared_ptr values_; std::size_t index_; }; diff --git a/ext/asio/asio/ip/basic_resolver_query.hpp b/ext/asio/asio/ip/basic_resolver_query.hpp index 3cbb335ca5..22ab643bb4 100644 --- a/ext/asio/asio/ip/basic_resolver_query.hpp +++ b/ext/asio/asio/ip/basic_resolver_query.hpp @@ -1,8 +1,8 @@ // -// basic_resolver_query.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// ip/basic_resolver_query.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/socket_ops.hpp" #include "asio/ip/resolver_query_base.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/detail/endpoint.hpp b/ext/asio/asio/ip/detail/endpoint.hpp new file mode 100644 index 0000000000..fbea71e01b --- /dev/null +++ b/ext/asio/asio/ip/detail/endpoint.hpp @@ -0,0 +1,140 @@ +// +// ip/detail/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_DETAIL_ENDPOINT_HPP +#define ASIO_IP_DETAIL_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include "asio/detail/socket_types.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/error_code.hpp" +#include "asio/ip/address.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { +namespace detail { + +// Helper class for implementating an IP endpoint. +class endpoint +{ +public: + // Default constructor. + ASIO_DECL endpoint(); + + // Construct an endpoint using a family and port number. + ASIO_DECL endpoint(int family, unsigned short port_num); + + // Construct an endpoint using an address and port number. + ASIO_DECL endpoint(const asio::ip::address& addr, + unsigned short port_num); + + // Copy constructor. + endpoint(const endpoint& other) + : data_(other.data_) + { + } + + // Assign from another endpoint. + endpoint& operator=(const endpoint& other) + { + data_ = other.data_; + return *this; + } + + // Get the underlying endpoint in the native type. + asio::detail::socket_addr_type* data() + { + return &data_.base; + } + + // Get the underlying endpoint in the native type. + const asio::detail::socket_addr_type* data() const + { + return &data_.base; + } + + // Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + if (is_v4()) + return sizeof(asio::detail::sockaddr_in4_type); + else + return sizeof(asio::detail::sockaddr_in6_type); + } + + // Set the underlying size of the endpoint in the native type. + ASIO_DECL void resize(std::size_t size); + + // Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return sizeof(asio::detail::sockaddr_storage_type); + } + + // Get the port associated with the endpoint. + ASIO_DECL unsigned short port() const; + + // Set the port associated with the endpoint. + ASIO_DECL void port(unsigned short port_num); + + // Get the IP address associated with the endpoint. + ASIO_DECL asio::ip::address address() const; + + // Set the IP address associated with the endpoint. + ASIO_DECL void address(const asio::ip::address& addr); + + // Compare two endpoints for equality. + ASIO_DECL friend bool operator==( + const endpoint& e1, const endpoint& e2); + + // Compare endpoints for ordering. + ASIO_DECL friend bool operator<( + const endpoint& e1, const endpoint& e2); + + // Determine whether the endpoint is IPv4. + bool is_v4() const + { + return data_.base.sa_family == AF_INET; + } + +#if !defined(BOOST_NO_IOSTREAM) + // Convert to a string. + ASIO_DECL std::string to_string(asio::error_code& ec) const; +#endif // !defined(BOOST_NO_IOSTREAM) + +private: + // The underlying IP socket address. + union data_union + { + asio::detail::socket_addr_type base; + asio::detail::sockaddr_storage_type storage; + asio::detail::sockaddr_in4_type v4; + asio::detail::sockaddr_in6_type v6; + } data_; +}; + +} // namespace detail +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/detail/impl/endpoint.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // ASIO_IP_DETAIL_ENDPOINT_HPP diff --git a/ext/asio/asio/ip/detail/impl/endpoint.ipp b/ext/asio/asio/ip/detail/impl/endpoint.ipp new file mode 100644 index 0000000000..b1469c85c3 --- /dev/null +++ b/ext/asio/asio/ip/detail/impl/endpoint.ipp @@ -0,0 +1,202 @@ +// +// ip/detail/impl/endpoint.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP +#define ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#if !defined(BOOST_NO_IOSTREAM) +# include +#endif // !defined(BOOST_NO_IOSTREAM) +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/ip/detail/endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { +namespace detail { + +ASIO_DECL +endpoint::endpoint() + : data_() +{ + data_.v4.sin_family = AF_INET; + data_.v4.sin_port = 0; + data_.v4.sin_addr.s_addr = INADDR_ANY; +} + +ASIO_DECL +endpoint::endpoint(int family, unsigned short port_num) + : data_() +{ + using namespace std; // For memcpy. + if (family == PF_INET) + { + data_.v4.sin_family = AF_INET; + data_.v4.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v4.sin_addr.s_addr = INADDR_ANY; + } + else + { + data_.v6.sin6_family = AF_INET6; + data_.v6.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v6.sin6_flowinfo = 0; + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + data_.v6.sin6_addr = tmp_addr; + data_.v6.sin6_scope_id = 0; + } +} + +ASIO_DECL +endpoint::endpoint(const asio::ip::address& addr, + unsigned short port_num) + : data_() +{ + using namespace std; // For memcpy. + if (addr.is_v4()) + { + data_.v4.sin_family = AF_INET; + data_.v4.sin_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v4.sin_addr.s_addr = + asio::detail::socket_ops::host_to_network_long( + addr.to_v4().to_ulong()); + } + else + { + data_.v6.sin6_family = AF_INET6; + data_.v6.sin6_port = + asio::detail::socket_ops::host_to_network_short(port_num); + data_.v6.sin6_flowinfo = 0; + asio::ip::address_v6 v6_addr = addr.to_v6(); + asio::ip::address_v6::bytes_type bytes = v6_addr.to_bytes(); + memcpy(data_.v6.sin6_addr.s6_addr, bytes.elems, 16); + data_.v6.sin6_scope_id = v6_addr.scope_id(); + } +} + +ASIO_DECL +void endpoint::resize(std::size_t size) +{ + if (size > sizeof(asio::detail::sockaddr_storage_type)) + { + asio::error_code ec(asio::error::invalid_argument); + asio::detail::throw_error(ec); + } +} + +ASIO_DECL +unsigned short endpoint::port() const +{ + if (is_v4()) + { + return asio::detail::socket_ops::network_to_host_short( + data_.v4.sin_port); + } + else + { + return asio::detail::socket_ops::network_to_host_short( + data_.v6.sin6_port); + } +} + +ASIO_DECL +void endpoint::port(unsigned short port_num) +{ + if (is_v4()) + { + data_.v4.sin_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } + else + { + data_.v6.sin6_port + = asio::detail::socket_ops::host_to_network_short(port_num); + } +} + +ASIO_DECL +asio::ip::address endpoint::address() const +{ + using namespace std; // For memcpy. + if (is_v4()) + { + return asio::ip::address_v4( + asio::detail::socket_ops::network_to_host_long( + data_.v4.sin_addr.s_addr)); + } + else + { + asio::ip::address_v6::bytes_type bytes; + memcpy(bytes.elems, data_.v6.sin6_addr.s6_addr, 16); + return asio::ip::address_v6(bytes, data_.v6.sin6_scope_id); + } +} + +ASIO_DECL +void endpoint::address(const asio::ip::address& addr) +{ + endpoint tmp_endpoint(addr, port()); + data_ = tmp_endpoint.data_; +} + +ASIO_DECL +bool operator==(const endpoint& e1, const endpoint& e2) +{ + return e1.address() == e2.address() && e1.port() == e2.port(); +} + +ASIO_DECL +bool operator<(const endpoint& e1, const endpoint& e2) +{ + if (e1.address() < e2.address()) + return true; + if (e1.address() != e2.address()) + return false; + return e1.port() < e2.port(); +} + +#if !defined(BOOST_NO_IOSTREAM) +ASIO_DECL +std::string endpoint::to_string(asio::error_code& ec) const +{ + std::string a = address().to_string(ec); + if (ec) + return std::string(); + + std::ostringstream tmp_os; + tmp_os.imbue(std::locale::classic()); + if (is_v4()) + tmp_os << a; + else + tmp_os << '[' << a << ']'; + tmp_os << ':' << port(); + + return tmp_os.str(); +} +#endif // !defined(BOOST_NO_IOSTREAM) + +} // namespace detail +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_DETAIL_IMPL_ENDPOINT_IPP diff --git a/ext/asio/asio/ip/detail/socket_option.hpp b/ext/asio/asio/ip/detail/socket_option.hpp index 00045f86d5..e370b113b2 100644 --- a/ext/asio/asio/ip/detail/socket_option.hpp +++ b/ext/asio/asio/ip/detail/socket_option.hpp @@ -1,8 +1,8 @@ // -// socket_option.hpp -// ~~~~~~~~~~~~~~~~~ +// detail/socket_option.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,18 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/ip/address.hpp" #include "asio/detail/socket_ops.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/ip/address.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/host_name.hpp b/ext/asio/asio/ip/host_name.hpp index f24ce1a223..8bfaae94c1 100644 --- a/ext/asio/asio/ip/host_name.hpp +++ b/ext/asio/asio/ip/host_name.hpp @@ -1,8 +1,8 @@ // -// host_name.hpp -// ~~~~~~~~~~~~~ +// ip/host_name.hpp +// ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,48 +15,28 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" +#include "asio/error_code.hpp" -#include "asio/error.hpp" -#include "asio/detail/socket_ops.hpp" -#include "asio/detail/throw_error.hpp" +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { /// Get the current host name. -std::string host_name(); +ASIO_DECL std::string host_name(); /// Get the current host name. -std::string host_name(asio::error_code& ec); - -inline std::string host_name() -{ - char name[1024]; - asio::error_code ec; - if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) - { - asio::detail::throw_error(ec); - return std::string(); - } - return std::string(name); -} - -inline std::string host_name(asio::error_code& ec) -{ - char name[1024]; - if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) - return std::string(); - return std::string(name); -} +ASIO_DECL std::string host_name(asio::error_code& ec); } // namespace ip } // namespace asio #include "asio/detail/pop_options.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/ip/impl/host_name.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // ASIO_IP_HOST_NAME_HPP diff --git a/ext/asio/asio/ip/icmp.hpp b/ext/asio/asio/ip/icmp.hpp index d76b4d1a6d..59df5b027f 100644 --- a/ext/asio/asio/ip/icmp.hpp +++ b/ext/asio/asio/ip/icmp.hpp @@ -1,8 +1,8 @@ // -// icmp.hpp -// ~~~~~~~~ +// ip/icmp.hpp +// ~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" +#include "asio/detail/socket_types.hpp" #include "asio/basic_raw_socket.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" -#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/impl/address.hpp b/ext/asio/asio/ip/impl/address.hpp new file mode 100644 index 0000000000..0983d24684 --- /dev/null +++ b/ext/asio/asio/ip/impl/address.hpp @@ -0,0 +1,53 @@ +// +// ip/impl/address.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_HPP +#define ASIO_IP_IMPL_ADDRESS_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(BOOST_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address& addr) +{ + asio::error_code ec; + std::string s = addr.to_string(ec); + if (ec) + { + if (os.exceptions() & std::basic_ostream::failbit) + asio::detail::throw_error(ec); + else + os.setstate(std::basic_ostream::failbit); + } + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_ADDRESS_HPP diff --git a/ext/asio/asio/ip/impl/address.ipp b/ext/asio/asio/ip/impl/address.ipp new file mode 100644 index 0000000000..24916abd01 --- /dev/null +++ b/ext/asio/asio/ip/impl/address.ipp @@ -0,0 +1,203 @@ +// +// ip/impl/address.ipp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_IPP +#define ASIO_IP_IMPL_ADDRESS_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/ip/address.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +ASIO_DECL +address::address() + : type_(ipv4), + ipv4_address_(), + ipv6_address_() +{ +} + +ASIO_DECL +address::address(const asio::ip::address_v4& ipv4_address) + : type_(ipv4), + ipv4_address_(ipv4_address), + ipv6_address_() +{ +} + +ASIO_DECL +address::address(const asio::ip::address_v6& ipv6_address) + : type_(ipv6), + ipv4_address_(), + ipv6_address_(ipv6_address) +{ +} + +ASIO_DECL +address::address(const address& other) + : type_(other.type_), + ipv4_address_(other.ipv4_address_), + ipv6_address_(other.ipv6_address_) +{ +} + +ASIO_DECL +address& address::operator=(const address& other) +{ + type_ = other.type_; + ipv4_address_ = other.ipv4_address_; + ipv6_address_ = other.ipv6_address_; + return *this; +} + +ASIO_DECL +address& address::operator=(const asio::ip::address_v4& ipv4_address) +{ + type_ = ipv4; + ipv4_address_ = ipv4_address; + ipv6_address_ = asio::ip::address_v6(); + return *this; +} + +ASIO_DECL +address& address::operator=(const asio::ip::address_v6& ipv6_address) +{ + type_ = ipv6; + ipv4_address_ = asio::ip::address_v4(); + ipv6_address_ = ipv6_address; + return *this; +} + +ASIO_DECL +asio::ip::address_v4 address::to_v4() const +{ + if (type_ != ipv4) + { + std::bad_cast ex; + boost::throw_exception(ex); + } + return ipv4_address_; +} + +ASIO_DECL +asio::ip::address_v6 address::to_v6() const +{ + if (type_ != ipv6) + { + std::bad_cast ex; + boost::throw_exception(ex); + } + return ipv6_address_; +} + +ASIO_DECL +std::string address::to_string() const +{ + if (type_ == ipv6) + return ipv6_address_.to_string(); + return ipv4_address_.to_string(); +} + +ASIO_DECL +std::string address::to_string(asio::error_code& ec) const +{ + if (type_ == ipv6) + return ipv6_address_.to_string(ec); + return ipv4_address_.to_string(ec); +} + +ASIO_DECL +address address::from_string(const char* str) +{ + asio::error_code ec; + address addr = from_string(str, ec); + asio::detail::throw_error(ec); + return addr; +} + +ASIO_DECL +address address::from_string(const char* str, asio::error_code& ec) +{ + asio::ip::address_v6 ipv6_address = + asio::ip::address_v6::from_string(str, ec); + if (!ec) + { + address tmp; + tmp.type_ = ipv6; + tmp.ipv6_address_ = ipv6_address; + return tmp; + } + + asio::ip::address_v4 ipv4_address = + asio::ip::address_v4::from_string(str, ec); + if (!ec) + { + address tmp; + tmp.type_ = ipv4; + tmp.ipv4_address_ = ipv4_address; + return tmp; + } + + return address(); +} + +ASIO_DECL +address address::from_string(const std::string& str) +{ + return from_string(str.c_str()); +} + +ASIO_DECL +address address::from_string(const std::string& str, + asio::error_code& ec) +{ + return from_string(str.c_str(), ec); +} + +ASIO_DECL +bool operator==(const address& a1, const address& a2) +{ + if (a1.type_ != a2.type_) + return false; + if (a1.type_ == address::ipv6) + return a1.ipv6_address_ == a2.ipv6_address_; + return a1.ipv4_address_ == a2.ipv4_address_; +} + +ASIO_DECL +bool operator<(const address& a1, const address& a2) +{ + if (a1.type_ < a2.type_) + return true; + if (a1.type_ > a2.type_) + return false; + if (a1.type_ == address::ipv6) + return a1.ipv6_address_ < a2.ipv6_address_; + return a1.ipv4_address_ < a2.ipv4_address_; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_ADDRESS_IPP diff --git a/ext/asio/asio/ip/impl/address_v4.hpp b/ext/asio/asio/ip/impl/address_v4.hpp new file mode 100644 index 0000000000..bbe088211b --- /dev/null +++ b/ext/asio/asio/ip/impl/address_v4.hpp @@ -0,0 +1,53 @@ +// +// ip/impl/address_v4.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V4_HPP +#define ASIO_IP_IMPL_ADDRESS_V4_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(BOOST_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v4& addr) +{ + asio::error_code ec; + std::string s = addr.to_string(ec); + if (ec) + { + if (os.exceptions() & std::basic_ostream::failbit) + asio::detail::throw_error(ec); + else + os.setstate(std::basic_ostream::failbit); + } + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_ADDRESS_V4_HPP diff --git a/ext/asio/asio/ip/impl/address_v4.ipp b/ext/asio/asio/ip/impl/address_v4.ipp new file mode 100644 index 0000000000..2c8e9aacf3 --- /dev/null +++ b/ext/asio/asio/ip/impl/address_v4.ipp @@ -0,0 +1,178 @@ +// +// ip/impl/address_v4.ipp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V4_IPP +#define ASIO_IP_IMPL_ADDRESS_V4_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include "asio/error.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/ip/address_v4.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +ASIO_DECL +address_v4::address_v4(const address_v4::bytes_type& bytes) +{ +#if UCHAR_MAX > 0xFF + if (bytes[0] > 0xFF || bytes[1] > 0xFF + || bytes[2] > 0xFF || bytes[3] > 0xFF) + { + std::out_of_range ex("address_v4 from bytes_type"); + boost::throw_exception(ex); + } +#endif // UCHAR_MAX > 0xFF + + using namespace std; // For memcpy. + memcpy(&addr_.s_addr, bytes.elems, 4); +} + +ASIO_DECL +address_v4::address_v4(unsigned long addr) +{ +#if ULONG_MAX > 0xFFFFFFFF + if (addr > 0xFFFFFFFF) + { + std::out_of_range ex("address_v4 from unsigned long"); + boost::throw_exception(ex); + } +#endif // ULONG_MAX > 0xFFFFFFFF + + addr_.s_addr = asio::detail::socket_ops::host_to_network_long(addr); +} + +ASIO_DECL +address_v4::bytes_type address_v4::to_bytes() const +{ + using namespace std; // For memcpy. + bytes_type bytes; + memcpy(bytes.elems, &addr_.s_addr, 4); + return bytes; +} + +ASIO_DECL +unsigned long address_v4::to_ulong() const +{ + return asio::detail::socket_ops::network_to_host_long(addr_.s_addr); +} + +ASIO_DECL +std::string address_v4::to_string() const +{ + asio::error_code ec; + std::string addr = to_string(ec); + asio::detail::throw_error(ec); + return addr; +} + +ASIO_DECL +std::string address_v4::to_string(asio::error_code& ec) const +{ + char addr_str[asio::detail::max_addr_v4_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop(AF_INET, &addr_, addr_str, + asio::detail::max_addr_v4_str_len, 0, ec); + if (addr == 0) + return std::string(); + return addr; +} + +ASIO_DECL +address_v4 address_v4::from_string(const char* str) +{ + asio::error_code ec; + address_v4 addr = from_string(str, ec); + asio::detail::throw_error(ec); + return addr; +} + +ASIO_DECL +address_v4 address_v4::from_string( + const char* str, asio::error_code& ec) +{ + address_v4 tmp; + if (asio::detail::socket_ops::inet_pton( + AF_INET, str, &tmp.addr_, 0, ec) <= 0) + return address_v4(); + return tmp; +} + +ASIO_DECL +address_v4 address_v4::from_string(const std::string& str) +{ + return from_string(str.c_str()); +} + +ASIO_DECL +address_v4 address_v4::from_string( + const std::string& str, asio::error_code& ec) +{ + return from_string(str.c_str(), ec); +} + +ASIO_DECL +bool address_v4::is_class_a() const +{ + return IN_CLASSA(to_ulong()); +} + +ASIO_DECL +bool address_v4::is_class_b() const +{ + return IN_CLASSB(to_ulong()); +} + +ASIO_DECL +bool address_v4::is_class_c() const +{ + return IN_CLASSC(to_ulong()); +} + +ASIO_DECL +bool address_v4::is_multicast() const +{ + return IN_MULTICAST(to_ulong()); +} + +ASIO_DECL +address_v4 address_v4::broadcast(const address_v4& addr, const address_v4& mask) +{ + return address_v4(addr.to_ulong() | (mask.to_ulong() ^ 0xFFFFFFFF)); +} + +ASIO_DECL +address_v4 address_v4::netmask(const address_v4& addr) +{ + if (addr.is_class_a()) + return address_v4(0xFF000000); + if (addr.is_class_b()) + return address_v4(0xFFFF0000); + if (addr.is_class_c()) + return address_v4(0xFFFFFF00); + return address_v4(0xFFFFFFFF); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_ADDRESS_V4_IPP diff --git a/ext/asio/asio/ip/impl/address_v6.hpp b/ext/asio/asio/ip/impl/address_v6.hpp new file mode 100644 index 0000000000..796a505c0a --- /dev/null +++ b/ext/asio/asio/ip/impl/address_v6.hpp @@ -0,0 +1,53 @@ +// +// ip/impl/address_v6.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V6_HPP +#define ASIO_IP_IMPL_ADDRESS_V6_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(BOOST_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, const address_v6& addr) +{ + asio::error_code ec; + std::string s = addr.to_string(ec); + if (ec) + { + if (os.exceptions() & std::basic_ostream::failbit) + asio::detail::throw_error(ec); + else + os.setstate(std::basic_ostream::failbit); + } + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_ADDRESS_V6_HPP diff --git a/ext/asio/asio/ip/impl/address_v6.ipp b/ext/asio/asio/ip/impl/address_v6.ipp new file mode 100644 index 0000000000..17b6382c3b --- /dev/null +++ b/ext/asio/asio/ip/impl/address_v6.ipp @@ -0,0 +1,313 @@ +// +// ip/impl/address_v6.ipp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_ADDRESS_V6_IPP +#define ASIO_IP_IMPL_ADDRESS_V6_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include +#include +#include +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/ip/address_v6.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +ASIO_DECL +address_v6::address_v6() + : scope_id_(0) +{ + asio::detail::in6_addr_type tmp_addr = IN6ADDR_ANY_INIT; + addr_ = tmp_addr; +} + +ASIO_DECL +address_v6::address_v6(const address_v6::bytes_type& bytes, + unsigned long scope_id) + : scope_id_(scope_id) +{ +#if UCHAR_MAX > 0xFF + for (std::size_t i = 0; i < bytes.size(); ++i) + { + if (bytes[i] > 0xFF) + { + std::out_of_range ex("address_v6 from bytes_type"); + boost::throw_exception(ex); + } + } +#endif // UCHAR_MAX > 0xFF + + using namespace std; // For memcpy. + memcpy(addr_.s6_addr, bytes.elems, 16); +} + +ASIO_DECL +address_v6::address_v6(const address_v6& other) + : addr_(other.addr_), + scope_id_(other.scope_id_) +{ +} + +ASIO_DECL +address_v6& address_v6::operator=(const address_v6& other) +{ + addr_ = other.addr_; + scope_id_ = other.scope_id_; + return *this; +} + +ASIO_DECL +address_v6::bytes_type address_v6::to_bytes() const +{ + using namespace std; // For memcpy. + bytes_type bytes; + memcpy(bytes.elems, addr_.s6_addr, 16); + return bytes; +} + +ASIO_DECL +std::string address_v6::to_string() const +{ + asio::error_code ec; + std::string addr = to_string(ec); + asio::detail::throw_error(ec); + return addr; +} + +ASIO_DECL +std::string address_v6::to_string(asio::error_code& ec) const +{ + char addr_str[asio::detail::max_addr_v6_str_len]; + const char* addr = + asio::detail::socket_ops::inet_ntop(AF_INET6, &addr_, addr_str, + asio::detail::max_addr_v6_str_len, scope_id_, ec); + if (addr == 0) + return std::string(); + return addr; +} + +ASIO_DECL +address_v6 address_v6::from_string(const char* str) +{ + asio::error_code ec; + address_v6 addr = from_string(str, ec); + asio::detail::throw_error(ec); + return addr; +} + +ASIO_DECL +address_v6 address_v6::from_string( + const char* str, asio::error_code& ec) +{ + address_v6 tmp; + if (asio::detail::socket_ops::inet_pton( + AF_INET6, str, &tmp.addr_, &tmp.scope_id_, ec) <= 0) + return address_v6(); + return tmp; +} + +ASIO_DECL +address_v6 address_v6::from_string(const std::string& str) +{ + return from_string(str.c_str()); +} + +ASIO_DECL +address_v6 address_v6::from_string( + const std::string& str, asio::error_code& ec) +{ + return from_string(str.c_str(), ec); +} + +ASIO_DECL +address_v4 address_v6::to_v4() const +{ + if (!is_v4_mapped() && !is_v4_compatible()) + { + std::bad_cast ex; + boost::throw_exception(ex); + } + + address_v4::bytes_type v4_bytes = { { addr_.s6_addr[12], + addr_.s6_addr[13], addr_.s6_addr[14], addr_.s6_addr[15] } }; + return address_v4(v4_bytes); +} + +ASIO_DECL +bool address_v6::is_loopback() const +{ +#if defined(__BORLANDC__) + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 1)); +#else + using namespace asio::detail; + return IN6_IS_ADDR_LOOPBACK(&addr_) != 0; +#endif +} + +ASIO_DECL +bool address_v6::is_unspecified() const +{ +#if defined(__BORLANDC__) + return ((addr_.s6_addr[0] == 0) && (addr_.s6_addr[1] == 0) + && (addr_.s6_addr[2] == 0) && (addr_.s6_addr[3] == 0) + && (addr_.s6_addr[4] == 0) && (addr_.s6_addr[5] == 0) + && (addr_.s6_addr[6] == 0) && (addr_.s6_addr[7] == 0) + && (addr_.s6_addr[8] == 0) && (addr_.s6_addr[9] == 0) + && (addr_.s6_addr[10] == 0) && (addr_.s6_addr[11] == 0) + && (addr_.s6_addr[12] == 0) && (addr_.s6_addr[13] == 0) + && (addr_.s6_addr[14] == 0) && (addr_.s6_addr[15] == 0)); +#else + using namespace asio::detail; + return IN6_IS_ADDR_UNSPECIFIED(&addr_) != 0; +#endif +} + +ASIO_DECL +bool address_v6::is_link_local() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_LINKLOCAL(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_site_local() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_SITELOCAL(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_v4_mapped() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_V4MAPPED(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_v4_compatible() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_V4COMPAT(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_multicast() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_MULTICAST(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_multicast_global() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_MC_GLOBAL(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_multicast_link_local() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_MC_LINKLOCAL(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_multicast_node_local() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_MC_NODELOCAL(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_multicast_org_local() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_MC_ORGLOCAL(&addr_) != 0; +} + +ASIO_DECL +bool address_v6::is_multicast_site_local() const +{ + using namespace asio::detail; + return IN6_IS_ADDR_MC_SITELOCAL(&addr_) != 0; +} + +ASIO_DECL +bool operator==(const address_v6& a1, const address_v6& a2) +{ + using namespace std; // For memcmp. + return memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)) == 0 + && a1.scope_id_ == a2.scope_id_; +} + +ASIO_DECL +bool operator<(const address_v6& a1, const address_v6& a2) +{ + using namespace std; // For memcmp. + int memcmp_result = memcmp(&a1.addr_, &a2.addr_, + sizeof(asio::detail::in6_addr_type)); + if (memcmp_result < 0) + return true; + if (memcmp_result > 0) + return false; + return a1.scope_id_ < a2.scope_id_; +} + +ASIO_DECL +address_v6 address_v6::loopback() +{ + address_v6 tmp; + asio::detail::in6_addr_type tmp_addr = IN6ADDR_LOOPBACK_INIT; + tmp.addr_ = tmp_addr; + return tmp; +} + +ASIO_DECL +address_v6 address_v6::v4_mapped(const address_v4& addr) +{ + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; + return address_v6(v6_bytes); +} + +ASIO_DECL +address_v6 address_v6::v4_compatible(const address_v4& addr) +{ + address_v4::bytes_type v4_bytes = addr.to_bytes(); + bytes_type v6_bytes = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + v4_bytes[0], v4_bytes[1], v4_bytes[2], v4_bytes[3] } }; + return address_v6(v6_bytes); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_ADDRESS_V6_IPP diff --git a/ext/asio/asio/ip/impl/basic_endpoint.hpp b/ext/asio/asio/ip/impl/basic_endpoint.hpp new file mode 100644 index 0000000000..db7d5fb27e --- /dev/null +++ b/ext/asio/asio/ip/impl/basic_endpoint.hpp @@ -0,0 +1,55 @@ +// +// ip/impl/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_BASIC_ENDPOINT_HPP +#define ASIO_IP_IMPL_BASIC_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#if !defined(BOOST_NO_IOSTREAM) + +#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const basic_endpoint& endpoint) +{ + asio::ip::detail::endpoint tmp_ep(endpoint.address(), endpoint.port()); + asio::error_code ec; + std::string s = tmp_ep.to_string(ec); + if (ec) + { + if (os.exceptions() & std::basic_ostream::failbit) + asio::detail::throw_error(ec); + else + os.setstate(std::basic_ostream::failbit); + } + else + for (std::string::iterator i = s.begin(); i != s.end(); ++i) + os << os.widen(*i); + return os; +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // !defined(BOOST_NO_IOSTREAM) + +#endif // ASIO_IP_IMPL_BASIC_ENDPOINT_HPP diff --git a/ext/asio/asio/ip/impl/host_name.ipp b/ext/asio/asio/ip/impl/host_name.ipp new file mode 100644 index 0000000000..5bf5cca431 --- /dev/null +++ b/ext/asio/asio/ip/impl/host_name.ipp @@ -0,0 +1,56 @@ +// +// ip/impl/host_name.ipp +// ~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_IP_IMPL_HOST_NAME_IPP +#define ASIO_IP_IMPL_HOST_NAME_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/detail/winsock_init.hpp" +#include "asio/ip/host_name.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace ip { + +ASIO_DECL +std::string host_name() +{ + char name[1024]; + asio::error_code ec; + if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) + { + asio::detail::throw_error(ec); + return std::string(); + } + return std::string(name); +} + +ASIO_DECL +std::string host_name(asio::error_code& ec) +{ + char name[1024]; + if (asio::detail::socket_ops::gethostname(name, sizeof(name), ec) != 0) + return std::string(); + return std::string(name); +} + +} // namespace ip +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_IP_IMPL_HOST_NAME_IPP diff --git a/ext/asio/asio/ip/multicast.hpp b/ext/asio/asio/ip/multicast.hpp index eeedc6ce19..f2193c0817 100644 --- a/ext/asio/asio/ip/multicast.hpp +++ b/ext/asio/asio/ip/multicast.hpp @@ -1,8 +1,8 @@ // -// multicast.hpp -// ~~~~~~~~~~~~~ +// ip/multicast.hpp +// ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/ip/detail/socket_option.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ip { namespace multicast { diff --git a/ext/asio/asio/ip/resolver_query_base.hpp b/ext/asio/asio/ip/resolver_query_base.hpp index 5a0acea8f3..8963f41dd8 100644 --- a/ext/asio/asio/ip/resolver_query_base.hpp +++ b/ext/asio/asio/ip/resolver_query_base.hpp @@ -1,8 +1,8 @@ // -// resolver_query_base.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~ +// ip/resolver_query_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/resolver_service.hpp b/ext/asio/asio/ip/resolver_service.hpp index 75f6b2c3af..0d866d6610 100644 --- a/ext/asio/asio/ip/resolver_service.hpp +++ b/ext/asio/asio/ip/resolver_service.hpp @@ -1,8 +1,8 @@ // -// resolver_service.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// ip/resolver_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/error.hpp" +#include "asio/detail/config.hpp" +#include "asio/error_code.hpp" +#include "asio/detail/resolver_service.hpp" #include "asio/io_service.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" -#include "asio/detail/resolver_service.hpp" -#include "asio/detail/service_base.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { @@ -72,13 +72,14 @@ public: explicit resolver_service(asio::io_service& io_service) : asio::detail::service_base< resolver_service >(io_service), - service_impl_(asio::use_service(io_service)) + service_impl_(io_service) { } /// Destroy all user-defined handler objects owned by the service. void shutdown_service() { + service_impl_.shutdown_service(); } /// Construct a new resolver implementation. @@ -130,8 +131,8 @@ public: } private: - // The service that provides the platform-specific implementation. - service_impl_type& service_impl_; + // The platform-specific implementation. + service_impl_type service_impl_; }; } // namespace ip diff --git a/ext/asio/asio/ip/tcp.hpp b/ext/asio/asio/ip/tcp.hpp index a2e9ac9c52..67fb09f122 100644 --- a/ext/asio/asio/ip/tcp.hpp +++ b/ext/asio/asio/ip/tcp.hpp @@ -1,8 +1,8 @@ // -// tcp.hpp -// ~~~~~~~ +// ip/tcp.hpp +// ~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/basic_socket_acceptor.hpp" #include "asio/basic_socket_iostream.hpp" #include "asio/basic_stream_socket.hpp" +#include "asio/detail/socket_option.hpp" +#include "asio/detail/socket_types.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" -#include "asio/detail/socket_option.hpp" -#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/udp.hpp b/ext/asio/asio/ip/udp.hpp index fb261187a8..ca3ac785f5 100644 --- a/ext/asio/asio/ip/udp.hpp +++ b/ext/asio/asio/ip/udp.hpp @@ -1,8 +1,8 @@ // -// udp.hpp -// ~~~~~~~ +// ip/udp.hpp +// ~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/basic_datagram_socket.hpp" +#include "asio/detail/socket_types.hpp" #include "asio/ip/basic_endpoint.hpp" #include "asio/ip/basic_resolver.hpp" #include "asio/ip/basic_resolver_iterator.hpp" #include "asio/ip/basic_resolver_query.hpp" -#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ip { diff --git a/ext/asio/asio/ip/unicast.hpp b/ext/asio/asio/ip/unicast.hpp index 46d7239c8e..e3d04e5899 100644 --- a/ext/asio/asio/ip/unicast.hpp +++ b/ext/asio/asio/ip/unicast.hpp @@ -1,8 +1,8 @@ // -// unicast.hpp -// ~~~~~~~~~~~ +// ip/unicast.hpp +// ~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/ip/detail/socket_option.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ip { namespace unicast { diff --git a/ext/asio/asio/ip/v6_only.hpp b/ext/asio/asio/ip/v6_only.hpp index 928caff0df..5ac6a37f7f 100644 --- a/ext/asio/asio/ip/v6_only.hpp +++ b/ext/asio/asio/ip/v6_only.hpp @@ -1,8 +1,8 @@ // -// v6_only.hpp -// ~~~~~~~~~~~ +// ip/v6_only.hpp +// ~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,10 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/socket_option.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ip { diff --git a/ext/asio/asio/is_read_buffered.hpp b/ext/asio/asio/is_read_buffered.hpp index 8d971747a3..e48de55227 100644 --- a/ext/asio/asio/is_read_buffered.hpp +++ b/ext/asio/asio/is_read_buffered.hpp @@ -2,7 +2,7 @@ // is_read_buffered.hpp // ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/buffered_read_stream_fwd.hpp" #include "asio/buffered_stream_fwd.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/is_write_buffered.hpp b/ext/asio/asio/is_write_buffered.hpp index 5d16b1c542..c681a2bb92 100644 --- a/ext/asio/asio/is_write_buffered.hpp +++ b/ext/asio/asio/is_write_buffered.hpp @@ -2,7 +2,7 @@ // is_write_buffered.hpp // ~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/buffered_stream_fwd.hpp" #include "asio/buffered_write_stream_fwd.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail { diff --git a/ext/asio/asio/local/basic_endpoint.hpp b/ext/asio/asio/local/basic_endpoint.hpp index 81e6a7ed57..d2d0a023e7 100644 --- a/ext/asio/asio/local/basic_endpoint.hpp +++ b/ext/asio/asio/local/basic_endpoint.hpp @@ -1,8 +1,8 @@ // -// basic_endpoint.hpp -// ~~~~~~~~~~~~~~~~~~ +// local/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Derived from a public domain implementation written by Daniel Casimiro. // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,30 +16,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/system_error.hpp" -#include "asio/detail/socket_ops.hpp" -#include "asio/detail/socket_types.hpp" -#include "asio/detail/throw_error.hpp" - -#if !defined(ASIO_DISABLE_LOCAL_SOCKETS) -# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -# define ASIO_HAS_LOCAL_SOCKETS 1 -# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -#endif // !defined(ASIO_DISABLE_LOCAL_SOCKETS) +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/local/detail/endpoint.hpp" + +#if !defined(BOOST_NO_IOSTREAM) +# include +#endif // !defined(BOOST_NO_IOSTREAM) + +#include "asio/detail/push_options.hpp" namespace asio { namespace local { @@ -74,34 +62,30 @@ public: /// Default constructor. basic_endpoint() { - init("", 0); } /// Construct an endpoint using the specified path name. basic_endpoint(const char* path) + : impl_(path) { - using namespace std; // For strlen. - init(path, strlen(path)); } /// Construct an endpoint using the specified path name. basic_endpoint(const std::string& path) + : impl_(path) { - init(path.data(), path.length()); } /// Copy constructor. basic_endpoint(const basic_endpoint& other) - : data_(other.data_), - path_length_(other.path_length_) + : impl_(other.impl_) { } /// Assign from another endpoint. basic_endpoint& operator=(const basic_endpoint& other) { - data_ = other.data_; - path_length_ = other.path_length_; + impl_ = other.impl_; return *this; } @@ -114,123 +98,96 @@ public: /// Get the underlying endpoint in the native type. data_type* data() { - return &data_.base; + return impl_.data(); } /// Get the underlying endpoint in the native type. const data_type* data() const { - return &data_.base; + return impl_.data(); } /// Get the underlying size of the endpoint in the native type. std::size_t size() const { - return path_length_ - + offsetof(asio::detail::sockaddr_un_type, sun_path); + return impl_.size(); } /// Set the underlying size of the endpoint in the native type. void resize(std::size_t size) { - if (size > sizeof(asio::detail::sockaddr_un_type)) - { - asio::system_error e(asio::error::invalid_argument); - boost::throw_exception(e); - } - else if (size == 0) - { - path_length_ = 0; - } - else - { - path_length_ = size - - offsetof(asio::detail::sockaddr_un_type, sun_path); - - // The path returned by the operating system may be NUL-terminated. - if (path_length_ > 0 && data_.local.sun_path[path_length_ - 1] == 0) - --path_length_; - } + impl_.resize(size); } /// Get the capacity of the endpoint in the native type. std::size_t capacity() const { - return sizeof(asio::detail::sockaddr_un_type); + return impl_.capacity(); } /// Get the path associated with the endpoint. std::string path() const { - return std::string(data_.local.sun_path, path_length_); + return impl_.path(); } /// Set the path associated with the endpoint. void path(const char* p) { - using namespace std; // For strlen. - init(p, strlen(p)); + impl_.path(p); } /// Set the path associated with the endpoint. void path(const std::string& p) { - init(p.data(), p.length()); + impl_.path(p); } /// Compare two endpoints for equality. friend bool operator==(const basic_endpoint& e1, const basic_endpoint& e2) { - return e1.path() == e2.path(); + return e1.impl_ == e2.impl_; } /// Compare two endpoints for inequality. friend bool operator!=(const basic_endpoint& e1, const basic_endpoint& e2) { - return e1.path() != e2.path(); + return !(e1.impl_ == e2.impl_); } /// Compare endpoints for ordering. friend bool operator<(const basic_endpoint& e1, const basic_endpoint& e2) { - return e1.path() < e2.path(); + return e1.impl_ < e2.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator>(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return e2.impl_ < e1.impl_; + } + + /// Compare endpoints for ordering. + friend bool operator<=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e2 < e1); + } + + /// Compare endpoints for ordering. + friend bool operator>=(const basic_endpoint& e1, + const basic_endpoint& e2) + { + return !(e1 < e2); } private: - // The underlying UNIX socket address. - union data_union - { - asio::detail::socket_addr_type base; - asio::detail::sockaddr_un_type local; - } data_; - - // The length of the path associated with the endpoint. - std::size_t path_length_; - - // Initialise with a specified path. - void init(const char* path, std::size_t path_length) - { - if (path_length > sizeof(data_.local.sun_path) - 1) - { - // The buffer is not large enough to store this address. - asio::error_code ec(asio::error::name_too_long); - asio::detail::throw_error(ec); - } - - using namespace std; // For memcpy. - data_.local = asio::detail::sockaddr_un_type(); - data_.local.sun_family = AF_UNIX; - memcpy(data_.local.sun_path, path, path_length); - path_length_ = path_length; - - // NUL-terminate normal path names. Names that start with a NUL are in the - // UNIX domain protocol's "abstract namespace" and are not NUL-terminated. - if (path_length > 0 && data_.local.sun_path[0] == 0) - data_.local.sun_path[path_length] = 0; - } + // The underlying UNIX domain endpoint. + asio::local::detail::endpoint impl_; }; /// Output an endpoint as a string. @@ -257,9 +214,9 @@ std::basic_ostream& operator<<( } // namespace local } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_LOCAL_BASIC_ENDPOINT_HPP diff --git a/ext/asio/asio/local/connect_pair.hpp b/ext/asio/asio/local/connect_pair.hpp index da1d4fc5bd..4a582477e3 100644 --- a/ext/asio/asio/local/connect_pair.hpp +++ b/ext/asio/asio/local/connect_pair.hpp @@ -1,8 +1,8 @@ // -// connect_pair.hpp -// ~~~~~~~~~~~~~~~~ +// local/connect_pair.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,19 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/basic_socket.hpp" -#include "asio/error.hpp" -#include "asio/local/basic_endpoint.hpp" -#include "asio/detail/socket_ops.hpp" -#include "asio/detail/throw_error.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/basic_socket.hpp" +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/local/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace local { @@ -73,8 +75,9 @@ inline asio::error_code connect_pair( if (socket1.assign(protocol, sv[0], ec)) { asio::error_code temp_ec; - asio::detail::socket_ops::close(sv[0], temp_ec); - asio::detail::socket_ops::close(sv[1], temp_ec); + asio::detail::socket_ops::state_type state[2] = { 0, 0 }; + asio::detail::socket_ops::close(sv[0], state[0], true, temp_ec); + asio::detail::socket_ops::close(sv[1], state[1], true, temp_ec); return ec; } @@ -82,7 +85,8 @@ inline asio::error_code connect_pair( { asio::error_code temp_ec; socket1.close(temp_ec); - asio::detail::socket_ops::close(sv[1], temp_ec); + asio::detail::socket_ops::state_type state = 0; + asio::detail::socket_ops::close(sv[1], state, true, temp_ec); return ec; } @@ -92,9 +96,9 @@ inline asio::error_code connect_pair( } // namespace local } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_LOCAL_CONNECT_PAIR_HPP diff --git a/ext/asio/asio/local/datagram_protocol.hpp b/ext/asio/asio/local/datagram_protocol.hpp index 0340180545..39f47ce7d5 100644 --- a/ext/asio/asio/local/datagram_protocol.hpp +++ b/ext/asio/asio/local/datagram_protocol.hpp @@ -1,8 +1,8 @@ // -// datagram_protocol.hpp -// ~~~~~~~~~~~~~~~~~~~~~ +// local/datagram_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/basic_datagram_socket.hpp" -#include "asio/local/basic_endpoint.hpp" -#include "asio/detail/socket_types.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_LOCAL_SOCKETS) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/basic_datagram_socket.hpp" +#include "asio/detail/socket_types.hpp" +#include "asio/local/basic_endpoint.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace local { @@ -70,9 +72,9 @@ public: } // namespace local } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_LOCAL_DATAGRAM_PROTOCOL_HPP diff --git a/ext/asio/asio/local/detail/endpoint.hpp b/ext/asio/asio/local/detail/endpoint.hpp new file mode 100644 index 0000000000..05fc992383 --- /dev/null +++ b/ext/asio/asio/local/detail/endpoint.hpp @@ -0,0 +1,133 @@ +// +// local/detail/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Derived from a public domain implementation written by Daniel Casimiro. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_DETAIL_ENDPOINT_HPP +#define ASIO_LOCAL_DETAIL_ENDPOINT_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) + +#include +#include +#include "asio/detail/socket_types.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { +namespace detail { + +// Helper class for implementing a UNIX domain endpoint. +class endpoint +{ +public: + // Default constructor. + ASIO_DECL endpoint(); + + // Construct an endpoint using the specified path name. + ASIO_DECL endpoint(const char* path); + + // Construct an endpoint using the specified path name. + ASIO_DECL endpoint(const std::string& path); + + // Copy constructor. + endpoint(const endpoint& other) + : data_(other.data_), + path_length_(other.path_length_) + { + } + + // Assign from another endpoint. + endpoint& operator=(const endpoint& other) + { + data_ = other.data_; + path_length_ = other.path_length_; + return *this; + } + + // Get the underlying endpoint in the native type. + asio::detail::socket_addr_type* data() + { + return &data_.base; + } + + // Get the underlying endpoint in the native type. + const asio::detail::socket_addr_type* data() const + { + return &data_.base; + } + + // Get the underlying size of the endpoint in the native type. + std::size_t size() const + { + return path_length_ + + offsetof(asio::detail::sockaddr_un_type, sun_path); + } + + // Set the underlying size of the endpoint in the native type. + ASIO_DECL void resize(std::size_t size); + + // Get the capacity of the endpoint in the native type. + std::size_t capacity() const + { + return sizeof(asio::detail::sockaddr_un_type); + } + + // Get the path associated with the endpoint. + ASIO_DECL std::string path() const; + + // Set the path associated with the endpoint. + ASIO_DECL void path(const char* p); + + // Set the path associated with the endpoint. + ASIO_DECL void path(const std::string& p); + + // Compare two endpoints for equality. + ASIO_DECL friend bool operator==( + const endpoint& e1, const endpoint& e2); + + // Compare endpoints for ordering. + ASIO_DECL friend bool operator<( + const endpoint& e1, const endpoint& e2); + +private: + // The underlying UNIX socket address. + union data_union + { + asio::detail::socket_addr_type base; + asio::detail::sockaddr_un_type local; + } data_; + + // The length of the path associated with the endpoint. + std::size_t path_length_; + + // Initialise with a specified path. + ASIO_DECL void init(const char* path, std::size_t path_length); +}; + +} // namespace detail +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#if defined(ASIO_HEADER_ONLY) +# include "asio/local/detail/impl/endpoint.ipp" +#endif // defined(ASIO_HEADER_ONLY) + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + +#endif // ASIO_LOCAL_DETAIL_ENDPOINT_HPP diff --git a/ext/asio/asio/local/detail/impl/endpoint.ipp b/ext/asio/asio/local/detail/impl/endpoint.ipp new file mode 100644 index 0000000000..1bc2c949a5 --- /dev/null +++ b/ext/asio/asio/local/detail/impl/endpoint.ipp @@ -0,0 +1,138 @@ +// +// local/detail/impl/endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Derived from a public domain implementation written by Daniel Casimiro. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP +#define ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) + +#include +#include "asio/detail/socket_ops.hpp" +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/local/detail/endpoint.hpp" + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace local { +namespace detail { + +ASIO_DECL +endpoint::endpoint() +{ + init("", 0); +} + +ASIO_DECL +endpoint::endpoint(const char* path) +{ + using namespace std; // For strlen. + init(path, strlen(path)); +} + +ASIO_DECL +endpoint::endpoint(const std::string& path) +{ + init(path.data(), path.length()); +} + +ASIO_DECL +void endpoint::resize(std::size_t size) +{ + if (size > sizeof(asio::detail::sockaddr_un_type)) + { + asio::error_code ec(asio::error::invalid_argument); + asio::detail::throw_error(ec); + } + else if (size == 0) + { + path_length_ = 0; + } + else + { + path_length_ = size + - offsetof(asio::detail::sockaddr_un_type, sun_path); + + // The path returned by the operating system may be NUL-terminated. + if (path_length_ > 0 && data_.local.sun_path[path_length_ - 1] == 0) + --path_length_; + } +} + +ASIO_DECL +std::string endpoint::path() const +{ + return std::string(data_.local.sun_path, path_length_); +} + +ASIO_DECL +void endpoint::path(const char* p) +{ + using namespace std; // For strlen. + init(p, strlen(p)); +} + +ASIO_DECL +void endpoint::path(const std::string& p) +{ + init(p.data(), p.length()); +} + +ASIO_DECL +bool operator==(const endpoint& e1, const endpoint& e2) +{ + return e1.path() == e2.path(); +} + +ASIO_DECL +bool operator<(const endpoint& e1, const endpoint& e2) +{ + return e1.path() < e2.path(); +} + +ASIO_DECL +void endpoint::init(const char* path, std::size_t path_length) +{ + if (path_length > sizeof(data_.local.sun_path) - 1) + { + // The buffer is not large enough to store this address. + asio::error_code ec(asio::error::name_too_long); + asio::detail::throw_error(ec); + } + + using namespace std; // For memcpy. + data_.local = asio::detail::sockaddr_un_type(); + data_.local.sun_family = AF_UNIX; + memcpy(data_.local.sun_path, path, path_length); + path_length_ = path_length; + + // NUL-terminate normal path names. Names that start with a NUL are in the + // UNIX domain protocol's "abstract namespace" and are not NUL-terminated. + if (path_length > 0 && data_.local.sun_path[0] == 0) + data_.local.sun_path[path_length] = 0; +} + +} // namespace detail +} // namespace local +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // defined(ASIO_HAS_LOCAL_SOCKETS) + +#endif // ASIO_LOCAL_DETAIL_IMPL_ENDPOINT_IPP diff --git a/ext/asio/asio/local/stream_protocol.hpp b/ext/asio/asio/local/stream_protocol.hpp index 47fe42f132..e65a2f6dfb 100644 --- a/ext/asio/asio/local/stream_protocol.hpp +++ b/ext/asio/asio/local/stream_protocol.hpp @@ -1,8 +1,8 @@ // -// stream_protocol.hpp -// ~~~~~~~~~~~~~~~~~~~ +// local/stream_protocol.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_LOCAL_SOCKETS) \ + || defined(GENERATING_DOCUMENTATION) #include "asio/basic_socket_acceptor.hpp" #include "asio/basic_socket_iostream.hpp" #include "asio/basic_stream_socket.hpp" -#include "asio/local/basic_endpoint.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/local/basic_endpoint.hpp" -#if defined(ASIO_HAS_LOCAL_SOCKETS) \ - || defined(GENERATING_DOCUMENTATION) +#include "asio/detail/push_options.hpp" namespace asio { namespace local { @@ -80,9 +82,9 @@ public: } // namespace local } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_LOCAL_SOCKETS) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_LOCAL_STREAM_PROTOCOL_HPP diff --git a/ext/asio/asio/placeholders.hpp b/ext/asio/asio/placeholders.hpp index 70e69fca53..6fda487d7f 100644 --- a/ext/asio/asio/placeholders.hpp +++ b/ext/asio/asio/placeholders.hpp @@ -2,7 +2,7 @@ // placeholders.hpp // ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include "asio/detail/pop_options.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace placeholders { diff --git a/ext/asio/asio/posix/basic_descriptor.hpp b/ext/asio/asio/posix/basic_descriptor.hpp index 37bcc94dfc..591e65a6d9 100644 --- a/ext/asio/asio/posix/basic_descriptor.hpp +++ b/ext/asio/asio/posix/basic_descriptor.hpp @@ -1,8 +1,8 @@ // -// basic_descriptor.hpp -// ~~~~~~~~~~~~~~~~~~~~ +// posix/basic_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) #include "asio/basic_io_object.hpp" +#include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/posix/descriptor_base.hpp" -#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace posix { @@ -291,4 +292,7 @@ protected: #include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + #endif // ASIO_POSIX_BASIC_DESCRIPTOR_HPP diff --git a/ext/asio/asio/posix/basic_stream_descriptor.hpp b/ext/asio/asio/posix/basic_stream_descriptor.hpp index 21e2287dbf..8211c58d06 100644 --- a/ext/asio/asio/posix/basic_stream_descriptor.hpp +++ b/ext/asio/asio/posix/basic_stream_descriptor.hpp @@ -1,8 +1,8 @@ // -// basic_stream_descriptor.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// posix/basic_stream_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,21 +15,19 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/posix/basic_descriptor.hpp" -#include "asio/posix/stream_descriptor_service.hpp" -#include "asio/detail/throw_error.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) +#include +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/posix/basic_descriptor.hpp" +#include "asio/posix/stream_descriptor_service.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace posix { @@ -296,9 +294,9 @@ public: } // namespace posix } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_POSIX_BASIC_STREAM_DESCRIPTOR_HPP diff --git a/ext/asio/asio/posix/descriptor_base.hpp b/ext/asio/asio/posix/descriptor_base.hpp index 29e17469df..2222915678 100644 --- a/ext/asio/asio/posix/descriptor_base.hpp +++ b/ext/asio/asio/posix/descriptor_base.hpp @@ -1,8 +1,8 @@ // -// descriptor_base.hpp -// ~~~~~~~~~~~~~~~~~~~ +// posix/descriptor_base.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,16 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" +#if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ + || defined(GENERATING_DOCUMENTATION) #include "asio/detail/io_control.hpp" #include "asio/detail/socket_option.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace posix { @@ -90,4 +90,7 @@ protected: #include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) + // || defined(GENERATING_DOCUMENTATION) + #endif // ASIO_POSIX_DESCRIPTOR_BASE_HPP diff --git a/ext/asio/asio/posix/stream_descriptor.hpp b/ext/asio/asio/posix/stream_descriptor.hpp index 72fbbed23c..48c20a3686 100644 --- a/ext/asio/asio/posix/stream_descriptor.hpp +++ b/ext/asio/asio/posix/stream_descriptor.hpp @@ -1,8 +1,8 @@ // -// stream_descriptor.hpp -// ~~~~~~~~~~~~~~~~~~~~~ +// posix/stream_descriptor.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/posix/basic_stream_descriptor.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/posix/basic_stream_descriptor.hpp" + namespace asio { namespace posix { @@ -34,6 +34,4 @@ typedef basic_stream_descriptor<> stream_descriptor; #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_POSIX_STREAM_DESCRIPTOR_HPP diff --git a/ext/asio/asio/posix/stream_descriptor_service.hpp b/ext/asio/asio/posix/stream_descriptor_service.hpp index 61cee1b54d..4660e79dab 100644 --- a/ext/asio/asio/posix/stream_descriptor_service.hpp +++ b/ext/asio/asio/posix/stream_descriptor_service.hpp @@ -1,8 +1,8 @@ // -// stream_descriptor_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// posix/stream_descriptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,28 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" - -#if !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) -# if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -# define ASIO_HAS_POSIX_STREAM_DESCRIPTOR 1 -# endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -#endif // !defined(ASIO_DISABLE_POSIX_STREAM_DESCRIPTOR) +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) \ || defined(GENERATING_DOCUMENTATION) +#include +#include "asio/error.hpp" +#include "asio/io_service.hpp" #include "asio/detail/reactive_descriptor_service.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace posix { @@ -179,9 +169,9 @@ private: } // namespace posix } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_POSIX_STREAM_DESCRIPTOR) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_POSIX_STREAM_DESCRIPTOR_SERVICE_HPP diff --git a/ext/asio/asio/raw_socket_service.hpp b/ext/asio/asio/raw_socket_service.hpp index a8973d3445..3d0016d328 100644 --- a/ext/asio/asio/raw_socket_service.hpp +++ b/ext/asio/asio/raw_socket_service.hpp @@ -2,7 +2,7 @@ // raw_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" @@ -32,6 +26,8 @@ # include "asio/detail/reactive_socket_service.hpp" #endif +#include "asio/detail/push_options.hpp" + namespace asio { /// Default service implementation for a raw socket. diff --git a/ext/asio/asio/read.hpp b/ext/asio/asio/read.hpp index 859c05a003..7f04b07dbd 100644 --- a/ext/asio/asio/read.hpp +++ b/ext/asio/asio/read.hpp @@ -2,7 +2,7 @@ // read.hpp // ~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/basic_streambuf.hpp" +#include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /** @@ -536,8 +533,8 @@ void async_read(AsyncReadStream& s, basic_streambuf& b, } // namespace asio -#include "asio/impl/read.ipp" - #include "asio/detail/pop_options.hpp" +#include "asio/impl/read.hpp" + #endif // ASIO_READ_HPP diff --git a/ext/asio/asio/read_at.hpp b/ext/asio/asio/read_at.hpp index 6bb3fe125a..4be1f96f2b 100644 --- a/ext/asio/asio/read_at.hpp +++ b/ext/asio/asio/read_at.hpp @@ -2,7 +2,7 @@ // read_at.hpp // ~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/basic_streambuf.hpp" +#include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /** @@ -569,8 +566,8 @@ void async_read_at(AsyncRandomAccessReadDevice& d, } // namespace asio -#include "asio/impl/read_at.ipp" - #include "asio/detail/pop_options.hpp" +#include "asio/impl/read_at.hpp" + #endif // ASIO_READ_AT_HPP diff --git a/ext/asio/asio/read_until.hpp b/ext/asio/asio/read_until.hpp index 5df71ce29c..67fa066de1 100644 --- a/ext/asio/asio/read_until.hpp +++ b/ext/asio/asio/read_until.hpp @@ -2,7 +2,7 @@ // read_until.hpp // ~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,27 +15,22 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/push_options.hpp" #include -#include #include #include #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/basic_streambuf.hpp" +#include "asio/detail/regex_fwd.hpp" #include "asio/error.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace detail @@ -914,10 +909,10 @@ void async_read_until(AsyncReadStream& s, } // namespace asio -#include "asio/impl/read_until.ipp" +#include "asio/detail/pop_options.hpp" + +#include "asio/impl/read_until.hpp" #endif // !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_READ_UNTIL_HPP diff --git a/ext/asio/asio/serial_port.hpp b/ext/asio/asio/serial_port.hpp index a55a03aa07..4cc6fd443b 100644 --- a/ext/asio/asio/serial_port.hpp +++ b/ext/asio/asio/serial_port.hpp @@ -2,7 +2,7 @@ // serial_port.hpp // ~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,13 +16,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/basic_serial_port.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/basic_serial_port.hpp" + namespace asio { /// Typedef for the typical usage of a serial port. @@ -33,6 +33,4 @@ typedef basic_serial_port<> serial_port; #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_SERIAL_PORT_HPP diff --git a/ext/asio/asio/serial_port_base.hpp b/ext/asio/asio/serial_port_base.hpp index 28e51a08a0..9c7a773954 100644 --- a/ext/asio/asio/serial_port_base.hpp +++ b/ext/asio/asio/serial_port_base.hpp @@ -2,7 +2,7 @@ // serial_port_base.hpp // ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // Copyright (c) 2008 Rep Invariant Systems, Inc. (info@repinvariant.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -16,32 +16,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#if !defined(ASIO_DISABLE_SERIAL_PORT) -# if defined(ASIO_HAS_IOCP) \ - || !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -# define ASIO_HAS_SERIAL_PORT 1 -# endif // defined(ASIO_HAS_IOCP) -#endif // !defined(ASIO_DISABLE_STREAM_HANDLE) +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -# include "asio/detail/push_options.hpp" # include -# include "asio/detail/pop_options.hpp" #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -#include "asio/error_code.hpp" +#include #include "asio/detail/socket_types.hpp" +#include "asio/error_code.hpp" #if defined(GENERATING_DOCUMENTATION) # define ASIO_OPTION_STORAGE implementation_defined @@ -51,6 +37,8 @@ # define ASIO_OPTION_STORAGE termios #endif +#include "asio/detail/push_options.hpp" + namespace asio { /// The serial_port_base class is used as a base for the basic_serial_port class @@ -67,9 +55,11 @@ public: public: explicit baud_rate(unsigned int rate = 0); unsigned int value() const; - asio::error_code store(ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; - asio::error_code load(const ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: unsigned int value_; @@ -83,11 +73,13 @@ public: { public: enum type { none, software, hardware }; - explicit flow_control(type t = none); + ASIO_DECL explicit flow_control(type t = none); type value() const; - asio::error_code store(ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; - asio::error_code load(const ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: type value_; @@ -101,11 +93,13 @@ public: { public: enum type { none, odd, even }; - explicit parity(type t = none); + ASIO_DECL explicit parity(type t = none); type value() const; - asio::error_code store(ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; - asio::error_code load(const ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: type value_; @@ -119,11 +113,13 @@ public: { public: enum type { one, onepointfive, two }; - explicit stop_bits(type t = one); + ASIO_DECL explicit stop_bits(type t = one); type value() const; - asio::error_code store(ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; - asio::error_code load(const ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: type value_; @@ -136,11 +132,13 @@ public: class character_size { public: - explicit character_size(unsigned int t = 8); + ASIO_DECL explicit character_size(unsigned int t = 8); unsigned int value() const; - asio::error_code store(ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code store( + ASIO_OPTION_STORAGE& storage, asio::error_code& ec) const; - asio::error_code load(const ASIO_OPTION_STORAGE& storage, + ASIO_DECL asio::error_code load( + const ASIO_OPTION_STORAGE& storage, asio::error_code& ec); private: unsigned int value_; @@ -161,13 +159,16 @@ private: } // namespace asio -#include "asio/impl/serial_port_base.ipp" +#include "asio/detail/pop_options.hpp" #undef ASIO_OPTION_STORAGE +#include "asio/impl/serial_port_base.hpp" +#if defined(ASIO_HEADER_ONLY) +# include "asio/impl/serial_port_base.ipp" +#endif // defined(ASIO_HEADER_ONLY) + #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_SERIAL_PORT_BASE_HPP diff --git a/ext/asio/asio/serial_port_service.hpp b/ext/asio/asio/serial_port_service.hpp index 5847c293f5..37142d5b56 100644 --- a/ext/asio/asio/serial_port_service.hpp +++ b/ext/asio/asio/serial_port_service.hpp @@ -2,7 +2,7 @@ // serial_port_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,24 +15,21 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/io_service.hpp" -#include "asio/serial_port_base.hpp" -#include "asio/detail/service_base.hpp" -#include "asio/detail/reactive_serial_port_service.hpp" -#include "asio/detail/win_iocp_serial_port_service.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_SERIAL_PORT) \ || defined(GENERATING_DOCUMENTATION) +#include +#include +#include "asio/detail/reactive_serial_port_service.hpp" +#include "asio/detail/win_iocp_serial_port_service.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" +#include "asio/serial_port_base.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { /// Default service implementation for a serial port. @@ -199,9 +196,9 @@ private: } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_SERIAL_PORT) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_SERIAL_PORT_SERVICE_HPP diff --git a/ext/asio/asio/socket_acceptor_service.hpp b/ext/asio/asio/socket_acceptor_service.hpp index b2e2c6d2ec..a1e4874ebf 100644 --- a/ext/asio/asio/socket_acceptor_service.hpp +++ b/ext/asio/asio/socket_acceptor_service.hpp @@ -2,7 +2,7 @@ // socket_acceptor_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/basic_socket.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" @@ -28,6 +26,8 @@ # include "asio/detail/reactive_socket_service.hpp" #endif +#include "asio/detail/push_options.hpp" + namespace asio { /// Default service implementation for a socket acceptor. diff --git a/ext/asio/asio/socket_base.hpp b/ext/asio/asio/socket_base.hpp index d82cd22e8e..56585e18f4 100644 --- a/ext/asio/asio/socket_base.hpp +++ b/ext/asio/asio/socket_base.hpp @@ -2,7 +2,7 @@ // socket_base.hpp // ~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/io_control.hpp" #include "asio/detail/socket_option.hpp" #include "asio/detail/socket_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /// The socket_base class is used as a base for the basic_stream_socket and diff --git a/ext/asio/asio/ssl.hpp b/ext/asio/asio/ssl.hpp index a9fff5e6ed..48aff5a9b9 100644 --- a/ext/asio/asio/ssl.hpp +++ b/ext/asio/asio/ssl.hpp @@ -2,7 +2,7 @@ // ssl.hpp // ~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/ext/asio/asio/ssl/basic_context.hpp b/ext/asio/asio/ssl/basic_context.hpp index ea3893ed5a..20ed57dc2e 100755 --- a/ext/asio/asio/ssl/basic_context.hpp +++ b/ext/asio/asio/ssl/basic_context.hpp @@ -1,9 +1,9 @@ // -// basic_context.hpp -// ~~~~~~~~~~~~~~~~~ +// ssl/basic_context.hpp +// ~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,17 +16,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" #include "asio/ssl/context_base.hpp" -#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ssl { diff --git a/ext/asio/asio/ssl/context.hpp b/ext/asio/asio/ssl/context.hpp index d53882afa9..7aedc4dbd8 100644 --- a/ext/asio/asio/ssl/context.hpp +++ b/ext/asio/asio/ssl/context.hpp @@ -1,9 +1,9 @@ // -// context.hpp -// ~~~~~~~~~~~ +// ssl/context.hpp +// ~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,8 +16,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/ssl/basic_context.hpp" #include "asio/ssl/context_service.hpp" @@ -30,6 +29,4 @@ typedef basic_context context; } // namespace ssl } // namespace asio -#include "asio/detail/pop_options.hpp" - #endif // ASIO_SSL_CONTEXT_HPP diff --git a/ext/asio/asio/ssl/context_base.hpp b/ext/asio/asio/ssl/context_base.hpp index a0700ca27a..7636bedebb 100755 --- a/ext/asio/asio/ssl/context_base.hpp +++ b/ext/asio/asio/ssl/context_base.hpp @@ -1,8 +1,8 @@ // -// context_base.hpp -// ~~~~~~~~~~~~~~~~ +// ssl/context_base.hpp +// ~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include -#include "asio/detail/pop_options.hpp" - #include "asio/ssl/detail/openssl_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ssl { diff --git a/ext/asio/asio/ssl/context_service.hpp b/ext/asio/asio/ssl/context_service.hpp index e9cfef7ef2..766bbdee24 100755 --- a/ext/asio/asio/ssl/context_service.hpp +++ b/ext/asio/asio/ssl/context_service.hpp @@ -1,9 +1,9 @@ // -// context_service.hpp -// ~~~~~~~~~~~~~~~~~~~ +// ssl/context_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,19 +16,16 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" #include "asio/ssl/context_base.hpp" #include "asio/ssl/detail/openssl_context_service.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ssl { diff --git a/ext/asio/asio/ssl/detail/openssl_context_service.hpp b/ext/asio/asio/ssl/detail/openssl_context_service.hpp index a3d4fdb54c..7019542ee2 100755 --- a/ext/asio/asio/ssl/detail/openssl_context_service.hpp +++ b/ext/asio/asio/ssl/detail/openssl_context_service.hpp @@ -1,9 +1,9 @@ // -// openssl_context_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ssl/detail/openssl_context_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,21 +16,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" #include "asio/ssl/context_base.hpp" #include "asio/ssl/detail/openssl_init.hpp" #include "asio/ssl/detail/openssl_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ssl { namespace detail { diff --git a/ext/asio/asio/ssl/detail/openssl_init.hpp b/ext/asio/asio/ssl/detail/openssl_init.hpp index 9ecbb78ffd..dcda50f32c 100755 --- a/ext/asio/asio/ssl/detail/openssl_init.hpp +++ b/ext/asio/asio/ssl/detail/openssl_init.hpp @@ -1,9 +1,9 @@ // -// openssl_init.hpp -// ~~~~~~~~~~~~~~~~ +// ssl/detail/openssl_init.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,20 +16,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include -#include #include -#include "asio/detail/pop_options.hpp" - #include "asio/detail/mutex.hpp" #include "asio/detail/tss_ptr.hpp" #include "asio/ssl/detail/openssl_types.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { namespace ssl { namespace detail { diff --git a/ext/asio/asio/ssl/detail/openssl_operation.hpp b/ext/asio/asio/ssl/detail/openssl_operation.hpp index 8d237e3616..c5119a5915 100755 --- a/ext/asio/asio/ssl/detail/openssl_operation.hpp +++ b/ext/asio/asio/ssl/detail/openssl_operation.hpp @@ -1,6 +1,6 @@ // -// openssl_operation.hpp -// ~~~~~~~~~~~~~~~~~~~~~ +// ssl/detail/openssl_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // @@ -15,19 +15,19 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/buffer.hpp" -#include "asio/placeholders.hpp" -#include "asio/write.hpp" #include "asio/detail/socket_ops.hpp" +#include "asio/placeholders.hpp" #include "asio/ssl/detail/openssl_types.hpp" +#include "asio/strand.hpp" +#include "asio/system_error.hpp" +#include "asio/write.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ssl { @@ -160,7 +160,7 @@ public: if (error_code == SSL_ERROR_SSL) return handler_(asio::error_code( - error_code, asio::error::get_ssl_category()), rc); + sys_error_code, asio::error::get_ssl_category()), rc); bool is_read_needed = (error_code == SSL_ERROR_WANT_READ); bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE || @@ -195,7 +195,7 @@ public: else { return handler_(asio::error_code( - error_code, asio::error::get_ssl_category()), rc); + sys_error_code, asio::error::get_ssl_category()), rc); } } diff --git a/ext/asio/asio/ssl/detail/openssl_stream_service.hpp b/ext/asio/asio/ssl/detail/openssl_stream_service.hpp index d7bb457a9e..aef9e7e955 100644 --- a/ext/asio/asio/ssl/detail/openssl_stream_service.hpp +++ b/ext/asio/asio/ssl/detail/openssl_stream_service.hpp @@ -1,9 +1,9 @@ // -// stream_service.hpp -// ~~~~~~~~~~~~~~~~~~ +// ssl/detail/stream_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,9 +16,7 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include @@ -26,17 +24,17 @@ #include #include #include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/buffer_sequence_adapter.hpp" #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/strand.hpp" -#include "asio/detail/buffer_sequence_adapter.hpp" -#include "asio/detail/service_base.hpp" #include "asio/ssl/basic_context.hpp" #include "asio/ssl/stream_base.hpp" #include "asio/ssl/detail/openssl_operation.hpp" #include "asio/ssl/detail/openssl_types.hpp" +#include "asio/strand.hpp" +#include "asio/system_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ssl { @@ -92,7 +90,7 @@ private: : base_handler(io_service) , handler_(handler) { - set_func(boost::bind( + this->set_func(boost::bind( &io_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } @@ -116,7 +114,7 @@ private: : base_handler(io_service) , handler_(handler) { - set_func(boost::bind( + this->set_func(boost::bind( &handshake_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } @@ -141,7 +139,7 @@ private: : base_handler(io_service), handler_(handler) { - set_func(boost::bind( + this->set_func(boost::bind( &shutdown_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } diff --git a/ext/asio/asio/ssl/detail/openssl_types.hpp b/ext/asio/asio/ssl/detail/openssl_types.hpp index c697d7400c..fbe1b74370 100755 --- a/ext/asio/asio/ssl/detail/openssl_types.hpp +++ b/ext/asio/asio/ssl/detail/openssl_types.hpp @@ -1,8 +1,8 @@ // -// openssl_types.hpp -// ~~~~~~~~~~~~~~~~~ +// ssl/detail/openssl_types.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,11 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/socket_types.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include #include #include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/detail/pop_options.hpp" +#include "asio/detail/socket_types.hpp" #endif // ASIO_SSL_DETAIL_OPENSSL_TYPES_HPP diff --git a/ext/asio/asio/ssl/stream.hpp b/ext/asio/asio/ssl/stream.hpp index e800e626ab..e16cd9a35e 100644 --- a/ext/asio/asio/ssl/stream.hpp +++ b/ext/asio/asio/ssl/stream.hpp @@ -1,9 +1,9 @@ // -// stream.hpp -// ~~~~~~~~~~ +// ssl/stream.hpp +// ~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,20 +16,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include #include #include -#include "asio/detail/pop_options.hpp" - +#include "asio/detail/throw_error.hpp" #include "asio/error.hpp" #include "asio/ssl/basic_context.hpp" #include "asio/ssl/stream_base.hpp" #include "asio/ssl/stream_service.hpp" -#include "asio/detail/throw_error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ssl { diff --git a/ext/asio/asio/ssl/stream_base.hpp b/ext/asio/asio/ssl/stream_base.hpp index d62d386ae5..e1cc0d72c5 100755 --- a/ext/asio/asio/ssl/stream_base.hpp +++ b/ext/asio/asio/ssl/stream_base.hpp @@ -1,8 +1,8 @@ // -// stream_base.hpp -// ~~~~~~~~~~~~~~~ +// ssl/stream_base.hpp +// ~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" +#include #include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" namespace asio { namespace ssl { diff --git a/ext/asio/asio/ssl/stream_service.hpp b/ext/asio/asio/ssl/stream_service.hpp index 1f1e6ad96d..d99548e333 100644 --- a/ext/asio/asio/ssl/stream_service.hpp +++ b/ext/asio/asio/ssl/stream_service.hpp @@ -1,9 +1,9 @@ // -// stream_service.hpp -// ~~~~~~~~~~~~~~~~~~ +// ssl/stream_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com -// Copyright (c) 2005-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -16,19 +16,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include #include -#include "asio/detail/pop_options.hpp" - #include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" #include "asio/ssl/basic_context.hpp" -#include "asio/ssl/stream_base.hpp" #include "asio/ssl/detail/openssl_stream_service.hpp" +#include "asio/ssl/stream_base.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace ssl { diff --git a/ext/asio/asio/strand.hpp b/ext/asio/asio/strand.hpp index 6b321513dd..5a526f24a3 100644 --- a/ext/asio/asio/strand.hpp +++ b/ext/asio/asio/strand.hpp @@ -2,7 +2,7 @@ // strand.hpp // ~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/io_service.hpp" +#include "asio/detail/config.hpp" #include "asio/detail/strand_service.hpp" #include "asio/detail/wrapped_handler.hpp" +#include "asio/io_service.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { diff --git a/ext/asio/asio/stream_socket_service.hpp b/ext/asio/asio/stream_socket_service.hpp index 1c4935c123..553719ecec 100644 --- a/ext/asio/asio/stream_socket_service.hpp +++ b/ext/asio/asio/stream_socket_service.hpp @@ -2,7 +2,7 @@ // stream_socket_service.hpp // ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,10 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" #if defined(ASIO_HAS_IOCP) # include "asio/detail/win_iocp_socket_service.hpp" @@ -32,6 +26,8 @@ # include "asio/detail/reactive_socket_service.hpp" #endif +#include "asio/detail/push_options.hpp" + namespace asio { /// Default service implementation for a stream socket. diff --git a/ext/asio/asio/streambuf.hpp b/ext/asio/asio/streambuf.hpp index 665155be84..553da3e56b 100644 --- a/ext/asio/asio/streambuf.hpp +++ b/ext/asio/asio/streambuf.hpp @@ -2,7 +2,7 @@ // streambuf.hpp // ~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,12 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/basic_streambuf.hpp" +#include "asio/detail/config.hpp" #if !defined(BOOST_NO_IOSTREAM) +#include "asio/basic_streambuf.hpp" + namespace asio { /// Typedef for the typical usage of basic_streambuf. @@ -30,6 +30,4 @@ typedef basic_streambuf<> streambuf; #endif // !defined(BOOST_NO_IOSTREAM) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_STREAMBUF_HPP diff --git a/ext/asio/asio/system_error.hpp b/ext/asio/asio/system_error.hpp index e704b3fb90..60580a11aa 100644 --- a/ext/asio/asio/system_error.hpp +++ b/ext/asio/asio/system_error.hpp @@ -2,7 +2,7 @@ // system_error.hpp // ~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,18 +15,15 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include +#include "asio/detail/config.hpp" #include #include #include #include -#include "asio/detail/pop_options.hpp" - #include "asio/error_code.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /// The system_error class is used to represent system conditions that diff --git a/ext/asio/asio/thread.hpp b/ext/asio/asio/thread.hpp index 9e578bc598..070483ef55 100644 --- a/ext/asio/asio/thread.hpp +++ b/ext/asio/asio/thread.hpp @@ -2,7 +2,7 @@ // thread.hpp // ~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,11 +15,12 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - +#include "asio/detail/config.hpp" #include "asio/detail/noncopyable.hpp" #include "asio/detail/thread.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /// A simple abstraction for starting threads. diff --git a/ext/asio/asio/time_traits.hpp b/ext/asio/asio/time_traits.hpp index 0371373689..3b2899b166 100644 --- a/ext/asio/asio/time_traits.hpp +++ b/ext/asio/asio/time_traits.hpp @@ -2,7 +2,7 @@ // time_traits.hpp // ~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,14 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - #include "asio/detail/socket_types.hpp" // Must come before posix_time. #include "asio/detail/push_options.hpp" #include #include "asio/detail/pop_options.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /// Time traits suitable for use with the deadline timer. diff --git a/ext/asio/asio/version.hpp b/ext/asio/asio/version.hpp index 198f5086fa..0402d0eb38 100644 --- a/ext/asio/asio/version.hpp +++ b/ext/asio/asio/version.hpp @@ -2,7 +2,7 @@ // version.hpp // ~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -18,6 +18,6 @@ // ASIO_VERSION % 100 is the sub-minor version // ASIO_VERSION / 100 % 1000 is the minor version // ASIO_VERSION / 100000 is the major version -#define ASIO_VERSION 100405 // 1.4.5 +#define ASIO_VERSION 100408 // 1.4.8 #endif // ASIO_VERSION_HPP diff --git a/ext/asio/asio/windows/basic_handle.hpp b/ext/asio/asio/windows/basic_handle.hpp index 8c2ee60ebb..f87b3db8e2 100644 --- a/ext/asio/asio/windows/basic_handle.hpp +++ b/ext/asio/asio/windows/basic_handle.hpp @@ -1,8 +1,8 @@ // -// basic_handle.hpp -// ~~~~~~~~~~~~~~~~ +// windows/basic_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,15 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" -#include "asio/detail/push_options.hpp" -#include -#include "asio/detail/pop_options.hpp" +#if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ + || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) #include "asio/basic_io_object.hpp" -#include "asio/error.hpp" #include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" + +#include "asio/detail/push_options.hpp" namespace asio { namespace windows { @@ -222,4 +224,8 @@ protected: #include "asio/detail/pop_options.hpp" +#endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) + // || defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) + // || defined(GENERATING_DOCUMENTATION) + #endif // ASIO_WINDOWS_BASIC_HANDLE_HPP diff --git a/ext/asio/asio/windows/basic_random_access_handle.hpp b/ext/asio/asio/windows/basic_random_access_handle.hpp index 2e6b994e0b..056b175794 100644 --- a/ext/asio/asio/windows/basic_random_access_handle.hpp +++ b/ext/asio/asio/windows/basic_random_access_handle.hpp @@ -1,8 +1,8 @@ // -// basic_random_access_handle.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// windows/basic_random_access_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,21 +15,19 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/windows/basic_handle.hpp" -#include "asio/windows/random_access_handle_service.hpp" -#include "asio/detail/throw_error.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ || defined(GENERATING_DOCUMENTATION) +#include +#include "asio/detail/throw_error.hpp" +#include "asio/error.hpp" +#include "asio/windows/basic_handle.hpp" +#include "asio/windows/random_access_handle_service.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace windows { @@ -312,9 +310,9 @@ public: } // namespace windows } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP diff --git a/ext/asio/asio/windows/basic_stream_handle.hpp b/ext/asio/asio/windows/basic_stream_handle.hpp index 48e7153da6..28149f4cca 100644 --- a/ext/asio/asio/windows/basic_stream_handle.hpp +++ b/ext/asio/asio/windows/basic_stream_handle.hpp @@ -1,8 +1,8 @@ // -// basic_stream_handle.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~ +// windows/basic_stream_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,20 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" + +#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ + || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/push_options.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - #include "asio/error.hpp" #include "asio/windows/basic_handle.hpp" #include "asio/windows/stream_handle_service.hpp" #include "asio/detail/throw_error.hpp" -#if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ - || defined(GENERATING_DOCUMENTATION) +#include "asio/detail/push_options.hpp" namespace asio { namespace windows { @@ -294,9 +292,9 @@ public: } // namespace windows } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_WINDOWS_BASIC_STREAM_HANDLE_HPP diff --git a/ext/asio/asio/windows/overlapped_ptr.hpp b/ext/asio/asio/windows/overlapped_ptr.hpp index 087170b5a0..8f4d628e0c 100644 --- a/ext/asio/asio/windows/overlapped_ptr.hpp +++ b/ext/asio/asio/windows/overlapped_ptr.hpp @@ -1,8 +1,8 @@ // -// overlapped_ptr.hpp -// ~~~~~~~~~~~~~~~~~~ +// windows/overlapped_ptr.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,21 +15,17 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/io_service.hpp" -#include "asio/detail/noncopyable.hpp" -#include "asio/detail/win_iocp_overlapped_ptr.hpp" - -#if !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) -# if defined(ASIO_HAS_IOCP) -# define ASIO_HAS_WINDOWS_OVERLAPPED_PTR 1 -# endif // defined(ASIO_HAS_IOCP) -#endif // !defined(ASIO_DISABLE_WINDOWS_OVERLAPPED_PTR) +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/detail/noncopyable.hpp" +#include "asio/detail/win_iocp_overlapped_ptr.hpp" +#include "asio/io_service.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace windows { @@ -110,9 +106,9 @@ private: } // namespace windows } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_WINDOWS_OVERLAPPED_PTR) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_WINDOWS_OVERLAPPED_PTR_HPP diff --git a/ext/asio/asio/windows/random_access_handle.hpp b/ext/asio/asio/windows/random_access_handle.hpp index 6e5dd7b6fc..727b084cab 100644 --- a/ext/asio/asio/windows/random_access_handle.hpp +++ b/ext/asio/asio/windows/random_access_handle.hpp @@ -1,8 +1,8 @@ // -// random_access_handle.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~ +// windows/random_access_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/windows/basic_random_access_handle.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/windows/basic_random_access_handle.hpp" + namespace asio { namespace windows { @@ -34,6 +34,4 @@ typedef basic_random_access_handle<> random_access_handle; #endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_HPP diff --git a/ext/asio/asio/windows/random_access_handle_service.hpp b/ext/asio/asio/windows/random_access_handle_service.hpp index 2e579f82e6..b2c05efc31 100644 --- a/ext/asio/asio/windows/random_access_handle_service.hpp +++ b/ext/asio/asio/windows/random_access_handle_service.hpp @@ -1,8 +1,8 @@ // -// random_access_handle_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// windows/random_access_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,28 +15,20 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" -#include "asio/detail/win_iocp_handle_service.hpp" - -#if !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) -# if defined(ASIO_HAS_IOCP) -# define ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE 1 -# endif // defined(ASIO_HAS_IOCP) -#endif // !defined(ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE) +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ || defined(GENERATING_DOCUMENTATION) +#include +#include +#include +#include "asio/detail/win_iocp_handle_service.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace windows { @@ -172,9 +164,9 @@ private: } // namespace windows } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_WINDOWS_RANDOM_ACCESS_HANDLE_SERVICE_HPP diff --git a/ext/asio/asio/windows/stream_handle.hpp b/ext/asio/asio/windows/stream_handle.hpp index d55dc3174a..802d157614 100644 --- a/ext/asio/asio/windows/stream_handle.hpp +++ b/ext/asio/asio/windows/stream_handle.hpp @@ -1,8 +1,8 @@ // -// stream_handle.hpp -// ~~~~~~~~~~~~~~~~~~ +// windows/stream_handle.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,13 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/windows/basic_stream_handle.hpp" +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ || defined(GENERATING_DOCUMENTATION) +#include "asio/windows/basic_stream_handle.hpp" + namespace asio { namespace windows { @@ -34,6 +34,4 @@ typedef basic_stream_handle<> stream_handle; #endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_WINDOWS_STREAM_HANDLE_HPP diff --git a/ext/asio/asio/windows/stream_handle_service.hpp b/ext/asio/asio/windows/stream_handle_service.hpp index 75eff60a6e..816cfe556b 100644 --- a/ext/asio/asio/windows/stream_handle_service.hpp +++ b/ext/asio/asio/windows/stream_handle_service.hpp @@ -1,8 +1,8 @@ // -// stream_handle_service.hpp -// ~~~~~~~~~~~~~~~~~~~~~~~~~ +// windows/stream_handle_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,27 +15,18 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" -#include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/error.hpp" -#include "asio/io_service.hpp" -#include "asio/detail/service_base.hpp" -#include "asio/detail/win_iocp_handle_service.hpp" - -#if !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) -# if defined(ASIO_HAS_IOCP) -# define ASIO_HAS_WINDOWS_STREAM_HANDLE 1 -# endif // defined(ASIO_HAS_IOCP) -#endif // !defined(ASIO_DISABLE_WINDOWS_STREAM_HANDLE) +#include "asio/detail/config.hpp" #if defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) \ || defined(GENERATING_DOCUMENTATION) +#include +#include "asio/detail/win_iocp_handle_service.hpp" +#include "asio/error.hpp" +#include "asio/io_service.hpp" + +#include "asio/detail/push_options.hpp" + namespace asio { namespace windows { @@ -170,9 +161,9 @@ private: } // namespace windows } // namespace asio +#include "asio/detail/pop_options.hpp" + #endif // defined(ASIO_HAS_WINDOWS_STREAM_HANDLE) // || defined(GENERATING_DOCUMENTATION) -#include "asio/detail/pop_options.hpp" - #endif // ASIO_WINDOWS_STREAM_HANDLE_SERVICE_HPP diff --git a/ext/asio/asio/write.hpp b/ext/asio/asio/write.hpp index 2b188cde4c..bd90ecb069 100644 --- a/ext/asio/asio/write.hpp +++ b/ext/asio/asio/write.hpp @@ -2,7 +2,7 @@ // write.hpp // ~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,16 +15,13 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include -#include "asio/detail/pop_options.hpp" - -#include "asio/basic_streambuf.hpp" +#include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /** @@ -533,8 +530,8 @@ void async_write(AsyncWriteStream& s, basic_streambuf& b, } // namespace asio -#include "asio/impl/write.ipp" - #include "asio/detail/pop_options.hpp" +#include "asio/impl/write.hpp" + #endif // ASIO_WRITE_HPP diff --git a/ext/asio/asio/write_at.hpp b/ext/asio/asio/write_at.hpp index 5690bcfda1..37d0cbb2ea 100644 --- a/ext/asio/asio/write_at.hpp +++ b/ext/asio/asio/write_at.hpp @@ -2,7 +2,7 @@ // write_at.hpp // ~~~~~~~~~~~~ // -// Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,17 +15,14 @@ # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) -#include "asio/detail/push_options.hpp" - -#include "asio/detail/push_options.hpp" +#include "asio/detail/config.hpp" #include -#include #include -#include "asio/detail/pop_options.hpp" - -#include "asio/basic_streambuf.hpp" +#include "asio/basic_streambuf_fwd.hpp" #include "asio/error.hpp" +#include "asio/detail/push_options.hpp" + namespace asio { /** @@ -556,8 +553,8 @@ void async_write_at(AsyncRandomAccessWriteDevice& d, boost::uint64_t offset, } // namespace asio -#include "asio/impl/write_at.ipp" - #include "asio/detail/pop_options.hpp" +#include "asio/impl/write_at.hpp" + #endif // ASIO_WRITE_AT_HPP diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc index a6db762b8e..480c2f7f1f 100644 --- a/src/bin/auth/main.cc +++ b/src/bin/auth/main.cc @@ -176,9 +176,15 @@ main(int argc, char* argv[]) { // all initial configurations, but as a short term workaround we // handle the traditional "database_file" setup by directly calling // updateConfig(). + // if server load configure failed, we won't exit, give user second chance + // to correct the configure. auth_server->setConfigSession(config_session); - configureAuthServer(*auth_server, config_session->getFullConfig()); - auth_server->updateConfig(ElementPtr()); + try { + configureAuthServer(*auth_server, config_session->getFullConfig()); + auth_server->updateConfig(ElementPtr()); + } catch (const AuthConfigError& ex) { + cout << "[bin10-auth] Server load config failed:" << ex.what() << endl; + } if (uid != NULL) { changeUser(uid); diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc index e68793c58c..415aa14307 100644 --- a/src/bin/auth/statistics.cc +++ b/src/bin/auth/statistics.cc @@ -91,10 +91,6 @@ AuthCountersImpl::submitStatistics() const { const int seq = statistics_session_->group_sendmsg(statistics_element, "Stats"); isc::data::ConstElementPtr env, answer; - if (verbose_mode_) { - std::cerr << "[b10-auth] " - << "send statistics data" << std::endl; - } // TODO: parse and check response from statistics module // currently it just returns empty message statistics_session_->group_recvmsg(env, answer, false, seq); diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am index 026dde3c5c..050373ab78 100644 --- a/src/bin/auth/tests/Makefile.am +++ b/src/bin/auth/tests/Makefile.am @@ -18,7 +18,6 @@ CLEANFILES = *.gcno *.gcda TESTS = if HAVE_GTEST -BUILT_SOURCES = ../spec_config.h TESTS += run_unittests run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc @@ -28,7 +27,6 @@ run_unittests_SOURCES += ../change_user.h ../change_user.cc run_unittests_SOURCES += ../auth_config.h ../auth_config.cc run_unittests_SOURCES += ../command.h ../command.cc run_unittests_SOURCES += ../common.h ../common.cc -run_unittests_SOURCES += ../spec_config.h run_unittests_SOURCES += ../statistics.h ../statistics.cc run_unittests_SOURCES += auth_srv_unittest.cc run_unittests_SOURCES += config_unittest.cc diff --git a/src/bin/bind10/bind10.py.in b/src/bin/bind10/bind10.py.in index e6504e1ab5..648d085089 100755 --- a/src/bin/bind10/bind10.py.in +++ b/src/bin/bind10/bind10.py.in @@ -546,6 +546,9 @@ class BoB: def start_stats(self, c_channel_env): self.start_simple("b10-stats", c_channel_env) + def start_stats_httpd(self, c_channel_env): + self.start_simple("b10-stats-httpd", c_channel_env) + def start_cmdctl(self, c_channel_env): """ Starts the command control process @@ -595,6 +598,7 @@ class BoB: # ... and finally start the remaining processes self.start_stats(c_channel_env) + self.start_stats_httpd(c_channel_env) self.start_cmdctl(c_channel_env) def startup(self): @@ -644,6 +648,7 @@ class BoB: self.cc_session.group_sendmsg(cmd, "Xfrin", "Xfrin") self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr") self.cc_session.group_sendmsg(cmd, "Stats", "Stats") + self.cc_session.group_sendmsg(cmd, "StatsHttpd", "StatsHttpd") def stop_process(self, process, recipient): """ diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in old mode 100644 new mode 100755 index edc01fe33d..89301bdda1 --- a/src/bin/bind10/run_bind10.sh.in +++ b/src/bin/bind10/run_bind10.sh.in @@ -23,14 +23,14 @@ BIND10_PATH=@abs_top_builddir@/src/bin/bind10 PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:$PATH export PATH -PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs +PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs export PYTHONPATH # If necessary (rare cases), explicitly specify paths to dynamic libraries # required by loadable python modules. SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@ if test $SET_ENV_LIBRARY_PATH = yes; then - @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ + @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@ export @ENV_LIBRARY_PATH@ fi diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in index 8432c892ba..2ffe2b4c1c 100644 --- a/src/bin/bind10/tests/bind10_test.py.in +++ b/src/bin/bind10/tests/bind10_test.py.in @@ -183,6 +183,7 @@ class MockBob(BoB): self.xfrin = False self.zonemgr = False self.stats = False + self.stats_httpd = False self.cmdctl = False self.c_channel_env = {} self.processes = { } @@ -236,10 +237,15 @@ class MockBob(BoB): self.processes[10] = ProcessInfo('b10-stats', ['/bin/false']) self.processes[10].pid = 10 + def start_stats_httpd(self, c_channel_env): + self.stats_httpd = True + self.processes[11] = ProcessInfo('b10-stats-httpd', ['/bin/false']) + self.processes[11].pid = 11 + def start_cmdctl(self, c_channel_env): self.cmdctl = True - self.processes[11] = ProcessInfo('b10-cmdctl', ['/bin/false']) - self.processes[11].pid = 11 + self.processes[12] = ProcessInfo('b10-cmdctl', ['/bin/false']) + self.processes[12].pid = 12 # We don't really use all of these stop_ methods. But it might turn out # someone would add some stop_ method to BoB and we want that one overriden @@ -289,9 +295,14 @@ class MockBob(BoB): del self.processes[10] self.stats = False + def stop_stats_httpd(self): + if self.stats_httpd: + del self.processes[11] + self.stats_httpd = False + def stop_cmdctl(self): if self.cmdctl: - del self.processes[11] + del self.processes[12] self.cmdctl = False class TestStartStopProcessesBob(unittest.TestCase): @@ -316,6 +327,7 @@ class TestStartStopProcessesBob(unittest.TestCase): self.assertEqual(bob.xfrin, auth) self.assertEqual(bob.zonemgr, auth) self.assertEqual(bob.stats, core) + self.assertEqual(bob.stats_httpd, core) self.assertEqual(bob.cmdctl, core) def check_preconditions(self, bob): @@ -544,7 +556,8 @@ class TestBossCmd(unittest.TestCase): [8, 'b10-xfrin'], [9, 'b10-zonemgr'], [10, 'b10-stats'], - [11, 'b10-cmdctl']] + [11, 'b10-stats-httpd'], + [12, 'b10-cmdctl']] self.assertEqual(answer, {'result': [0, processes]}) class TestParseArgs(unittest.TestCase): diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in index 607a6dcd29..d91dfca764 100755 --- a/src/bin/cfgmgr/b10-cfgmgr.py.in +++ b/src/bin/cfgmgr/b10-cfgmgr.py.in @@ -18,6 +18,7 @@ import sys; sys.path.append ('@@PYTHONPATH@@') from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError +import bind10_config from isc.cc import SessionError import isc.util.process import signal @@ -28,24 +29,10 @@ import os.path isc.util.process.rename() -# If B10_FROM_SOURCE is set in the environment, we use data files -# from a directory relative to the value of that variable, or, if defined, -# relative to the value of B10_FROM_SOURCE_LOCALSTATEDIR. Otherwise -# we use the ones installed on the system. -# B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for -# tests where we want to use variuos types of configuration within the test -# environment. (We may want to make it even more generic so that the path is -# passed from the boss process) -if "B10_FROM_SOURCE" in os.environ: - if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ: - DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] - else: - DATA_PATH = os.environ["B10_FROM_SOURCE"] - PLUGIN_PATH = [DATA_PATH + '/src/bin/cfgmgr/plugins'] -else: - PREFIX = "@prefix@" - DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX) - PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"] +# Import some paths from our configuration +DATA_PATH = bind10_config.DATA_PATH +PLUGIN_PATHS = bind10_config.PLUGIN_PATHS +PREFIX = bind10_config.PREFIX DEFAULT_CONFIG_FILE = "b10-config.db" cm = None diff --git a/src/bin/cfgmgr/plugins/Makefile.am b/src/bin/cfgmgr/plugins/Makefile.am index 952fde6edc..d83c2bbd58 100644 --- a/src/bin/cfgmgr/plugins/Makefile.am +++ b/src/bin/cfgmgr/plugins/Makefile.am @@ -1 +1,5 @@ -EXTRA_DIST = README +SUBDIRS = tests +EXTRA_DIST = README tsig_keys.py tsig_keys.spec + +config_plugindir = @prefix@/share/@PACKAGE@/config_plugins +config_plugin_DATA = tsig_keys.py tsig_keys.spec diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am new file mode 100644 index 0000000000..896dab7f6c --- /dev/null +++ b/src/bin/cfgmgr/plugins/tests/Makefile.am @@ -0,0 +1,27 @@ +PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ +PYTESTS = tsig_keys_test.py + +EXTRA_DIST = $(PYTESTS) + +# If necessary (rare cases), explicitly specify paths to dynamic libraries +# required by loadable python modules. +LIBRARY_PATH_PLACEHOLDER = +if SET_ENV_LIBRARY_PATH +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs +endif + +# test using command-line arguments, so use check-local target instead of TESTS +check-local: +if ENABLE_PYTHON_COVERAGE + touch $(abs_top_srcdir)/.coverage + rm -f .coverage + ${LN_S} $(abs_top_srcdir)/.coverage .coverage +endif + for pytest in $(PYTESTS) ; do \ + echo Running test: $$pytest ; \ + env B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \ + env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \ + $(LIBRARY_PATH_PLACEHOLDER) \ + $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ + done + diff --git a/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py b/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py new file mode 100644 index 0000000000..be2921c05f --- /dev/null +++ b/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py @@ -0,0 +1,103 @@ +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# Make sure we can load the module, put it into path +import sys +import os +sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':')) + +import tsig_keys +import unittest +import isc.config.module_spec + +class TSigKeysTest(unittest.TestCase): + def test_load(self): + """ + Checks the entry point returns the correct values. + """ + (spec, check) = tsig_keys.load() + # It returns the checking function + self.assertEqual(check, tsig_keys.check) + # The plugin stores it's spec + self.assertEqual(spec, tsig_keys.spec) + + def test_spec(self): + """ + Checks the spec is looking sane (doesn't do really deep check here). + """ + spec = tsig_keys.spec + # In python, we don't generally check the type of something, because + # of the duck typing. + # But this is unittest, so we check it does what we intend and + # supplying that's behaving the same but is different is not our + # intention + self.assertTrue(isinstance(spec, isc.config.module_spec.ModuleSpec)) + # Correct name + self.assertEqual("tsig_keys", spec.get_module_name()) + # There are no commands, nobody would handle them anyway + self.assertEqual([], spec.get_commands_spec()) + # There's some nonempty configuration + self.assertNotEqual({}, spec.get_config_spec()) + + def test_missing_keys(self): + """ + Test that missing keys doesn't kill us. There are just no keys there. + """ + self.assertEqual(None, tsig_keys.check({})) + + def test_data_empty(self): + """Check we accept valid config with empty set of tsig keys.""" + self.assertEqual(None, tsig_keys.check({'keys': []})) + + def test_keys_valid(self): + """ + Check we accept some valid keys (we don't check all the algorithms, + that's the job of isc.dns.TSIGKey). + """ + self.assertEqual(None, tsig_keys.check({'keys': + ['testkey:QklORCAxMCBpcyBjb29sCg==', + 'test.key:QklORCAxMCBpcyBjb29sCg==:hmac-sha1']})) + + def test_keys_same_name(self): + """ + Test we reject when we have multiple keys with the same name. + """ + self.assertEqual("Multiple TSIG keys with name 'test.key.'", + tsig_keys.check({'keys': + ['test.key:QklORCAxMCBpcyBjb29sCg==', + 'test.key:b3RoZXIK']})) + + def test_invalid_key(self): + """ + Test we reject invalid key. + """ + self.assertEqual("TSIG: Invalid TSIG key string: invalid.key", + tsig_keys.check({'keys': ['invalid.key']})) + self.assertEqual( + "TSIG: attempt to decode a value not in base64 char set", + tsig_keys.check({'keys': ['invalid.key:123']})) + + def test_bad_format(self): + """ + Test we fail on bad format. We don't really care much how here, though, + as this should not get in trough config manager anyway. + """ + self.assertNotEqual(None, tsig_keys.check({'bad_name': {}})) + self.assertNotEqual(None, tsig_keys.check({'keys': 'not_list'})) + self.assertNotEqual(None, tsig_keys.check({'keys': 42})) + self.assertNotEqual(None, tsig_keys.check({'keys': {}})) + +if __name__ == '__main__': + unittest.main() diff --git a/src/bin/cfgmgr/plugins/tsig_keys.py b/src/bin/cfgmgr/plugins/tsig_keys.py new file mode 100644 index 0000000000..d57e645930 --- /dev/null +++ b/src/bin/cfgmgr/plugins/tsig_keys.py @@ -0,0 +1,50 @@ +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# This is the plugin for tsig_keys configuration section. The TSIG keyring +# lives there (eg. all the shared secrets, with some exceptions where there +# are some TSIG keys elsewhere, but these should be removed soon). We do +# sanity checking of user configuration here, simply by trying to construct +# all the keys here. + +from isc.config.module_spec import module_spec_from_file +from isc.util.file import path_search +from pydnspp import TSIGKey, InvalidParameter +from bind10_config import PLUGIN_PATHS +spec = module_spec_from_file(path_search('tsig_keys.spec', PLUGIN_PATHS)) + +def check(config): + # Check the data layout first + errors=[] + if not spec.validate_config(False, config, errors): + return ' '.join(errors) + # Get the list of keys, if any + keys = config.get('keys', []) + # Run through them, check they can be constructed and there are no + # duplicates + keyNames = set() + for key in keys: + try: + name = str(TSIGKey(key).get_key_name()) + except InvalidParameter as e: + return "TSIG: " + str(e) + if name in keyNames: + return "Multiple TSIG keys with name '" + name + "'" + keyNames.add(name) + # No error found, so let's assume it's OK + return None + +def load(): + return (spec, check) diff --git a/src/bin/cfgmgr/plugins/tsig_keys.spec b/src/bin/cfgmgr/plugins/tsig_keys.spec new file mode 100644 index 0000000000..e558dd2b84 --- /dev/null +++ b/src/bin/cfgmgr/plugins/tsig_keys.spec @@ -0,0 +1,21 @@ +{ + "module_spec": { + "module_name": "tsig_keys", + "module_description": "The TSIG keyring is stored here", + "config_data": [ + { + "item_name": "keys", + "item_type": "list", + "item_optional": false, + "item_default": [], + "list_item_spec": { + "item_name": "key", + "item_type": "string", + "item_optional": false, + "item_default": "" + } + } + ], + "commands": [] + } +} diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in index 37cd0f50c1..ea5fc8b519 100644 --- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in +++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in @@ -20,6 +20,7 @@ import unittest import os import sys +import bind10_config from isc.testutils.parse_args import OptsError, TestOptParser class MyConfigManager: @@ -110,6 +111,7 @@ class TestConfigManagerStartup(unittest.TestCase): env_var = os.environ["B10_FROM_SOURCE"] os.environ["B10_FROM_SOURCE"] = tmp_env_var + bind10_config.reload() b = __import__("b10-cfgmgr", globals(), locals()) b.PLUGIN_PATH = [] # It's enough to test plugins in one test b.ConfigManager = MyConfigManager @@ -117,6 +119,7 @@ class TestConfigManagerStartup(unittest.TestCase): if env_var != None: os.environ["B10_FROM_SOURCE"] = env_var + bind10_config.reload() sys.modules.pop("b10-cfgmgr") diff --git a/src/bin/host/Makefile.am b/src/bin/host/Makefile.am index 0758cb95f8..ec34ce751f 100644 --- a/src/bin/host/Makefile.am +++ b/src/bin/host/Makefile.am @@ -10,17 +10,20 @@ endif CLEANFILES = *.gcno *.gcda -bin_PROGRAMS = host -host_SOURCES = host.cc -host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la -host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la +bin_PROGRAMS = b10-host +b10_host_SOURCES = host.cc +b10_host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la +b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la -#man_MANS = host.1 -#EXTRA_DIST = $(man_MANS) host.xml -# -#if ENABLE_MAN -# -#host.1: host.xml -# xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/host.xml -# -#endif +man_MANS = b10-host.1 +EXTRA_DIST = $(man_MANS) b10-host.xml + +.PHONY: man +if ENABLE_MAN + +man: b10-host.1 + +b10-host.1: b10-host.xml + xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-host.xml + +endif diff --git a/src/bin/host/README b/src/bin/host/README index d493a95313..5cc4068257 100644 --- a/src/bin/host/README +++ b/src/bin/host/README @@ -1,14 +1,4 @@ -Rewriting host(1) in C++ from scratch using BIND 10's libdns. +Rewriting host(1) in C++ from scratch using BIND 10's libdns++. -Initial functionality: - - host _hostname_ [server] - -By default, it looks up the A, AAAA, and MX record sets. - -Note it doesn't use /etc/resolv.conf at this time. -The default name server used is 127.0.0.1. - - -r disable recursive processing - -t _type_ specific query type - -v enable verbose output mode, including elapsed time +The bugs and incompatibilities are listed in the manual page +and in the source code. diff --git a/src/bin/host/b10-host.1 b/src/bin/host/b10-host.1 new file mode 100644 index 0000000000..ed0068b9a1 --- /dev/null +++ b/src/bin/host/b10-host.1 @@ -0,0 +1,122 @@ +'\" t +.\" Title: b10-host +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.75.2 +.\" Date: May 4, 2011 +.\" Manual: BIND10 +.\" Source: BIND10 +.\" Language: English +.\" +.TH "B10\-HOST" "1" "May 4, 2011" "BIND10" "BIND10" +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +b10-host \- DNS lookup utility +.SH "SYNOPSIS" +.HP \w'\fBb10\-host\fR\ 'u +\fBb10\-host\fR [\fB\-a\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-d\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-r\fR] [\fB\-t\ \fR\fB\fItype\fR\fR] [\fB\-v\fR] [\fIname\fR] [\fB\fIserver\fR\fR] +.SH "DESCRIPTION" +.PP +The +\fBb10\-host\fR +utility does DNS lookups\&. Its initial goal is to be a +\fBhost\fR(1) +clone, but also add a few features useful for BIND 10 development testing\&. +.PP +By default, it looks up the A, AAAA, and MX record sets for the +\fIname\fR\&. Optionally, you may select a name server to query against by adding the +\fIserver\fR +argument\&. +.SH "OPTIONS" +.PP +The arguments are as follows: +.PP +\fB\-a\fR +.RS 4 +Enable verbose mode and do a query for type ANY\&. (If the +\fB\-t\fR +option is also set, then the ANY query is not done, but it still uses verbose mode\&.) +.RE +.PP +\fB\-c \fR\fB\fIclass\fR\fR +.RS 4 +Define the class for the query\&. The default is IN (Internet)\&. +.RE +.PP +\fB\-d\fR +.RS 4 +Enable verbose output mode, including elapsed time in milliseconds\&. Verbose mode shows the header, question, answer, authority, and additional sections (if provided)\&. (Same as +\fB\-v\fR\&.) +.RE +.PP +\fB\-p \fR\fB\fIport\fR\fR +.RS 4 +Select an alternative port for the query\&. This may be a number or a service name\&. The default is 53 (domain)\&. This is not a standard feature of +\fBhost\fR(1)\&. +.RE +.PP +\fB\-r\fR +.RS 4 +Disable recursive processing by not setting the Recursion Desired flag in the query\&. +.RE +.PP +\fB\-t \fR\fB\fItype\fR\fR +.RS 4 +Select a specific resource record type for the query\&. By default, it looks up the A, AAAA, and MX record sets\&. +(This overrides the +\fB\-a\fR +option\&.) +.RE +.PP +\fB\-v\fR +.RS 4 +Same as +\fB\-d\fR +option\&. +.RE +.SH "COMPATIBILITY / BUGS" +.PP + +\fBb10\-host\fR +does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&. +.PP +Unknown +\fB\-c\fR +class or +\fB\-t\fR +type causes +\fBb10\-host\fR +to Abort\&. +.PP +Not all types are supported yet for formatting\&. Not all switches are supported yet\&. +.PP +It doesn\'t use +/etc/resolv\&.conf +at this time\&. The default name server used is 127\&.0\&.0\&.1\&. +.PP + +\fBb10\-host\fR +does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&. +.PP + +\fB\-p\fR +is not a standard feature\&. +.SH "HISTORY" +.PP +The C++ version of +\fBb10\-host\fR +was started in October 2009 by Jeremy C\&. Reed of ISC\&. Its usage and output were based on the standard +\fBhost\fR +command\&. +.SH "COPYRIGHT" +.br +Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC") +.br diff --git a/src/bin/host/b10-host.xml b/src/bin/host/b10-host.xml new file mode 100644 index 0000000000..7da07dd16c --- /dev/null +++ b/src/bin/host/b10-host.xml @@ -0,0 +1,201 @@ +]> + + + + + + + May 4, 2011 + + + + b10-host + 1 + BIND10 + + + + b10-host + DNS lookup utility + + + + + 2011 + Internet Systems Consortium, Inc. ("ISC") + + + + + + b10-host + + + + + + + + name + + + + + + DESCRIPTION + + The b10-host utility does DNS lookups. + Its initial goal is to be a + host + 1 + clone, but also add a few features useful for BIND 10 development + testing. + + + + By default, it looks up the A, AAAA, and MX record sets for the + name. + Optionally, you may select a name server to query against by adding + the server argument. + + + + + OPTIONS + + The arguments are as follows: + + + + + + + Enable verbose mode and do a query for type ANY. + (If the option is also set, then the + ANY query is not done, but it still uses verbose mode.) + + + + + + + Define the class for the query. + The default is IN (Internet). + + + + + + + + Enable verbose output mode, including elapsed time in + milliseconds. + Verbose mode shows the header, question, answer, authority, + and additional sections (if provided). + (Same as .) + + + + + + + Select an alternative port for the query. + This may be a number or a service name. + The default is 53 (domain). + This is not a standard feature of + host + 1. + + + + + + + Disable recursive processing by not setting the + Recursion Desired flag in the query. + + + + + + + Select a specific resource record type for the query. + By default, it looks up the A, AAAA, and MX record sets. + + (This overrides the option.) + + + + + + + Same as option. + + + + + + + + + COMPATIBILITY / BUGS + + b10-host does not do reverse lookups by + default yet (by detecting if name is a IPv4 or IPv6 address). + + + + Unknown class or type + causes b10-host to Abort. + + + + Not all types are supported yet for formatting. + Not all switches are supported yet. + + + + It doesn't use /etc/resolv.conf at this time. + The default name server used is 127.0.0.1. + + + + b10-host does not do reverse lookups by + default yet (by detecting if name is a IPv4 or IPv6 address). + + + + is not a standard feature. + + + + + HISTORY + + The C++ version of b10-host was started in + October 2009 by Jeremy C. Reed of ISC. + Its usage and output were based on the standard host + command. + + + diff --git a/src/bin/host/host.cc b/src/bin/host/host.cc index 973509e956..f0df0c8540 100644 --- a/src/bin/host/host.cc +++ b/src/bin/host/host.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -44,13 +44,16 @@ namespace { char* dns_type = NULL; // not set, so A, AAAA, MX const char* server = "127.0.0.1"; const char* server_port = "53"; -int verbose = 0; -int first_time = 1; -bool recursive_bit = true; +const char* dns_class = "IN"; +bool verbose = false; +bool dns_any = false; +int first_time = 1; +bool recursive_bit = true; struct timeval before_time, after_time; int -host_lookup(const char* const name, const char* const type) { +host_lookup(const char* const name, const char* const dns_class, + const char* const type, bool any) { Message msg(Message::RENDER); @@ -64,8 +67,8 @@ host_lookup(const char* const name, const char* const type) { } msg.addQuestion(Question(Name(name), - RRClass::IN(), // IN class only for now - RRType(type))); // if NULL then: + RRClass(dns_class), + any ? RRType::ANY() : RRType(type))); // if NULL then: OutputBuffer obuffer(512); MessageRenderer renderer(obuffer); @@ -127,18 +130,29 @@ host_lookup(const char* const name, const char* const type) { rmsg.fromWire(ibuffer); if (!verbose) { + string description = ""; for (RRsetIterator it = rmsg.beginSection(Message::SECTION_ANSWER); it != rmsg.endSection(Message::SECTION_ANSWER); ++it) { - if ((*it)->getType() != RRType::A()) { - continue; + + if ((*it)->getType() == RRType::A()) { + description = "has address"; + } + else if ((*it)->getType() == RRType::AAAA()) { + description = "has IPv6 address"; + } + else if ((*it)->getType() == RRType::MX()) { + description = "mail is handled by"; + } + else if ((*it)->getType() == RRType::TXT()) { + description = "descriptive text"; } RdataIteratorPtr rit = (*it)->getRdataIterator(); for (; !rit->isLast(); rit->next()) { // instead of using my name, maybe use returned label? - cout << name << " has address " << + cout << name << " " << description << " " << (*rit).getCurrent().toText() << endl; } } @@ -159,13 +173,19 @@ host_lookup(const char* const name, const char* const type) { // TODO: if NXDOMAIN, host(1) doesn't show HEADER // Host hsdjkfhksjhdfkj not found: 3(NXDOMAIN) - // TODO: figure out the new libdns way to test if NXDOMAIN + // TODO: test if NXDOMAIN std::cout << "Received " << cc << " bytes in " << elapsed_time << " ms\n"; // TODO: " bytes from 127.0.0.1#53 in 0 ms } //verbose +/* +TODO: handle InvalidRRClass +TODO: handle invalid type exception + } catch (InvalidType ivt) { + std::cerr << "invalid type:" << ivt.what(); +*/ } catch (const exception& ex) { std::cerr << "parse failed for " << string(name) << "/" << type << ": " << ex.what() << std::endl; @@ -184,26 +204,36 @@ int main(int argc, char* argv[]) { int c; - while ((c = getopt(argc, argv, "p:rt:v")) != -1) + while ((c = getopt(argc, argv, "ac:dp:rt:v")) != -1) switch (c) { + case 'a': + dns_any = true; + verbose = true; + break; + case 'c': + dns_class = optarg; + break; + // p for port is a non-standard switch + case 'p': + server_port = optarg; + break; case 'r': recursive_bit = false; break; case 't': dns_type = optarg; break; - case 'p': - server_port = optarg; - break; + case 'd': + // drop through to v, because debug and verbose are equivalent case 'v': - verbose = 1; + verbose = true; break; } argc -= optind; argv += optind; if (argc < 1) { - cout << "Usage: host [-vr] [-t type] hostname [server]\n"; + cout << "Usage: host [-adprv] [-c class] [-t type] hostname [server]\n"; exit(1); } @@ -212,12 +242,13 @@ main(int argc, char* argv[]) { } if (dns_type == NULL) { - host_lookup(argv[0], "A"); + host_lookup(argv[0], dns_class, "A", dns_any); // TODO: don't do next if A doesn't exist - host_lookup(argv[0], "AAAA"); - host_lookup(argv[0], "MX"); + host_lookup(argv[0], dns_class, "AAAA", dns_any); + host_lookup(argv[0], dns_class, "MX", dns_any); } else { - host_lookup(argv[0], dns_type); + // -t overrides -a, regardless of order + host_lookup(argv[0], dns_class, dns_type, false); } return (0); } diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py index f926845393..26878f7fb8 100644 --- a/src/bin/msgq/tests/msgq_test.py +++ b/src/bin/msgq/tests/msgq_test.py @@ -132,7 +132,7 @@ class SendNonblock(unittest.TestCase): task() # If we got here, then everything worked well and in time # In that case, we terminate successfully - sys.exit(0) # needs exit code + os._exit(0) # needs exit code else: (pid, status) = os.waitpid(task_pid, 0) self.assertEqual(0, status, diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc index 591e214ace..e43b48e56a 100644 --- a/src/bin/resolver/resolver.cc +++ b/src/bin/resolver/resolver.cc @@ -144,7 +144,7 @@ public: void resolve(const isc::dns::QuestionPtr& question, const isc::resolve::ResolverInterface::CallbackPtr& callback); - void processNormalQuery(const Question& question, + void processNormalQuery(ConstMessagePtr query_message, MessagePtr answer_message, OutputBufferPtr buffer, DNSServer* server); @@ -468,7 +468,7 @@ Resolver::processMessage(const IOMessage& io_message, // The RecursiveQuery object will post the "resume" event to the // DNSServer when an answer arrives, so we don't have to do it now. sendAnswer = false; - impl_->processNormalQuery(*question, answer_message, + impl_->processNormalQuery(query_message, answer_message, buffer, server); } } @@ -486,13 +486,19 @@ ResolverImpl::resolve(const QuestionPtr& question, } void -ResolverImpl::processNormalQuery(const Question& question, +ResolverImpl::processNormalQuery(ConstMessagePtr query_message, MessagePtr answer_message, OutputBufferPtr buffer, DNSServer* server) { - dlog("Processing normal query"); - rec_query_->resolve(question, answer_message, buffer, server); + if (upstream_.empty()) { + dlog("Processing normal query"); + ConstQuestionPtr question = *query_message->beginQuestion(); + rec_query_->resolve(*question, answer_message, buffer, server); + } else { + dlog("Processing forward query"); + rec_query_->forward(query_message, answer_message, buffer, server); + } } ConstElementPtr diff --git a/src/bin/resolver/response_scrubber.cc b/src/bin/resolver/response_scrubber.cc index 93bd8084a3..d35a96f47b 100644 --- a/src/bin/resolver/response_scrubber.cc +++ b/src/bin/resolver/response_scrubber.cc @@ -1,4 +1,3 @@ - // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any @@ -50,7 +49,7 @@ ResponseScrubber::Category ResponseScrubber::addressCheck( unsigned int ResponseScrubber::scrubSection(Message& message, const vector& names, - const NameComparisonResult::NameRelation connection, + const NameComparisonResult::NameRelation connection, const Message::Section section) { unsigned int count = 0; // Count of RRsets removed @@ -74,30 +73,32 @@ ResponseScrubber::scrubSection(Message& message, // Start looking at the remaining entries in the section. removed = false; - for (; (i != message.endSection(section)) && (!removed); ++i) { + for (; i != message.endSection(section); ++i) { // Loop through the list of names given and see if any are in the // given relationship with the QNAME of this RRset - bool nomatch = true; + bool match = false; for (vector::const_iterator n = names.begin(); - ((n != names.end()) && nomatch); ++n) { + n != names.end(); ++n) { NameComparisonResult result = (*i)->getName().compare(**n); NameComparisonResult::NameRelation relationship = result.getRelation(); if ((relationship == NameComparisonResult::EQUAL) || (relationship == connection)) { - + // RRset in the specified relationship, so a match has // been found - nomatch = false; + match = true; + break; } } // Remove the RRset if there was no match to one of the given names. - if (nomatch) { + if (!match) { message.removeRRset(section, i); ++count; // One more RRset removed removed = true; // Something was removed + break; // It invalidated the iterators, start again } else { // There was a match so this is one more entry we can skip next @@ -107,7 +108,7 @@ ResponseScrubber::scrubSection(Message& message, } } - return count; + return (count); } // Perform the scrubbing of all sections of the message. @@ -126,7 +127,7 @@ ResponseScrubber::scrubAllSections(Message& message, const Name& bailiwick) { count += scrubSection(message, bailiwick_names, NameComparisonResult::SUBDOMAIN, Message::SECTION_ADDITIONAL); - return count; + return (count); } // Scrub across sections. @@ -171,7 +172,6 @@ ResponseScrubber::scrubCrossSections(isc::dns::Message& message) { // superdomain of the names in the question/answer section. return (scrubSection(message, source, NameComparisonResult::SUPERDOMAIN, Message::SECTION_AUTHORITY)); - } // Scrub a message @@ -183,7 +183,7 @@ ResponseScrubber::scrub(const isc::dns::MessagePtr& message, unsigned int sections_removed = scrubAllSections(*message, bailiwick); sections_removed += scrubCrossSections(*message); - return sections_removed; + return (sections_removed); } diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am index b173813d04..e4a4f9273b 100644 --- a/src/bin/stats/Makefile.am +++ b/src/bin/stats/Makefile.am @@ -2,35 +2,35 @@ SUBDIRS = tests pkglibexecdir = $(libexecdir)/@PACKAGE@ -pkglibexec_SCRIPTS = b10-stats -noinst_SCRIPTS = b10-stats_stub +pkglibexec_SCRIPTS = b10-stats b10-stats-httpd b10_statsdir = $(pkgdatadir) -b10_stats_DATA = stats.spec +b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec +b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl -CLEANFILES = stats.spec b10-stats stats.pyc stats.pyo b10-stats_stub stats_stub.pyc stats_stub.pyo +CLEANFILES = b10-stats stats.pyc +CLEANFILES += b10-stats-httpd stats_httpd.pyc -man_MANS = b10-stats.8 -EXTRA_DIST = $(man_MANS) b10-stats.xml +man_MANS = b10-stats.8 b10-stats-httpd.8 +EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml +EXTRA_DIST += stats.spec stats-httpd.spec stats-schema.spec +EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl if ENABLE_MAN b10-stats.8: b10-stats.xml xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-stats.xml -endif +b10-stats-httpd.8: b10-stats-httpd.xml + xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-stats-httpd.xml -stats.spec: stats.spec.pre - $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" stats.spec.pre >$@ +endif # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix b10-stats: stats.py - $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ - -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" \ - -e "s|.*#@@REMOVED@@$$||" stats.py >$@ + $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats.py >$@ chmod a+x $@ -b10-stats_stub: stats_stub.py stats.py - $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \ - -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" stats_stub.py >$@ +b10-stats-httpd: stats_httpd.py + $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats_httpd.py >$@ chmod a+x $@ diff --git a/src/bin/stats/b10-stats-httpd.8 b/src/bin/stats/b10-stats-httpd.8 new file mode 100644 index 0000000000..ed4aafa6c6 --- /dev/null +++ b/src/bin/stats/b10-stats-httpd.8 @@ -0,0 +1,136 @@ +'\" t +.\" Title: b10-stats-httpd +.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] +.\" Generator: DocBook XSL Stylesheets v1.76.1 +.\" Date: Mar 8, 2011 +.\" Manual: BIND10 +.\" Source: BIND10 +.\" Language: English +.\" +.TH "B10\-STATS\-HTTPD" "8" "Mar 8, 2011" "BIND10" "BIND10" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +b10-stats-httpd \- BIND 10 HTTP server for HTTP/XML interface of statistics +.SH "SYNOPSIS" +.HP \w'\fBb10\-stats\-httpd\fR\ 'u +\fBb10\-stats\-httpd\fR [\fB\-v\fR]| [\fB\-\-verbose\fR] +.SH "DESCRIPTION" +.PP + +\fBb10\-stats\-httpd\fR +is a standalone HTTP server\&. It is intended for HTTP/XML interface for statistics module\&. This server process runs as a process separated from the process of the BIND 10 Stats daemon (\fBb10\-stats\fR)\&. The server is initially executed by the BIND 10 boss process (\fBbind10\fR) and eventually exited by it\&. The server is intended to be server requests by HTTP clients like web browsers and third\-party modules\&. When the server is asked, it requests BIND 10 statistics data from +\fBb10\-stats\fR, and it sends the data back in Python dictionary format and the server converts it into XML format\&. The server sends it to the HTTP client\&. The server can send three types of document, which are XML (Extensible Markup Language), XSD (XML Schema definition) and XSL (Extensible Stylesheet Language)\&. The XML document is the statistics data of BIND 10, The XSD document is the data schema of it, and The XSL document is the style sheet to be showed for the web browsers\&. There is different URL for each document\&. But please note that you would be redirected to the URL of XML document if you request the URL of the root document\&. For example, you would be redirected to http://127\&.0\&.0\&.1:8000/bind10/statistics/xml if you request http://127\&.0\&.0\&.1:8000/\&. Please see the manual and the spec file of +\fBb10\-stats\fR +for more details about the items of BIND 10 statistics\&. The server uses CC session in communication with +\fBb10\-stats\fR\&. CC session is provided by +\fBb10\-msgq\fR +which is started by +\fBbind10\fR +in advance\&. The server is implemented by HTTP\-server libraries included in Python 3\&. The server obtains the configuration from the config manager (\fBb10\-cfgmgr\fR) in runtime\&. Please see below for more details about this spec file and configuration of the server\&. +.SH "OPTIONS" +.PP +The argument is as follow: +.PP +\fB\-v\fR, \fB\-\-verbose\fR +.RS 4 + +\fBb10\-stats\-httpd\fR +switches to verbose mode and sends verbose messages to STDOUT\&. +.RE +.SH "FILES" +.PP + +/usr/local/share/bind10\-devel/stats\-httpd\&.spec +\(em the spec file of +\fBb10\-stats\-httpd\fR\&. This file contains configurable settings of +\fBb10\-stats\-httpd\fR\&. This setting can be configured in runtime via +bindctl(1)\&. Please see the manual of +bindctl(1) +about how to configure the settings\&. +.PP +/usr/local/share/bind10\-devel/stats\-schema\&.spec +\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via +bindctl(1)\&. +.PP + +/usr/local/share/bind10\-devel/stats\-httpd\-xml\&.tpl +\(em the template file of XML document\&. +.PP + +/usr/local/share/bind10\-devel/stats\-httpd\-xsd\&.tpl +\(em the template file of XSD document\&. +.PP + +/usr/local/share/bind10\-devel/stats\-httpd\-xsl\&.tpl +\(em the template file of XSL document\&. +.SH "CONFIGURATION AND COMMANDS" +.PP +The configurable setting in +stats\-httpd\&.spec +is: +.PP +\fIlisten_on\fR +.RS 4 +a list of pairs of address and port for +\fBb10\-stats\-httpd\fR +to listen HTTP requests on\&. The pair consists of the +\fIaddress\fR +string and +\fIport\fR +number\&. The default setting is the list of address 127\&.0\&.0\&.1 port 8000\&. If the server is started by the default setting being left, for example, the URL for XML document is http://127\&.0\&.0\&.1:8000/bind10/statistics/xml\&. And also IPv6 addresses can be configured and they works in the runtime environment for dual stack\&. You can change the settings through +bindctl(8)\&. +.RE +.PP +The commands in +stats\-httpd\&.spec +are: +.PP +\fBstatus\fR +.RS 4 +shows the status of +\fBb10\-stats\-httpd\fR +with its PID\&. +.RE +.PP +\fBshutdown\fR +.RS 4 +exits the +\fBb10\-stats\-httpd\fR +process\&. (Note that the BIND 10 boss process will restart this service\&.) +.RE +.SH "SEE ALSO" +.PP + +\fBb10-stats\fR(8), +\fBb10-msgq\fR(8), +\fBb10-cfgmgr\fR(8), +\fBbind10\fR(8), +\fBbindctl\fR(1), +BIND 10 Guide\&. +.SH "HISTORY" +.PP + +\fBb10\-stats\-httpd\fR +was designed and implemented by Naoki Kambe of JPRS in Mar 2011\&. +.SH "COPYRIGHT" +.br +Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC") +.br diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml new file mode 100644 index 0000000000..34c704f509 --- /dev/null +++ b/src/bin/stats/b10-stats-httpd.xml @@ -0,0 +1,221 @@ +]> + + + + + + Mar 8, 2011 + + + + b10-stats-httpd + 8 + BIND10 + + + + b10-stats-httpd + BIND 10 HTTP server for HTTP/XML interface of statistics + + + + + 2011 + Internet Systems Consortium, Inc. ("ISC") + + + + + b10-stats-httpd + | + + + + + DESCRIPTION + + b10-stats-httpd is a standalone HTTP server. It is + intended for HTTP/XML interface for statistics module. This server + process runs as a process separated from the process of the BIND 10 Stats + daemon (b10-stats). The server is initially executed + by the BIND 10 boss process (bind10) and eventually + exited by it. The server is intended to be server requests by HTTP + clients like web browsers and third-party modules. When the server is + asked, it requests BIND 10 statistics data from + b10-stats, and it sends the data back in Python + dictionary format and the server converts it into XML format. The server + sends it to the HTTP client. The server can send three types of document, + which are XML (Extensible Markup Language), XSD (XML Schema definition) + and XSL (Extensible Stylesheet Language). The XML document is the + statistics data of BIND 10, The XSD document is the data schema of it, + and The XSL document is the style sheet to be showed for the web + browsers. There is different URL for each document. But please note that + you would be redirected to the URL of XML document if you request the URL + of the root document. For example, you would be redirected to + http://127.0.0.1:8000/bind10/statistics/xml if you request + http://127.0.0.1:8000/. Please see the manual and the spec file + of b10-stats for more details about the items of BIND + 10 statistics. The server uses CC session in communication + with b10-stats. CC session is provided + by b10-msgq which is started + by bind10 in advance. The server is implemented by + HTTP-server libraries included in Python 3. The server obtains the + configuration from the config manager (b10-cfgmgr) in + runtime. Please see below for more details about this spec file and + configuration of the server. + + + + + OPTIONS + The argument is as follow: + + + , + + + b10-stats-httpd switches to verbose mode and sends + verbose messages to STDOUT. + + + + + + + + FILES + + /usr/local/share/bind10-devel/stats-httpd.spec + + — the spec file of b10-stats-httpd. This file + contains configurable settings + of b10-stats-httpd. This setting can be configured in + runtime via + bindctl1. Please + see the manual + of bindctl1 about + how to configure the settings. + + /usr/local/share/bind10-devel/stats-schema.spec + + — This is a spec file for data schema of + of BIND 10 statistics. This schema cannot be configured + via bindctl1. + + + /usr/local/share/bind10-devel/stats-httpd-xml.tpl + + — the template file of XML document. + + + /usr/local/share/bind10-devel/stats-httpd-xsd.tpl + + — the template file of XSD document. + + + /usr/local/share/bind10-devel/stats-httpd-xsl.tpl + + — the template file of XSL document. + + + + + CONFIGURATION AND COMMANDS + + The configurable setting in + stats-httpd.spec is: + + + + listen_on + + + a list of pairs of address and port for + b10-stats-httpd to listen HTTP requests on. The + pair consists of the address string + and port number. The default setting is the list + of address 127.0.0.1 port 8000. If the server is started by the + default setting being left, for example, the URL for XML document + is http://127.0.0.1:8000/bind10/statistics/xml. And also IPv6 + addresses can be configured and they works in the runtime + environment for dual stack. You can change the settings + through bindctl8. + + + + + + The commands in stats-httpd.spec are: + + + + status + + + shows the status of b10-stats-httpd with its + PID. + + + + + shutdown + + + exits the b10-stats-httpd process. (Note that + the BIND 10 boss process will restart this service.) + + + + + + + + SEE ALSO + + + b10-stats8 + , + + b10-msgq8 + , + + b10-cfgmgr8 + , + + bind108 + , + + bindctl1 + , + BIND 10 Guide. + + + + + HISTORY + + b10-stats-httpd was designed and implemented by Naoki + Kambe of JPRS in Mar 2011. + + + diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8 index 5714234aa3..f69e4d37fa 100644 --- a/src/bin/stats/b10-stats.8 +++ b/src/bin/stats/b10-stats.8 @@ -63,11 +63,17 @@ switches to verbose mode\&. It sends verbose messages to STDOUT\&. .PP /usr/local/share/bind10\-devel/stats\&.spec \(em This is a spec file for -\fBb10\-stats\fR\&. It contains definitions of statistics items of BIND 10 and commands received via +\fBb10\-stats\fR\&. It contains commands for +\fBb10\-stats\fR\&. They can be invoked via +bindctl(1)\&. +.PP +/usr/local/share/bind10\-devel/stats\-schema\&.spec +\(em This is a spec file for data schema of of BIND 10 statistics\&. This schema cannot be configured via bindctl(1)\&. .SH "SEE ALSO" .PP +\fBb10-stats-httpd\fR(8), \fBbind10\fR(8), \fBbindctl\fR(1), \fBb10-auth\fR(8), diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml index 7ec58ddfb3..f0c472dd29 100644 --- a/src/bin/stats/b10-stats.xml +++ b/src/bin/stats/b10-stats.xml @@ -89,16 +89,26 @@ FILES /usr/local/share/bind10-devel/stats.spec + — This is a spec file for b10-stats. It - contains definitions of statistics items of BIND 10 and commands - received via - bindctl1. + contains commands for b10-stats. They can be + invoked + via bindctl1. + + /usr/local/share/bind10-devel/stats-schema.spec + + — This is a spec file for data schema of + of BIND 10 statistics. This schema cannot be configured + via bindctl1. SEE ALSO + + b10-stats-httpd8 + , bind108 , diff --git a/src/bin/stats/stats-httpd-xml.tpl.in b/src/bin/stats/stats-httpd-xml.tpl.in new file mode 100644 index 0000000000..d5846ad61f --- /dev/null +++ b/src/bin/stats/stats-httpd-xml.tpl.in @@ -0,0 +1,24 @@ + + + + + + $xml_string + diff --git a/src/bin/stats/stats-httpd-xsd.tpl.in b/src/bin/stats/stats-httpd-xsd.tpl.in new file mode 100644 index 0000000000..6ad1280bee --- /dev/null +++ b/src/bin/stats/stats-httpd-xsd.tpl.in @@ -0,0 +1,38 @@ + + + + + + XML schema of the statistics + data in BIND 10 + + + + A set of statistics data + + + $xsd_string + + + Version number of syntax + + + + + diff --git a/src/bin/stats/stats-httpd-xsl.tpl.in b/src/bin/stats/stats-httpd-xsl.tpl.in new file mode 100644 index 0000000000..01ffdc681b --- /dev/null +++ b/src/bin/stats/stats-httpd-xsl.tpl.in @@ -0,0 +1,56 @@ + + + + + + + + + BIND 10 Statistics + + + +

BIND 10 Statistics

+ + + + + + +
TitleValue
+ + +
+ $xsl_string +
diff --git a/src/bin/stats/stats-httpd.spec.in b/src/bin/stats/stats-httpd.spec.in new file mode 100644 index 0000000000..6307135cc8 --- /dev/null +++ b/src/bin/stats/stats-httpd.spec.in @@ -0,0 +1,54 @@ +{ + "module_spec": { + "module_name": "StatsHttpd", + "module_description": "Stats HTTP daemon", + "config_data": [ + { + "item_name": "listen_on", + "item_type": "list", + "item_optional": false, + "item_default": [ + { + "address": "127.0.0.1", + "port": 8000 + } + ], + "list_item_spec": { + "item_name": "address", + "item_type": "map", + "item_optional": false, + "item_default": {}, + "map_item_spec": [ + { + "item_name": "address", + "item_type": "string", + "item_optional": true, + "item_default": "127.0.0.1", + "item_description": "listen-on address for HTTP" + }, + { + "item_name": "port", + "item_type": "integer", + "item_optional": true, + "item_default": 8000, + "item_description": "listen-on port for HTTP" + } + ] + }, + "item_description": "http listen-on address and port" + } + ], + "commands": [ + { + "command_name": "status", + "command_description": "Status of the stats httpd", + "command_args": [] + }, + { + "command_name": "shutdown", + "command_description": "Shut down the stats httpd", + "command_args": [] + } + ] + } +} diff --git a/src/bin/stats/stats.spec.pre.in b/src/bin/stats/stats-schema.spec.in similarity index 65% rename from src/bin/stats/stats.spec.pre.in rename to src/bin/stats/stats-schema.spec.in index 6970250d98..37e9c1ae9a 100644 --- a/src/bin/stats/stats.spec.pre.in +++ b/src/bin/stats/stats-schema.spec.in @@ -1,7 +1,7 @@ { "module_spec": { "module_name": "Stats", - "module_description": "Stats daemon", + "module_description": "Statistics data schema", "config_data": [ { "item_name": "report_time", @@ -17,7 +17,7 @@ "item_type": "string", "item_optional": false, "item_default": "1970-01-01T00:00:00Z", - "item_title": "stats.BootTime", + "item_title": "bind10.BootTime", "item_description": "A date time when bind10 process starts initially", "item_format": "date-time" }, @@ -82,59 +82,6 @@ "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially" } ], - "commands": [ - { - "command_name": "status", - "command_description": "identify whether stats module is alive or not", - "command_args": [] - }, - { - "command_name": "show", - "command_description": "show the specified/all statistics data", - "command_args": [ - { - "item_name": "stats_item_name", - "item_type": "string", - "item_optional": true, - "item_default": "" - } - ] - }, - { - "command_name": "set", - "command_description": "set the value of specified name in statistics data", - "command_args": [ - { - "item_name": "stats_data", - "item_type": "map", - "item_optional": false, - "item_default": {}, - "map_item_spec": [] - } - ] - }, - { - "command_name": "remove", - "command_description": "remove the specified name from statistics data", - "command_args": [ - { - "item_name": "stats_item_name", - "item_type": "string", - "item_optional": false, - "item_default": "" - } - ] - }, - { - "command_name": "reset", - "command_description": "reset all statistics data to default values except for several constant names", - "command_args": [] - }, - { - "command_name": "shutdown", - "command_description": "Shut down the stats module", - "command_args": [] - } - ] + "commands": [] } } diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in old mode 100755 new mode 100644 index eeddd925ea..969676e13b --- a/src/bin/stats/stats.py.in +++ b/src/bin/stats/stats.py.in @@ -24,26 +24,24 @@ from optparse import OptionParser, OptionValueError from collections import defaultdict from isc.config.ccsession import ModuleCCSession, create_answer from isc.cc import Session, SessionError -# Note: Following lines are removed in b10-stats #@@REMOVED@@ -if __name__ == 'stats': #@@REMOVED@@ - try: #@@REMOVED@@ - from fake_time import time, strftime, gmtime #@@REMOVED@@ - except ImportError: #@@REMOVED@@ - pass #@@REMOVED@@ # for setproctitle import isc.util.process isc.util.process.rename() -# If B10_FROM_BUILD is set in the environment, we use data files +# If B10_FROM_SOURCE is set in the environment, we use data files # from a directory relative to that, otherwise we use the ones # installed on the system -if "B10_FROM_BUILD" in os.environ: - SPECFILE_LOCATION = os.environ["B10_FROM_BUILD"] + "/src/bin/stats/stats.spec" +if "B10_FROM_SOURCE" in os.environ: + BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" else: PREFIX = "@prefix@" DATAROOTDIR = "@datarootdir@" - SPECFILE_LOCATION = "@datadir@/@PACKAGE@/stats.spec".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) + BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) +SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats.spec" +SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" class Singleton(type): """ @@ -182,8 +180,7 @@ class CCSessionListener(Listener): self.session = self.subject.session = self.cc_session._session # initialize internal data - self.config_spec = self.cc_session.get_module_spec().get_config_spec() - self.stats_spec = self.config_spec + self.stats_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION).get_config_spec() self.stats_data = self.initialize_data(self.stats_spec) # add event handler invoked via SessionSubject object @@ -274,9 +271,6 @@ class CCSessionListener(Listener): """ handle set command """ - if self.verbose: - sys.stdout.write("[b10-stats] 'set' command received, args: "+str(args)+"\n") - # 'args' must be dictionary type self.stats_data.update(args['stats_data']) diff --git a/src/bin/stats/stats.spec.in b/src/bin/stats/stats.spec.in new file mode 100644 index 0000000000..25f6b54827 --- /dev/null +++ b/src/bin/stats/stats.spec.in @@ -0,0 +1,61 @@ +{ + "module_spec": { + "module_name": "Stats", + "module_description": "Stats daemon", + "config_data": [], + "commands": [ + { + "command_name": "status", + "command_description": "identify whether stats module is alive or not", + "command_args": [] + }, + { + "command_name": "show", + "command_description": "show the specified/all statistics data", + "command_args": [ + { + "item_name": "stats_item_name", + "item_type": "string", + "item_optional": true, + "item_default": "" + } + ] + }, + { + "command_name": "set", + "command_description": "set the value of specified name in statistics data", + "command_args": [ + { + "item_name": "stats_data", + "item_type": "map", + "item_optional": false, + "item_default": {}, + "map_item_spec": [] + } + ] + }, + { + "command_name": "remove", + "command_description": "remove the specified name from statistics data", + "command_args": [ + { + "item_name": "stats_item_name", + "item_type": "string", + "item_optional": false, + "item_default": "" + } + ] + }, + { + "command_name": "reset", + "command_description": "reset all statistics data to default values except for several constant names", + "command_args": [] + }, + { + "command_name": "shutdown", + "command_description": "Shut down the stats module", + "command_args": [] + } + ] + } +} diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in new file mode 100644 index 0000000000..97e9c784a4 --- /dev/null +++ b/src/bin/stats/stats_httpd.py.in @@ -0,0 +1,485 @@ +#!@PYTHON@ + +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +A standalone HTTP server for HTTP/XML interface of statistics in BIND 10 + +""" +import sys; sys.path.append ('@@PYTHONPATH@@') +import os +import time +import errno +import select +from optparse import OptionParser, OptionValueError +import http.server +import socket +import string +import xml.etree.ElementTree + +import isc.cc +import isc.config +import isc.util.process + +# If B10_FROM_SOURCE is set in the environment, we use data files +# from a directory relative to that, otherwise we use the ones +# installed on the system +if "B10_FROM_SOURCE" in os.environ: + BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" +else: + PREFIX = "@prefix@" + DATAROOTDIR = "@datarootdir@" + BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) +SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd.spec" +SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec" +XML_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xml.tpl" +XSD_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsd.tpl" +XSL_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsl.tpl" + +# These variables are paths part of URL. +# eg. "http://${address}" + XXX_URL_PATH +XML_URL_PATH = '/bind10/statistics/xml' +XSD_URL_PATH = '/bind10/statistics/xsd' +XSL_URL_PATH = '/bind10/statistics/xsl' +# TODO: This should be considered later. +XSD_NAMESPACE = 'http://bind10.isc.org' + XSD_URL_PATH +DEFAULT_CONFIG = dict(listen_on=[('127.0.0.1', 8000)]) + +# Assign this process name +isc.util.process.rename() + +class HttpHandler(http.server.BaseHTTPRequestHandler): + """HTTP handler class for HttpServer class. The class inhrits the super + class http.server.BaseHTTPRequestHandler. It implemets do_GET() + and do_HEAD() and orverrides log_message()""" + def do_GET(self): + body = self.send_head() + if body is not None: + self.wfile.write(body.encode()) + + def do_HEAD(self): + self.send_head() + + def send_head(self): + try: + if self.path == XML_URL_PATH: + body = self.server.xml_handler() + elif self.path == XSD_URL_PATH: + body = self.server.xsd_handler() + elif self.path == XSL_URL_PATH: + body = self.server.xsl_handler() + else: + if self.path == '/' and 'Host' in self.headers.keys(): + # redirect to XML URL only when requested with '/' + self.send_response(302) + self.send_header( + "Location", + "http://" + self.headers.get('Host') + XML_URL_PATH) + self.end_headers() + return None + else: + # Couldn't find HOST + self.send_error(404) + return None + except StatsHttpdError as err: + self.send_error(500) + if self.server.verbose: + self.server.log_writer( + "[b10-stats-httpd] %s\n" % err) + return None + else: + self.send_response(200) + self.send_header("Content-type", "text/xml") + self.send_header("Content-Length", len(body)) + self.end_headers() + return body + + def log_message(self, format, *args): + """Change the default log format""" + if self.server.verbose: + self.server.log_writer( + "[b10-stats-httpd] %s - - [%s] %s\n" % + (self.address_string(), + self.log_date_time_string(), + format%args)) + +class HttpServerError(Exception): + """Exception class for HttpServer class. It is intended to be + passed from the HttpServer object to the StatsHttpd object.""" + pass + +class HttpServer(http.server.HTTPServer): + """HTTP Server class. The class inherits the super + http.server.HTTPServer. Some parameters are specified as + arguments, which are xml_handler, xsd_handler, xsl_handler, and + log_writer. These all are parameters which the StatsHttpd object + has. The handler parameters are references of functions which + return body of each document. The last parameter log_writer is + reference of writer function to just write to + sys.stderr.write. They are intended to be referred by HttpHandler + object.""" + def __init__(self, server_address, handler, + xml_handler, xsd_handler, xsl_handler, log_writer, verbose=False): + self.server_address = server_address + self.xml_handler = xml_handler + self.xsd_handler = xsd_handler + self.xsl_handler = xsl_handler + self.log_writer = log_writer + self.verbose = verbose + http.server.HTTPServer.__init__(self, server_address, handler) + +class StatsHttpdError(Exception): + """Exception class for StatsHttpd class. It is intended to be + thrown from the the StatsHttpd object to the HttpHandler object or + main routine.""" + pass + +class StatsHttpd: + """The main class of HTTP server of HTTP/XML interface for + statistics module. It handles HTTP requests, and command channel + and config channel CC session. It uses select.select function + while waiting for clients requests.""" + def __init__(self, verbose=False): + self.verbose = verbose + self.running = False + self.poll_intval = 0.5 + self.write_log = sys.stderr.write + self.mccs = None + self.httpd = [] + self.open_mccs() + self.load_config() + self.load_templates() + self.open_httpd() + + def open_mccs(self): + """Opens a ModuleCCSession object""" + # create ModuleCCSession + if self.verbose: + self.write_log("[b10-stats-httpd] Starting CC Session\n") + self.mccs = isc.config.ModuleCCSession( + SPECFILE_LOCATION, self.config_handler, self.command_handler) + self.cc_session = self.mccs._session + # read spec file of stats module and subscribe 'Stats' + self.stats_module_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION) + self.stats_config_spec = self.stats_module_spec.get_config_spec() + self.stats_module_name = self.stats_module_spec.get_module_name() + + def close_mccs(self): + """Closes a ModuleCCSession object""" + if self.mccs is None: + return + if self.verbose: + self.write_log("[b10-stats-httpd] Closing CC Session\n") + self.mccs.close() + self.mccs = None + + def load_config(self, new_config={}): + """Loads configuration from spec file or new configuration + from the config manager""" + # load config + if len(new_config) > 0: + self.config.update(new_config) + else: + self.config = DEFAULT_CONFIG + self.config.update( + dict([ + (itm['item_name'], self.mccs.get_value(itm['item_name'])[0]) + for itm in self.mccs.get_module_spec().get_config_spec() + ]) + ) + # set addresses and ports for HTTP + self.http_addrs = [ (cf['address'], cf['port']) for cf in self.config['listen_on'] ] + + def open_httpd(self): + """Opens sockets for HTTP. Iterating each HTTP address to be + configured in spec file""" + for addr in self.http_addrs: + self.httpd.append(self._open_httpd(addr)) + + def _open_httpd(self, server_address, address_family=None): + try: + # try IPv6 at first + if address_family is not None: + HttpServer.address_family = address_family + elif socket.has_ipv6: + HttpServer.address_family = socket.AF_INET6 + httpd = HttpServer( + server_address, HttpHandler, + self.xml_handler, self.xsd_handler, self.xsl_handler, + self.write_log, self.verbose) + except (socket.gaierror, socket.error, + OverflowError, TypeError) as err: + # try IPv4 next + if HttpServer.address_family == socket.AF_INET6: + httpd = self._open_httpd(server_address, socket.AF_INET) + else: + raise HttpServerError( + "Invalid address %s, port %s: %s: %s" % + (server_address[0], server_address[1], + err.__class__.__name__, err)) + else: + if self.verbose: + self.write_log( + "[b10-stats-httpd] Started on address %s, port %s\n" % + server_address) + return httpd + + def close_httpd(self): + """Closes sockets for HTTP""" + if len(self.httpd) == 0: + return + for ht in self.httpd: + if self.verbose: + self.write_log( + "[b10-stats-httpd] Closing address %s, port %s\n" % + (ht.server_address[0], ht.server_address[1]) + ) + ht.server_close() + self.httpd = [] + + def start(self): + """Starts StatsHttpd objects to run. Waiting for client + requests by using select.select functions""" + self.mccs.start() + self.running = True + while self.running: + try: + (rfd, wfd, xfd) = select.select( + self.get_sockets(), [], [], self.poll_intval) + except select.error as err: + # select.error exception is caught only in the case of + # EINTR, or in other cases it is just thrown. + if err.args[0] == errno.EINTR: + (rfd, wfd, xfd) = ([], [], []) + else: + raise + # FIXME: This module can handle only one request at a + # time. If someone sends only part of the request, we block + # waiting for it until we time out. + # But it isn't so big issue for administration purposes. + for fd in rfd + xfd: + if fd == self.mccs.get_socket(): + self.mccs.check_command(nonblock=False) + continue + for ht in self.httpd: + if fd == ht.socket: + ht.handle_request() + break + self.stop() + + def stop(self): + """Stops the running StatsHttpd objects. Closes CC session and + HTTP handling sockets""" + if self.verbose: + self.write_log("[b10-stats-httpd] Shutting down\n") + self.close_httpd() + self.close_mccs() + + def get_sockets(self): + """Returns sockets to select.select""" + sockets = [] + if self.mccs is not None: + sockets.append(self.mccs.get_socket()) + if len(self.httpd) > 0: + for ht in self.httpd: + sockets.append(ht.socket) + return sockets + + def config_handler(self, new_config): + """Config handler for the ModuleCCSession object. It resets + addresses and ports to listen HTTP requests on.""" + if self.verbose: + self.write_log("[b10-stats-httpd] Loading config : %s\n" % str(new_config)) + for key in new_config.keys(): + if key not in DEFAULT_CONFIG: + if self.verbose: + self.write_log( + "[b10-stats-httpd] Unknown known config: %s" % key) + return isc.config.ccsession.create_answer( + 1, "Unknown known config: %s" % key) + # backup old config + old_config = self.config.copy() + self.close_httpd() + self.load_config(new_config) + try: + self.open_httpd() + except HttpServerError as err: + if self.verbose: + self.write_log("[b10-stats-httpd] %s\n" % err) + self.write_log("[b10-stats-httpd] Restoring old config\n") + # restore old config + self.config_handler(old_config) + return isc.config.ccsession.create_answer( + 1, "[b10-stats-httpd] %s" % err) + else: + return isc.config.ccsession.create_answer(0) + + def command_handler(self, command, args): + """Command handler for the ModuleCCSesson object. It handles + "status" and "shutdown" commands.""" + if command == "status": + if self.verbose: + self.write_log("[b10-stats-httpd] Received 'status' command\n") + return isc.config.ccsession.create_answer( + 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")") + elif command == "shutdown": + if self.verbose: + self.write_log("[b10-stats-httpd] Received 'shutdown' command\n") + self.running = False + return isc.config.ccsession.create_answer( + 0, "Stats Httpd is shutting down.") + else: + if self.verbose: + self.write_log("[b10-stats-httpd] Received unknown command\n") + return isc.config.ccsession.create_answer( + 1, "Unknown command: " + str(command)) + + def get_stats_data(self): + """Requests statistics data to the Stats daemon and returns + the data which obtains from it""" + try: + seq = self.cc_session.group_sendmsg( + isc.config.ccsession.create_command('show'), + self.stats_module_name) + (answer, env) = self.cc_session.group_recvmsg(False, seq) + if answer: + (rcode, value) = isc.config.ccsession.parse_answer(answer) + except (isc.cc.session.SessionTimeout, + isc.cc.session.SessionError) as err: + raise StatsHttpdError("%s: %s" % + (err.__class__.__name__, err)) + else: + if rcode == 0: + return value + else: + raise StatsHttpdError("Stats module: %s" % str(value)) + + def get_stats_spec(self): + """Just returns spec data""" + return self.stats_config_spec + + def load_templates(self): + """Setup the bodies of XSD and XSL documents to be responds to + HTTP clients. Before that it also creates XML tag structures by + using xml.etree.ElementTree.Element class and substitutes + concrete strings with parameters embed in the string.Template + object.""" + # for XSD + xsd_root = xml.etree.ElementTree.Element("all") # started with "all" tag + for item in self.get_stats_spec(): + element = xml.etree.ElementTree.Element( + "element", + dict( name=item["item_name"], + type=item["item_type"] if item["item_type"].lower() != 'real' else 'float', + minOccurs="1", + maxOccurs="1" ), + ) + annotation = xml.etree.ElementTree.Element("annotation") + appinfo = xml.etree.ElementTree.Element("appinfo") + documentation = xml.etree.ElementTree.Element("documentation") + appinfo.text = item["item_title"] + documentation.text = item["item_description"] + annotation.append(appinfo) + annotation.append(documentation) + element.append(annotation) + xsd_root.append(element) + xsd_string = xml.etree.ElementTree.tostring(xsd_root) + self.xsd_body = self.open_template(XSD_TEMPLATE_LOCATION).substitute( + xsd_string=xsd_string, + xsd_namespace=XSD_NAMESPACE + ) + assert self.xsd_body is not None + + # for XSL + xsd_root = xml.etree.ElementTree.Element( + "xsl:template", + dict(match="*")) # started with xml:template tag + for item in self.get_stats_spec(): + tr = xml.etree.ElementTree.Element("tr") + td1 = xml.etree.ElementTree.Element( + "td", { "class" : "title", + "title" : item["item_description"] }) + td1.text = item["item_title"] + td2 = xml.etree.ElementTree.Element("td") + xsl_valueof = xml.etree.ElementTree.Element( + "xsl:value-of", + dict(select=item["item_name"])) + td2.append(xsl_valueof) + tr.append(td1) + tr.append(td2) + xsd_root.append(tr) + xsl_string = xml.etree.ElementTree.tostring(xsd_root) + self.xsl_body = self.open_template(XSL_TEMPLATE_LOCATION).substitute( + xsl_string=xsl_string, + xsd_namespace=XSD_NAMESPACE) + assert self.xsl_body is not None + + def xml_handler(self): + """Handler which requests to Stats daemon to obtain statistics + data and returns the body of XML document""" + xml_list=[] + for (k, v) in self.get_stats_data().items(): + (k, v) = (str(k), str(v)) + elem = xml.etree.ElementTree.Element(k) + elem.text = v + xml_list.append( + xml.etree.ElementTree.tostring(elem)) + xml_string = "".join(xml_list) + self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute( + xml_string=xml_string, + xsd_namespace=XSD_NAMESPACE, + xsd_url_path=XSD_URL_PATH, + xsl_url_path=XSL_URL_PATH) + assert self.xml_body is not None + return self.xml_body + + def xsd_handler(self): + """Handler which just returns the body of XSD document""" + return self.xsd_body + + def xsl_handler(self): + """Handler which just returns the body of XSL document""" + return self.xsl_body + + def open_template(self, file_name): + """It opens a template file, and it loads all lines to a + string variable and returns string. Template object includes + the variable. Limitation of a file size isn't needed there.""" + lines = "".join( + open(file_name, 'r').readlines()) + assert lines is not None + return string.Template(lines) + +if __name__ == "__main__": + try: + parser = OptionParser() + parser.add_option( + "-v", "--verbose", dest="verbose", action="store_true", + help="display more about what is going on") + (options, args) = parser.parse_args() + stats_httpd = StatsHttpd(verbose=options.verbose) + stats_httpd.start() + except OptionValueError: + sys.exit("[b10-stats-httpd] Error parsing options") + except isc.cc.session.SessionError as se: + sys.exit("[b10-stats-httpd] Error creating module, " + + "is the command channel daemon running?") + except HttpServerError as hse: + sys.exit("[b10-stats-httpd] %s" % hse) + except KeyboardInterrupt as kie: + sys.exit("[b10-stats-httpd] Interrupted, exiting") diff --git a/src/bin/stats/stats_stub.py.in b/src/bin/stats/stats_stub.py.in deleted file mode 100755 index 4a4a6414b4..0000000000 --- a/src/bin/stats/stats_stub.py.in +++ /dev/null @@ -1,154 +0,0 @@ -#!@PYTHON@ - -# Copyright (C) 2010 Internet Systems Consortium. -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM -# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL -# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING -# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -__version__ = "$Revision$" - -import sys; sys.path.append ('@@PYTHONPATH@@') -import os -import time -from optparse import OptionParser, OptionValueError -from isc.config.ccsession import ModuleCCSession, create_command, parse_answer, parse_command, create_answer -from isc.cc import Session, SessionError -from stats import get_datetime - -# for setproctitle -import isc.util.process -isc.util.process.rename() - -# If B10_FROM_BUILD is set in the environment, we use data files -# from a directory relative to that, otherwise we use the ones -# installed on the system -if "B10_FROM_BUILD" in os.environ: - SPECFILE_LOCATION = os.environ["B10_FROM_BUILD"] + "/src/bin/stats/stats.spec" -else: - PREFIX = "@prefix@" - DATAROOTDIR = "@datarootdir@" - SPECFILE_LOCATION = "@datadir@/@PACKAGE@/stats.spec".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX) - -class CCSessionStub: - """ - This class is intended to behaves as a sender to Stats module. It - creates MoudleCCSession object and send specified command. - """ - def __init__(self, session=None, verbose=False): - # create ModuleCCSession object - self.verbose = verbose - self.cc_session = ModuleCCSession(SPECFILE_LOCATION, - self.__dummy, self.__dummy, session) - self.module_name = self.cc_session._module_name - self.session = self.cc_session._session - - def __dummy(self, *args): - pass - - def send_command(self, command, args): - """ - send command to stats module with args - """ - cmd = create_command(command, args) - if self.verbose: - sys.stdout.write("[b10-stats_stub] send command : " + str(cmd) + "\n") - seq = self.session.group_sendmsg(cmd, self.module_name) - msg, env = self.session.group_recvmsg(False, seq) # non-blocking is False - if self.verbose: - sys.stdout.write("[b10-stats_stub] received env : " + str(env) + "\n") - sys.stdout.write("[b10-stats_stub] received message : " + str(msg) + "\n") - (ret, arg) = (None, None) - if 'result' in msg: - ret, arg = parse_answer(msg) - elif 'command' in msg: - ret, arg = parse_command(msg) - self.session.group_reply(env, create_answer(0)) - return ret, arg, env - -class BossModuleStub: - """ - This class is customized from CCSessionStub and is intended to behaves - as a virtual Boss module to send to Stats Module. - """ - def __init__(self, session=None, verbose=False): - self.stub = CCSessionStub(session=session, verbose=verbose) - - def send_boottime(self): - return self.stub.send_command("set", {"stats_data": {"bind10.boot_time": get_datetime()}}) - -class AuthModuleStub: - """ - This class is customized CCSessionStub and is intended to behaves - as a virtual Auth module to send to Stats Module. - """ - def __init__(self, session=None, verbose=False): - self.stub = CCSessionStub(session=session, verbose=verbose) - self.count = { "udp": 0, "tcp": 0 } - - def send_udp_query_count(self, cmd="set", cnt=0): - """ - count up udp query count - """ - prt = "udp" - self.count[prt] = 1 - if cnt > 0: - self.count[prt] = cnt - return self.stub.send_command(cmd, - {"stats_data": - {"auth.queries."+prt: self.count[prt]} - }) - - def send_tcp_query_count(self, cmd="set", cnt=0): - """ - set udp query count - """ - prt = "tcp" - self.count[prt] = self.count[prt] + 1 - if cnt > 0: - self.count[prt] = cnt - return self.stub.send_command(cmd, - {"stats_data": - {"auth.queries."+prt: self.count[prt]} - }) - -def main(session=None): - try: - parser=OptionParser() - parser.add_option("-v", "--verbose", dest="verbose", action="store_true", - help="display more about what is going on") - (options, args) = parser.parse_args() - stub = CCSessionStub(session=session, verbose=options.verbose) - boss = BossModuleStub(session=stub.session, verbose=options.verbose) - auth = AuthModuleStub(session=stub.session, verbose=options.verbose) - stub.send_command("status", None) - boss.send_boottime() - t_cnt=0 - u_cnt=81120 - auth.send_udp_query_count(cnt=u_cnt) # This is an example. - while True: - u_cnt = u_cnt + 1 - t_cnt = t_cnt + 1 - auth.send_udp_query_count(cnt=u_cnt) - auth.send_tcp_query_count(cnt=t_cnt) - time.sleep(1) - - except OptionValueError: - sys.stderr.write("[b10-stats_stub] Error parsing options\n") - except SessionError as se: - sys.stderr.write("[b10-stats_stub] Error creating Stats module, " - + "is the command channel daemon running?\n") - except KeyboardInterrupt as kie: - sys.stderr.write("[b10-stats_stub] Interrupted, exiting\n") - -if __name__ == "__main__": - main() diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am index 9e940509d8..5a13277be8 100644 --- a/src/bin/stats/tests/Makefile.am +++ b/src/bin/stats/tests/Makefile.am @@ -1,8 +1,8 @@ -SUBDIRS = isc testdata +SUBDIRS = isc http testdata PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ -PYTESTS = b10-stats_test.py b10-stats_stub_test.py -EXTRA_DIST = $(PYTESTS) fake_time.py -CLEANFILES = fake_time.pyc +PYTESTS = b10-stats_test.py b10-stats-httpd_test.py +EXTRA_DIST = $(PYTESTS) fake_time.py fake_socket.py fake_select.py +CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc # test using command-line arguments, so use check-local target instead of TESTS check-local: @@ -14,6 +14,6 @@ endif for pytest in $(PYTESTS) ; do \ echo Running test: $$pytest ; \ env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \ - B10_FROM_BUILD=$(abs_top_builddir) \ + B10_FROM_SOURCE=$(abs_top_srcdir) \ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \ done diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py new file mode 100644 index 0000000000..07999ea552 --- /dev/null +++ b/src/bin/stats/tests/b10-stats-httpd_test.py @@ -0,0 +1,444 @@ +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest +import os +import http.server +import string +import fake_select +import imp +import sys +import fake_socket + +import isc.cc + +import stats_httpd +stats_httpd.socket = fake_socket +stats_httpd.select = fake_select + +DUMMY_DATA = { + "auth.queries.tcp": 10000, + "auth.queries.udp": 12000, + "bind10.boot_time": "2011-03-04T11:59:05Z", + "report_time": "2011-03-04T11:59:19Z", + "stats.boot_time": "2011-03-04T11:59:06Z", + "stats.last_update_time": "2011-03-04T11:59:07Z", + "stats.lname": "4d70d40a_c@host", + "stats.start_time": "2011-03-04T11:59:06Z", + "stats.timestamp": 1299239959.560846 + } + +def push_answer(stats_httpd): + stats_httpd.cc_session.group_sendmsg( + { 'result': + [ 0, DUMMY_DATA ] }, "Stats") + +def pull_query(stats_httpd): + (msg, env) = stats_httpd.cc_session.group_recvmsg() + if 'result' in msg: + (ret, arg) = isc.config.ccsession.parse_answer(msg) + else: + (ret, arg) = isc.config.ccsession.parse_command(msg) + return (ret, arg, env) + +class TestHttpHandler(unittest.TestCase): + """Tests for HttpHandler class""" + + def setUp(self): + self.verbose = True + self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) + self.assertTrue(type(self.stats_httpd.httpd) is list) + self.httpd = self.stats_httpd.httpd + for ht in self.httpd: + self.assertTrue(ht.verbose) + self.stats_httpd.cc_session.verbose = False + + def test_do_GET(self): + for ht in self.httpd: + self._test_do_GET(ht._handler) + + def _test_do_GET(self, handler): + + # URL is '/bind10/statistics/xml' + handler.path = stats_httpd.XML_URL_PATH + push_answer(self.stats_httpd) + handler.do_GET() + (ret, arg, env) = pull_query(self.stats_httpd) + self.assertEqual(ret, "show") + self.assertIsNone(arg) + self.assertTrue('group' in env) + self.assertEqual(env['group'], 'Stats') + self.assertEqual(handler.response.code, 200) + self.assertEqual(handler.response.headers["Content-type"], "text/xml") + self.assertTrue(handler.response.headers["Content-Length"] > 0) + self.assertTrue(handler.response.wrote_headers) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_URL_PATH)>0) + for (k, v) in DUMMY_DATA.items(): + self.assertTrue(handler.response.body.find(str(k))>0) + self.assertTrue(handler.response.body.find(str(v))>0) + + # URL is '/bind10/statitics/xsd' + handler.path = stats_httpd.XSD_URL_PATH + handler.do_GET() + self.assertEqual(handler.response.code, 200) + self.assertEqual(handler.response.headers["Content-type"], "text/xml") + self.assertTrue(handler.response.headers["Content-Length"] > 0) + self.assertTrue(handler.response.wrote_headers) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) + for (k, v) in DUMMY_DATA.items(): + self.assertTrue(handler.response.body.find(str(k))>0) + + # URL is '/bind10/statitics/xsl' + handler.path = stats_httpd.XSL_URL_PATH + handler.do_GET() + self.assertEqual(handler.response.code, 200) + self.assertEqual(handler.response.headers["Content-type"], "text/xml") + self.assertTrue(handler.response.headers["Content-Length"] > 0) + self.assertTrue(handler.response.wrote_headers) + self.assertTrue(handler.response.body.find(stats_httpd.XSD_NAMESPACE)>0) + for (k, v) in DUMMY_DATA.items(): + self.assertTrue(handler.response.body.find(str(k))>0) + + # 302 redirect + handler.path = '/' + handler.headers = {'Host': 'my.host.domain'} + handler.do_GET() + self.assertEqual(handler.response.code, 302) + self.assertEqual(handler.response.headers["Location"], + "http://my.host.domain%s" % stats_httpd.XML_URL_PATH) + + # 404 NotFound + handler.path = '/path/to/foo/bar' + handler.headers = {} + handler.do_GET() + self.assertEqual(handler.response.code, 404) + + # failure case(connection with Stats is down) + handler.path = stats_httpd.XML_URL_PATH + push_answer(self.stats_httpd) + self.assertFalse(self.stats_httpd.cc_session._socket._closed) + self.stats_httpd.cc_session._socket._closed = True + handler.do_GET() + self.stats_httpd.cc_session._socket._closed = False + self.assertEqual(handler.response.code, 500) + self.stats_httpd.cc_session._clear_queues() + + # failure case(Stats module returns err) + handler.path = stats_httpd.XML_URL_PATH + self.stats_httpd.cc_session.group_sendmsg( + { 'result': [ 1, "I have an error." ] }, "Stats") + self.assertFalse(self.stats_httpd.cc_session._socket._closed) + self.stats_httpd.cc_session._socket._closed = False + handler.do_GET() + self.assertEqual(handler.response.code, 500) + self.stats_httpd.cc_session._clear_queues() + + def test_do_HEAD(self): + for ht in self.httpd: + self._test_do_HEAD(ht._handler) + + def _test_do_HEAD(self, handler): + handler.path = '/path/to/foo/bar' + handler.do_HEAD() + self.assertEqual(handler.response.code, 404) + + def test_log_message(self): + for ht in self.httpd: + self._test_log_message(ht._handler) + + def _test_log_message(self, handler): + # switch write_log function + handler.server.log_writer = handler.response._write_log + log_message = 'ABCDEFG' + handler.log_message("%s", log_message) + self.assertEqual(handler.response.log, + "[b10-stats-httpd] %s - - [%s] %s\n" % + (handler.address_string(), + handler.log_date_time_string(), + log_message)) + +class TestHttpServerError(unittest.TestCase): + """Tests for HttpServerError exception""" + + def test_raises(self): + try: + raise stats_httpd.HttpServerError('Nothing') + except stats_httpd.HttpServerError as err: + self.assertEqual(str(err), 'Nothing') + +class TestHttpServer(unittest.TestCase): + """Tests for HttpServer class""" + + def test_httpserver(self): + self.verbose = True + self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) + self.stats_httpd.cc_session.verbose = False + for ht in self.stats_httpd.httpd: + self.assertTrue(ht.server_address in self.stats_httpd.http_addrs) + self.assertEqual(ht.verbose, self.verbose) + self.assertEqual(ht.xml_handler, self.stats_httpd.xml_handler) + self.assertEqual(ht.xsd_handler, self.stats_httpd.xsd_handler) + self.assertEqual(ht.xsl_handler, self.stats_httpd.xsl_handler) + self.assertEqual(ht.log_writer, self.stats_httpd.write_log) + self.assertTrue(isinstance(ht._handler, stats_httpd.HttpHandler)) + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + +class TestStatsHttpdError(unittest.TestCase): + """Tests for StatsHttpdError exception""" + + def test_raises(self): + try: + raise stats_httpd.StatsHttpdError('Nothing') + except stats_httpd.StatsHttpdError as err: + self.assertEqual(str(err), 'Nothing') + +class TestStatsHttpd(unittest.TestCase): + """Tests for StatsHttpd class""" + + def setUp(self): + self.verbose = True + fake_socket._CLOSED = False + fake_socket.has_ipv6 = True + self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) + self.stats_httpd.cc_session.verbose = False + + def tearDown(self): + self.stats_httpd.stop() + + def test_init(self): + self.assertTrue(self.stats_httpd.verbose) + self.assertFalse(self.stats_httpd.mccs.get_socket()._closed) + self.assertEqual(self.stats_httpd.mccs.get_socket().fileno(), + id(self.stats_httpd.mccs.get_socket())) + for ht in self.stats_httpd.httpd: + self.assertFalse(ht.socket._closed) + self.assertEqual(ht.socket.fileno(), id(ht.socket)) + fake_socket._CLOSED = True + self.assertRaises(isc.cc.session.SessionError, + stats_httpd.StatsHttpd) + fake_socket._CLOSED = False + + def test_mccs(self): + self.stats_httpd.open_mccs() + self.assertTrue( + isinstance(self.stats_httpd.mccs.get_socket(), fake_socket.socket)) + self.assertTrue( + isinstance(self.stats_httpd.cc_session, isc.cc.session.Session)) + self.assertTrue( + isinstance(self.stats_httpd.stats_module_spec, isc.config.ModuleSpec)) + for cfg in self.stats_httpd.stats_config_spec: + self.assertTrue('item_name' in cfg) + self.assertTrue(cfg['item_name'] in DUMMY_DATA) + self.assertTrue(len(self.stats_httpd.stats_config_spec), len(DUMMY_DATA)) + + def test_load_config(self): + self.stats_httpd.load_config() + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + + def test_httpd(self): + # dual stack (addresses is ipv4 and ipv6) + fake_socket.has_ipv6 = True + self.assertTrue(('127.0.0.1', 8000) in set(self.stats_httpd.http_addrs)) + self.stats_httpd.http_addrs = [ ('::1', 8000), ('127.0.0.1', 8000) ] + self.assertTrue( + stats_httpd.HttpServer.address_family in set([fake_socket.AF_INET, fake_socket.AF_INET6])) + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() + + # dual stack (address is ipv6) + fake_socket.has_ipv6 = True + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() + + # dual stack (address is ipv4) + fake_socket.has_ipv6 = True + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() + + # only-ipv4 single stack + fake_socket.has_ipv6 = False + self.stats_httpd.http_addrs = [ ('127.0.0.1', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() + + # only-ipv4 single stack (force set ipv6 ) + fake_socket.has_ipv6 = False + self.stats_httpd.http_addrs = [ ('::1', 8000) ] + self.assertRaises(stats_httpd.HttpServerError, + self.stats_httpd.open_httpd) + + # hostname + self.stats_httpd.http_addrs = [ ('localhost', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() + + self.stats_httpd.http_addrs = [ ('my.host.domain', 8000) ] + self.stats_httpd.open_httpd() + for ht in self.stats_httpd.httpd: + self.assertTrue(isinstance(ht.socket, fake_socket.socket)) + self.stats_httpd.close_httpd() + + # over flow of port number + self.stats_httpd.http_addrs = [ ('', 80000) ] + self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + # negative + self.stats_httpd.http_addrs = [ ('', -8000) ] + self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + # alphabet + self.stats_httpd.http_addrs = [ ('', 'ABCDE') ] + self.assertRaises(stats_httpd.HttpServerError, self.stats_httpd.open_httpd) + + def test_start(self): + self.stats_httpd.cc_session.group_sendmsg( + { 'command': [ "shutdown" ] }, "StatsHttpd") + self.stats_httpd.start() + self.stats_httpd = stats_httpd.StatsHttpd(self.verbose) + self.stats_httpd.cc_session.verbose = False + self.assertRaises( + fake_select.error, self.stats_httpd.start) + + def test_stop(self): + # success case + fake_socket._CLOSED = False + self.stats_httpd.stop() + self.assertFalse(self.stats_httpd.running) + self.assertIsNone(self.stats_httpd.mccs) + for ht in self.stats_httpd.httpd: + self.assertTrue(ht.socket._closed) + self.assertTrue(self.stats_httpd.cc_session._socket._closed) + # failure case + self.stats_httpd.cc_session._socket._closed = False + self.stats_httpd.open_mccs() + self.stats_httpd.cc_session._socket._closed = True + self.stats_httpd.stop() # No excetion raises + self.stats_httpd.cc_session._socket._closed = False + + def test_open_template(self): + # successful conditions + tmpl = self.stats_httpd.open_template(stats_httpd.XML_TEMPLATE_LOCATION) + self.assertTrue(isinstance(tmpl, string.Template)) + opts = dict( + xml_string="", + xsd_namespace="http://host/path/to/", + xsd_url_path="/path/to/", + xsl_url_path="/path/to/") + lines = tmpl.substitute(opts) + for n in opts: + self.assertTrue(lines.find(opts[n])>0) + tmpl = self.stats_httpd.open_template(stats_httpd.XSD_TEMPLATE_LOCATION) + self.assertTrue(isinstance(tmpl, string.Template)) + opts = dict( + xsd_string="", + xsd_namespace="http://host/path/to/") + lines = tmpl.substitute(opts) + for n in opts: + self.assertTrue(lines.find(opts[n])>0) + tmpl = self.stats_httpd.open_template(stats_httpd.XSL_TEMPLATE_LOCATION) + self.assertTrue(isinstance(tmpl, string.Template)) + opts = dict( + xsl_string="", + xsd_namespace="http://host/path/to/") + lines = tmpl.substitute(opts) + for n in opts: + self.assertTrue(lines.find(opts[n])>0) + # unsuccessful condition + self.assertRaises( + IOError, + self.stats_httpd.open_template, '/path/to/foo/bar') + + def test_commands(self): + self.assertEqual(self.stats_httpd.command_handler("status", None), + isc.config.ccsession.create_answer( + 0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")) + self.stats_httpd.running = True + self.assertEqual(self.stats_httpd.command_handler("shutdown", None), + isc.config.ccsession.create_answer( + 0, "Stats Httpd is shutting down.")) + self.assertFalse(self.stats_httpd.running) + self.assertEqual( + self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None), + isc.config.ccsession.create_answer( + 1, "Unknown command: __UNKNOWN_COMMAND__")) + + def test_config(self): + self.assertEqual( + self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)), + isc.config.ccsession.create_answer( + 1, "Unknown known config: _UNKNOWN_KEY_")) + self.assertEqual( + self.stats_httpd.config_handler( + dict(listen_on=[dict(address="::2",port=8000)])), + isc.config.ccsession.create_answer(0)) + self.assertTrue("listen_on" in self.stats_httpd.config) + for addr in self.stats_httpd.config["listen_on"]: + self.assertTrue("address" in addr) + self.assertTrue("port" in addr) + self.assertTrue(addr["address"] == "::2") + self.assertTrue(addr["port"] == 8000) + + self.assertEqual( + self.stats_httpd.config_handler( + dict(listen_on=[dict(address="::1",port=80)])), + isc.config.ccsession.create_answer(0)) + self.assertTrue("listen_on" in self.stats_httpd.config) + for addr in self.stats_httpd.config["listen_on"]: + self.assertTrue("address" in addr) + self.assertTrue("port" in addr) + self.assertTrue(addr["address"] == "::1") + self.assertTrue(addr["port"] == 80) + + self.assertEqual( + self.stats_httpd.config_handler( + dict(listen_on=[dict(address="1.2.3.4",port=54321)])), + isc.config.ccsession.create_answer(0)) + self.assertTrue("listen_on" in self.stats_httpd.config) + for addr in self.stats_httpd.config["listen_on"]: + self.assertTrue("address" in addr) + self.assertTrue("port" in addr) + self.assertTrue(addr["address"] == "1.2.3.4") + self.assertTrue(addr["port"] == 54321) + (ret, arg) = isc.config.ccsession.parse_answer( + self.stats_httpd.config_handler( + dict(listen_on=[dict(address="1.2.3.4",port=543210)])) + ) + self.assertEqual(ret, 1) + + def test_for_without_B10_FROM_SOURCE(self): + # just lets it go through the code without B10_FROM_SOURCE env + # variable + if "B10_FROM_SOURCE" in os.environ: + tmppath = os.environ["B10_FROM_SOURCE"] + os.environ.pop("B10_FROM_SOURCE") + imp.reload(stats_httpd) + os.environ["B10_FROM_SOURCE"] = tmppath + imp.reload(stats_httpd) + stats_httpd.socket = fake_socket + stats_httpd.select = fake_select + +if __name__ == "__main__": + unittest.main() diff --git a/src/bin/stats/tests/b10-stats_stub_test.py b/src/bin/stats/tests/b10-stats_stub_test.py deleted file mode 100644 index b8983c53ec..0000000000 --- a/src/bin/stats/tests/b10-stats_stub_test.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (C) 2010 Internet Systems Consortium. -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM -# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL -# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING -# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -__version__ = "$Revision$" - -# -# Tests for the stats stub module -# -import unittest -import time -import os -import imp -import stats_stub -from isc.cc.session import Session -from stats_stub import CCSessionStub, BossModuleStub, AuthModuleStub -from stats import get_datetime - -class TestStats(unittest.TestCase): - - def setUp(self): - self.session = Session() - self.stub = CCSessionStub(session=self.session, verbose=True) - self.boss = BossModuleStub(session=self.session, verbose=True) - self.auth = AuthModuleStub(session=self.session, verbose=True) - self.env = {'from': self.session.lname, 'group': 'Stats', - 'instance': '*', 'to':'*', - 'type':'send','seq':0} - self.result_ok = {'result': [0]} - - def tearDown(self): - self.session.close() - - def test_stub(self): - """ - Test for send_command of CCSessionStub object - """ - env = self.env - result_ok = self.result_ok - self.assertEqual(('status', None, env), - self.stub.send_command('status', None)) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - self.assertEqual(('shutdown', None, env), - self.stub.send_command('shutdown', None)) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - self.assertEqual(('show', None, env), - self.stub.send_command('show', None)) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - self.assertEqual(('set', {'atest': 100.0}, env), - self.stub.send_command('set', {'atest': 100.0})) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - - def test_boss_stub(self): - """ - Test for send_command of BossModuleStub object - """ - env = self.env - result_ok = self.result_ok - self.assertEqual(('set', {"stats_data": - {"bind10.boot_time": get_datetime()} - }, env), self.boss.send_boottime()) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - - def test_auth_stub(self): - """ - Test for send_command of AuthModuleStub object - """ - env = self.env - result_ok = self.result_ok - self.assertEqual( - ('set', {"stats_data": {"auth.queries.udp": 1}}, env), - self.auth.send_udp_query_count()) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - self.assertEqual( - ('set', {"stats_data": {"auth.queries.tcp": 1}}, env), - self.auth.send_tcp_query_count()) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - self.assertEqual( - ('set', {"stats_data": {"auth.queries.udp": 100}}, env), - self.auth.send_udp_query_count(cmd='set', cnt=100)) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - self.assertEqual( - ('set', {"stats_data": {"auth.queries.tcp": 99}}, env), - self.auth.send_tcp_query_count(cmd='set', cnt=99)) - self.assertEqual(result_ok, self.session.get_message("Stats", None)) - - def test_func_main(self): - # explicitly make failed - self.session.close() - stats_stub.main(session=self.session) - - def test_osenv(self): - """ - test for not having environ "B10_FROM_BUILD" - """ - if "B10_FROM_BUILD" in os.environ: - path = os.environ["B10_FROM_BUILD"] - os.environ.pop("B10_FROM_BUILD") - imp.reload(stats_stub) - os.environ["B10_FROM_BUILD"] = path - imp.reload(stats_stub) - -if __name__ == "__main__": - unittest.main() diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py index 3566bc47e3..eccabdcbb0 100644 --- a/src/bin/stats/tests/b10-stats_test.py +++ b/src/bin/stats/tests/b10-stats_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010,2011 Internet Systems Consortium. +# Copyright (C) 2010, 2011 Internet Systems Consortium. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -23,7 +23,11 @@ import unittest import imp from isc.cc.session import Session, SessionError from isc.config.ccsession import ModuleCCSession, ModuleCCSessionError +from fake_time import time, strftime, gmtime import stats +stats.time = time +stats.strftime = strftime +stats.gmtime = gmtime from stats import SessionSubject, CCSessionListener, get_timestamp, get_datetime from fake_time import _TEST_TIME_SECS, _TEST_TIME_STRF @@ -535,12 +539,19 @@ class TestStats2(unittest.TestCase): Test for specfile """ - if "B10_FROM_BUILD" in os.environ: + if "B10_FROM_SOURCE" in os.environ: self.assertEqual(stats.SPECFILE_LOCATION, - os.environ["B10_FROM_BUILD"] + "/src/bin/stats/stats.spec") + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats.spec") + self.assertEqual(stats.SCHEMA_SPECFILE_LOCATION, + os.environ["B10_FROM_SOURCE"] + os.sep + \ + "src" + os.sep + "bin" + os.sep + "stats" + \ + os.sep + "stats-schema.spec") imp.reload(stats) # change path of SPECFILE_LOCATION stats.SPECFILE_LOCATION = TEST_SPECFILE_LOCATION + stats.SCHEMA_SPECFILE_LOCATION = TEST_SPECFILE_LOCATION self.assertEqual(stats.SPECFILE_LOCATION, TEST_SPECFILE_LOCATION) self.subject = stats.SessionSubject(session=self.session, verbose=True) self.session = self.subject.session @@ -631,13 +642,13 @@ class TestStats2(unittest.TestCase): def test_osenv(self): """ - test for not having environ "B10_FROM_BUILD" + test for not having environ "B10_FROM_SOURCE" """ - if "B10_FROM_BUILD" in os.environ: - path = os.environ["B10_FROM_BUILD"] - os.environ.pop("B10_FROM_BUILD") + if "B10_FROM_SOURCE" in os.environ: + path = os.environ["B10_FROM_SOURCE"] + os.environ.pop("B10_FROM_SOURCE") imp.reload(stats) - os.environ["B10_FROM_BUILD"] = path + os.environ["B10_FROM_SOURCE"] = path imp.reload(stats) def result_ok(*args): diff --git a/src/bin/stats/tests/fake_select.py b/src/bin/stats/tests/fake_select.py new file mode 100644 index 0000000000..ca0ca82619 --- /dev/null +++ b/src/bin/stats/tests/fake_select.py @@ -0,0 +1,43 @@ +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +A mock-up module of select + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import fake_socket +import errno + +class error(Exception): + pass + +def select(rlst, wlst, xlst, timeout): + if type(timeout) != int and type(timeout) != float: + raise TypeError("Error: %s must be integer or float" + % timeout.__class__.__name__) + for s in rlst + wlst + xlst: + if type(s) != fake_socket.socket: + raise TypeError("Error: %s must be a dummy socket" + % s.__class__.__name__) + s._called = s._called + 1 + if s._called > 3: + raise error("Something is happened!") + elif s._called > 2: + raise error(errno.EINTR) + return (rlst, wlst, xlst) diff --git a/src/bin/stats/tests/fake_socket.py b/src/bin/stats/tests/fake_socket.py new file mode 100644 index 0000000000..4e3a4581a5 --- /dev/null +++ b/src/bin/stats/tests/fake_socket.py @@ -0,0 +1,70 @@ +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +A mock-up module of socket + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import re + +AF_INET = 'AF_INET' +AF_INET6 = 'AF_INET6' +_ADDRFAMILY = AF_INET +has_ipv6 = True +_CLOSED = False + +class gaierror(Exception): + pass + +class error(Exception): + pass + +class socket: + + def __init__(self, family=None): + if family is None: + self.address_family = _ADDRFAMILY + else: + self.address_family = family + self._closed = _CLOSED + if self._closed: + raise error('socket is already closed!') + self._called = 0 + + def close(self): + self._closed = True + + def fileno(self): + return id(self) + + def bind(self, server_class): + (self.server_address, self.server_port) = server_class + if self.address_family not in set([AF_INET, AF_INET6]): + raise error("Address family not supported by protocol: %s" % self.address_family) + if self.address_family == AF_INET6 and not has_ipv6: + raise error("Address family not supported in this machine: %s has_ipv6: %s" + % (self.address_family, str(has_ipv6))) + if self.address_family == AF_INET and re.search(':', self.server_address) is not None: + raise gaierror("Address family for hostname not supported : %s %s" % (self.server_address, self.address_family)) + if self.address_family == AF_INET6 and re.search(':', self.server_address) is None: + raise error("Cannot assign requested address : %s" % str(self.server_address)) + if type(self.server_port) is not int: + raise TypeError("an integer is required: %s" % str(self.server_port)) + if self.server_port < 0 or self.server_port > 65535: + raise OverflowError("port number must be 0-65535.: %s" % str(self.server_port)) diff --git a/src/bin/stats/tests/http/Makefile.am b/src/bin/stats/tests/http/Makefile.am new file mode 100644 index 0000000000..879e8a8883 --- /dev/null +++ b/src/bin/stats/tests/http/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = __init__.py server.py +CLEANFILES = __init__.pyc server.pyc diff --git a/src/bin/stats/tests/isc/utils/__init__.py b/src/bin/stats/tests/http/__init__.py similarity index 100% rename from src/bin/stats/tests/isc/utils/__init__.py rename to src/bin/stats/tests/http/__init__.py diff --git a/src/bin/stats/tests/http/server.py b/src/bin/stats/tests/http/server.py new file mode 100644 index 0000000000..70ed6faa30 --- /dev/null +++ b/src/bin/stats/tests/http/server.py @@ -0,0 +1,96 @@ +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +A mock-up module of http.server + +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" + +import fake_socket + +class DummyHttpResponse: + def __init__(self, path): + self.path = path + self.headers={} + self.log = "" + + def _write_log(self, msg): + self.log = self.log + msg + +class HTTPServer: + """ + A mock-up class of http.server.HTTPServer + """ + address_family = fake_socket.AF_INET + def __init__(self, server_class, handler_class): + self.socket = fake_socket.socket(self.address_family) + self.server_class = server_class + self.socket.bind(self.server_class) + self._handler = handler_class(None, None, self) + + def handle_request(self): + pass + + def server_close(self): + self.socket.close() + +class BaseHTTPRequestHandler: + """ + A mock-up class of http.server.BaseHTTPRequestHandler + """ + + def __init__(self, request, client_address, server): + self.path = "/path/to" + self.headers = {} + self.server = server + self.response = DummyHttpResponse(path=self.path) + self.response.write = self._write + self.wfile = self.response + + def send_response(self, code=0): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.code = code + + def send_header(self, key, value): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.headers[key] = value + + def end_headers(self): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.wrote_headers = True + + def send_error(self, code, message=None): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.code = code + self.response.body = message + + def address_string(self): + return 'dummyhost' + + def log_date_time_string(self): + return '[DD/MM/YYYY HH:MI:SS]' + + def _write(self, obj): + if self.path != self.response.path: + self.response = DummyHttpResponse(path=self.path) + self.response.body = obj.decode() + diff --git a/src/bin/stats/tests/isc/cc/session.py b/src/bin/stats/tests/isc/cc/session.py index 4d12adcc08..e16d6a9abc 100644 --- a/src/bin/stats/tests/isc/cc/session.py +++ b/src/bin/stats/tests/isc/cc/session.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010 Internet Systems Consortium. +# Copyright (C) 2010,2011 Internet Systems Consortium. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -13,11 +13,16 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# This module is a mock-up class of isc.cc.session +""" +A mock-up module of isc.cc.session -__version__ = "$Revision$" +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" import sys +import fake_socket # set a dummy lname _TEST_LNAME = '123abc@xxxx' @@ -33,12 +38,18 @@ class Queue(): class SessionError(Exception): pass +class SessionTimeout(Exception): + pass + class Session: def __init__(self, socket_file=None, verbose=False): self._lname = _TEST_LNAME self.message_queue = [] self.old_message_queue = [] - self._socket = True + try: + self._socket = fake_socket.socket() + except fake_socket.error as se: + raise SessionError(se) self.verbose = verbose @property @@ -46,13 +57,17 @@ class Session: return self._lname def close(self): - self._socket = False + self._socket.close() + + def _clear_queues(self): + while len(self.message_queue) > 0: + self.dequeue() def _next_sequence(self, que=None): return len(self.message_queue) def enqueue(self, msg=None, env={}): - if not self._socket: + if self._socket._closed: raise SessionError("Session has been closed.") seq = self._next_sequence() env.update({"seq": 0}) # fixed here @@ -62,12 +77,12 @@ class Session: sys.stdout.write("[Session] enqueue: " + str(que.dump()) + "\n") return seq - def dequeue(self, seq=0): - if not self._socket: + def dequeue(self): + if self._socket._closed: raise SessionError("Session has been closed.") que = None try: - que = self.message_queue.pop(seq) + que = self.message_queue.pop(0) # always pop at index 0 self.old_message_queue.append(que) except IndexError: que = Queue() @@ -76,7 +91,7 @@ class Session: return que def get_queue(self, seq=None): - if not self._socket: + if self._socket._closed: raise SessionError("Session has been closed.") if seq is None: seq = len(self.message_queue) - 1 @@ -99,7 +114,7 @@ class Session: "instance": instance }) def group_recvmsg(self, nonblock=True, seq=0): - que = self.dequeue(seq) + que = self.dequeue() return que.msg, que.env def group_reply(self, routing, msg): @@ -112,7 +127,7 @@ class Session: "reply": routing["seq"] }) def get_message(self, group, to='*'): - if not self._socket: + if self._socket._closed: raise SessionError("Session has been closed.") que = Queue() for q in self.message_queue: @@ -124,3 +139,10 @@ class Session: sys.stdout.write("[Session] get_message: " + str(que.dump()) + "\n") return q.msg + def group_subscribe(self, group, instance = "*"): + if self._socket._closed: + raise SessionError("Session has been closed.") + + def group_unsubscribe(self, group, instance = "*"): + if self._socket._closed: + raise SessionError("Session has been closed.") diff --git a/src/bin/stats/tests/isc/config/ccsession.py b/src/bin/stats/tests/isc/config/ccsession.py index fc285a7b5a..a4e9c37c1f 100644 --- a/src/bin/stats/tests/isc/config/ccsession.py +++ b/src/bin/stats/tests/isc/config/ccsession.py @@ -1,4 +1,4 @@ -# Copyright (C) 2010 Internet Systems Consortium. +# Copyright (C) 2010,2011 Internet Systems Consortium. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -13,16 +13,22 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# This module is a mock-up class of isc.cc.session +""" +A mock-up module of isc.cc.session -__version__ = "$Revision$" +*** NOTE *** +It is only for testing stats_httpd module and not reusable for +external module. +""" import json +import os from isc.cc.session import Session COMMAND_CONFIG_UPDATE = "config_update" def parse_answer(msg): + assert 'result' in msg try: return msg['result'][0], msg['result'][1] except IndexError: @@ -35,6 +41,7 @@ def create_answer(rcode, arg = None): return { 'result': [ rcode, arg ] } def parse_command(msg): + assert 'command' in msg try: return msg['command'][0], msg['command'][1] except IndexError: @@ -47,9 +54,21 @@ def create_command(command_name, params = None): return {"command": [command_name, params]} def module_spec_from_file(spec_file, check = True): - file = open(spec_file) - module_spec = json.loads(file.read()) - return ModuleSpec(module_spec['module_spec'], check) + try: + file = open(spec_file) + json_str = file.read() + module_spec = json.loads(json_str) + file.close() + return ModuleSpec(module_spec['module_spec'], check) + except IOError as ioe: + raise ModuleSpecError("JSON read error: " + str(ioe)) + except ValueError as ve: + raise ModuleSpecError("JSON parse error: " + str(ve)) + except KeyError as err: + raise ModuleSpecError("Data definition has no module_spec element") + +class ModuleSpecError(Exception): + pass class ModuleSpec: def __init__(self, module_spec, check = True): @@ -67,10 +86,34 @@ class ModuleSpec: class ModuleCCSessionError(Exception): pass +class DataNotFoundError(Exception): + pass + class ConfigData: def __init__(self, specification): self.specification = specification + def get_value(self, identifier): + """Returns a tuple where the first item is the value at the + given identifier, and the second item is absolutely False + even if the value is an unset default or not. Raises an + DataNotFoundError if the identifier is not found in the + specification file. + *** NOTE *** + There are some differences from the original method. This + method never handles local settings like the original + method. But these different behaviors aren't so big issues + for a mock-up method of stats_httpd because stats_httpd + calls this method at only first.""" + for config_map in self.get_module_spec().get_config_spec(): + if config_map['item_name'] == identifier: + if 'item_default' in config_map: + return config_map['item_default'], False + raise DataNotFoundError("item_name %s is not found in the specfile" % identifier) + + def get_module_spec(self): + return self.specification + class ModuleCCSession(ConfigData): def __init__(self, spec_file_name, config_handler, command_handler, cc_session = None): module_spec = module_spec_from_file(spec_file_name) @@ -111,3 +154,7 @@ class ModuleCCSession(ConfigData): def get_module_spec(self): return self.specification + + def get_socket(self): + return self._session._socket + diff --git a/src/bin/stats/tests/isc/util/process.py b/src/bin/stats/tests/isc/util/process.py index 7274a48c2a..0f764c1872 100644 --- a/src/bin/stats/tests/isc/util/process.py +++ b/src/bin/stats/tests/isc/util/process.py @@ -13,6 +13,9 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# A dummy function of isc.util.process.rename() +""" +A dummy function of isc.util.process.rename() +""" + def rename(name=None): pass diff --git a/src/bin/stats/tests/isc/utils/Makefile.am b/src/bin/stats/tests/isc/utils/Makefile.am deleted file mode 100644 index b09fdeef34..0000000000 --- a/src/bin/stats/tests/isc/utils/Makefile.am +++ /dev/null @@ -1,2 +0,0 @@ -EXTRA_DIST = __init__.py process.py -CLEANFILES = __init__.pyc process.pyc diff --git a/src/bin/stats/tests/isc/utils/process.py b/src/bin/stats/tests/isc/utils/process.py deleted file mode 100644 index 4c0cf8c1dd..0000000000 --- a/src/bin/stats/tests/isc/utils/process.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2010 Internet Systems Consortium. -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM -# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL -# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING -# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -# A dummy function of isc.utils.process.rename() -def rename(name=None): - pass diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am index dfa771474f..d4efbc77e0 100644 --- a/src/bin/xfrin/tests/Makefile.am +++ b/src/bin/xfrin/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py index 04d04a67d1..0ccbbb8139 100644 --- a/src/bin/xfrin/tests/xfrin_test.py +++ b/src/bin/xfrin/tests/xfrin_test.py @@ -35,6 +35,8 @@ TEST_MASTER_IPV6_ADDRINFO = (socket.AF_INET6, socket.SOCK_STREAM, # If some other process uses this port test will fail. TEST_MASTER_PORT = '53535' +TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==") + soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS, 'master.example.com. admin.example.com ' + '1234 3600 1800 2419200 7200') @@ -51,6 +53,13 @@ default_answers = [soa_rrset] class XfrinTestException(Exception): pass +def strip_mutable_tsig_data(data): + # Unfortunately we cannot easily compare TSIG RR because we can't tweak + # current time. As a work around this helper function strips off the time + # dependent part of TSIG RDATA, i.e., the MAC (assuming HMAC-MD5) and + # Time Signed. + return data[0:-32] + data[-26:-22] + data[-6:] + class MockXfrin(Xfrin): # This is a class attribute of a callable object that specifies a non # default behavior triggered in _cc_check_command(). Specific test methods @@ -60,6 +69,7 @@ class MockXfrin(Xfrin): check_command_hook = None def _cc_setup(self): + self._tsig_key_str = None pass def _get_db_file(self): @@ -196,10 +206,46 @@ class TestXfrinConnection(unittest.TestCase): RRClass.CH()) c.close() + def test_send_query(self): + def create_msg(query_type): + msg = Message(Message.RENDER) + query_id = 0x1035 + msg.set_qid(query_id) + msg.set_opcode(Opcode.QUERY()) + msg.set_rcode(Rcode.NOERROR()) + query_question = Question(Name("example.com."), RRClass.IN(), query_type) + msg.add_question(query_question) + return msg + self.conn._create_query = create_msg + # soa request + self.conn._send_query(RRType.SOA()) + self.assertEqual(self.conn.query_data, b'\x00\x1d\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\x06\x00\x01') + # axfr request + self.conn._send_query(RRType.AXFR()) + self.assertEqual(self.conn.query_data, b'\x00\x1d\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01') + + # soa request with tsig + self.conn._tsig_ctx = TSIGContext(TSIG_KEY) + self.conn._send_query(RRType.SOA()) + tsig_soa_data = strip_mutable_tsig_data(self.conn.query_data) + self.assertEqual(tsig_soa_data, b'\x00n\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x07example\x03com\x00\x00\x06\x00\x01\x07example\x03com\x00\x00\xfa\x00\xff\x00\x00\x00\x00\x00:\x08hmac-md5\x07sig-alg\x03reg\x03int\x00\x01,\x00\x10\x105\x00\x00\x00\x00') + + # axfr request with tsig + self.conn._send_query(RRType.AXFR()) + tsig_axfr_data = strip_mutable_tsig_data(self.conn.query_data) + self.assertEqual(tsig_axfr_data, b'\x00n\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x07example\x03com\x00\x00\xfc\x00\x01\x07example\x03com\x00\x00\xfa\x00\xff\x00\x00\x00\x00\x00:\x08hmac-md5\x07sig-alg\x03reg\x03int\x00\x01,\x00\x10\x105\x00\x00\x00\x00') + def test_response_with_invalid_msg(self): self.conn.reply_data = b'aaaxxxx' self.assertRaises(XfrinTestException, self._handle_xfrin_response) + def test_response_with_tsig(self): + self.conn._tsig_ctx = TSIGContext(TSIG_KEY) + # server tsig check fail, return with RCODE 9 (NOTAUTH) + self.conn._send_query(RRType.SOA()) + self.conn.reply_data = self.conn.create_response_data(rcode=Rcode.NOTAUTH()) + self.assertRaises(XfrinException, self._handle_xfrin_response) + def test_response_without_end_soa(self): self.conn._send_query(RRType.AXFR()) self.conn.reply_data = self.conn.create_response_data() @@ -399,15 +445,20 @@ class TestXfrinRecorder(unittest.TestCase): class TestXfrin(unittest.TestCase): def setUp(self): + # redirect output + self.stderr_backup = sys.stderr + sys.stderr = open(os.devnull, 'w') self.xfr = MockXfrin() self.args = {} self.args['zone_name'] = TEST_ZONE_NAME self.args['port'] = TEST_MASTER_PORT self.args['master'] = TEST_MASTER_IPV4_ADDRESS self.args['db_file'] = TEST_DB_FILE + self.args['tsig_key'] = '' def tearDown(self): self.xfr.shutdown() + sys.stderr= self.stderr_backup def _do_parse_zone_name_class(self): return self.xfr._parse_zone_name_and_class(self.args) diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index 10a866e8d7..1bf46c1b5c 100755 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -67,15 +67,16 @@ DEFAULT_MASTER = '127.0.0.1' def log_error(msg): sys.stderr.write("[b10-xfrin] %s\n" % str(msg)) -class XfrinException(Exception): +class XfrinException(Exception): pass class XfrinConnection(asyncore.dispatcher): - '''Do xfrin in this class. ''' + '''Do xfrin in this class. ''' def __init__(self, sock_map, zone_name, rrclass, db_file, shutdown_event, - master_addrinfo, verbose = False, idle_timeout = 60): + master_addrinfo, tsig_key_str = None, verbose = False, + idle_timeout = 60): ''' idle_timeout: max idle time for read data from socket. db_file: specify the data source file. check_soa: when it's true, check soa first before sending xfr query @@ -93,6 +94,9 @@ class XfrinConnection(asyncore.dispatcher): self._shutdown_event = shutdown_event self._verbose = verbose self._master_address = master_addrinfo[2] + self._tsig_ctx = None + if tsig_key_str is not None: + self._tsig_ctx = TSIGContext(TSIGKey(tsig_key_str)) def connect_to_master(self): '''Connect to master in TCP.''' @@ -130,9 +134,14 @@ class XfrinConnection(asyncore.dispatcher): msg = self._create_query(query_type) render = MessageRenderer() - msg.to_wire(render) - header_len = struct.pack('H', socket.htons(render.get_length())) + # XXX Currently, python wrapper doesn't accept 'None' parameter in this case, + # we should remove the if statement and use a universal interface later. + if self._tsig_ctx is not None: + msg.to_wire(render, self._tsig_ctx) + else: + msg.to_wire(render) + header_len = struct.pack('H', socket.htons(render.get_length())) self._send_data(header_len) self._send_data(render.get_data()) @@ -142,7 +151,7 @@ class XfrinConnection(asyncore.dispatcher): _get_request_response so that we can test the rest of the code without involving actual communication with a remote server.''' asyncore.loop(self._idle_timeout, map=self._sock_map, count=1) - + def _get_request_response(self, size): recv_size = 0 data = b'' @@ -176,7 +185,7 @@ class XfrinConnection(asyncore.dispatcher): # strict we should be (see the comment in _check_response_header()) self._check_response_header(msg) - # TODO, need select soa record from data source then compare the two + # TODO, need select soa record from data source then compare the two # serial, current just return OK, since this function hasn't been used # now. return XFRIN_OK @@ -290,14 +299,14 @@ class XfrinConnection(asyncore.dispatcher): msg = Message(Message.PARSE) msg.from_wire(recvdata) self._check_response_status(msg) - + answer_section = msg.get_section(Message.SECTION_ANSWER) for rr in self._handle_answer_section(answer_section): yield rr if self._soa_rr_count == 2: break - + if self._shutdown_event.is_set(): raise XfrinException('xfrin is forced to stop') @@ -322,16 +331,18 @@ class XfrinConnection(asyncore.dispatcher): sys.stdout.write('[b10-xfrin] %s\n' % str(msg)) -def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file, - shutdown_event, master_addrinfo, check_soa, verbose): +def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file, + shutdown_event, master_addrinfo, check_soa, verbose, + tsig_key_str): xfrin_recorder.increment(zone_name) sock_map = {} conn = XfrinConnection(sock_map, zone_name, rrclass, db_file, - shutdown_event, master_addrinfo, verbose) + shutdown_event, master_addrinfo, + tsig_key_str, verbose) ret = XFRIN_FAIL if conn.connect_to_master(): ret = conn.do_xfrin(check_soa) - + # Publish the zone transfer result news, so zonemgr can reset the # zone timer, and xfrout can notify the zone's slaves if the result # is success. @@ -379,11 +390,11 @@ class Xfrin: self._verbose = verbose def _cc_setup(self): - '''This method is used only as part of initialization, but is - implemented separately for convenience of unit tests; by letting - the test code override this method we can test most of this class + '''This method is used only as part of initialization, but is + implemented separately for convenience of unit tests; by letting + the test code override this method we can test most of this class without requiring a command channel.''' - # Create one session for sending command to other modules, because the + # Create one session for sending command to other modules, because the # listening session will block the send operation. self._send_cc_session = isc.cc.Session() self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, @@ -394,15 +405,17 @@ class Xfrin: self._max_transfers_in = config_data.get("transfers_in") self._master_addr = config_data.get('master_addr') or self._master_addr self._master_port = config_data.get('master_port') or self._master_port + self._tsig_key_str = config_data.get('tsig_key') or None def _cc_check_command(self): - '''This is a straightforward wrapper for cc.check_command, - but provided as a separate method for the convenience + '''This is a straightforward wrapper for cc.check_command, + but provided as a separate method for the convenience of unit tests.''' self._module_cc.check_command(False) def config_handler(self, new_config): self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in + self._tsig_key_str = new_config.get('tsig_key') or None if ('master_addr' in new_config) or ('master_port' in new_config): # User should change the port and address together. try: @@ -420,7 +433,7 @@ class Xfrin: return create_answer(0) def shutdown(self): - ''' shutdown the xfrin process. the thread which is doing xfrin should be + ''' shutdown the xfrin process. the thread which is doing xfrin should be terminated. ''' self._shutdown_event.set() @@ -436,30 +449,32 @@ class Xfrin: if command == 'shutdown': self._shutdown_event.set() elif command == 'notify' or command == REFRESH_FROM_ZONEMGR: - # Xfrin receives the refresh/notify command from zone manager. - # notify command maybe has the parameters which + # Xfrin receives the refresh/notify command from zone manager. + # notify command maybe has the parameters which # specify the notifyfrom address and port, according the RFC1996, zone # transfer should starts first from the notifyfrom, but now, let 'TODO' it. (zone_name, rrclass) = self._parse_zone_name_and_class(args) (master_addr) = build_addr_info(self._master_addr, self._master_port) - ret = self.xfrin_start(zone_name, - rrclass, + ret = self.xfrin_start(zone_name, + rrclass, self._get_db_file(), master_addr, + self._tsig_key_str, True) answer = create_answer(ret[0], ret[1]) elif command == 'retransfer' or command == 'refresh': # Xfrin receives the retransfer/refresh from cmdctl(sent by bindctl). - # If the command has specified master address, do transfer from the - # master address, or else do transfer from the configured masters. + # If the command has specified master address, do transfer from the + # master address, or else do transfer from the configured masters. (zone_name, rrclass) = self._parse_zone_name_and_class(args) master_addr = self._parse_master_and_port(args) db_file = args.get('db_file') or self._get_db_file() - ret = self.xfrin_start(zone_name, - rrclass, - db_file, + ret = self.xfrin_start(zone_name, + rrclass, + db_file, master_addr, + self._tsig_key_str, (False if command == 'retransfer' else True)) answer = create_answer(ret[0], ret[1]) @@ -483,14 +498,14 @@ class Xfrin: rrclass = RRClass(rrclass) except InvalidRRClass as e: raise XfrinException('invalid RRClass: ' + rrclass) - + return zone_name, rrclass def _parse_master_and_port(self, args): port = args.get('port') or self._master_port master = args.get('master') or self._master_addr return build_addr_info(master, port) - + def _get_db_file(self): #TODO, the db file path should be got in auth server's configuration # if we need access to this configuration more often, we @@ -506,12 +521,12 @@ class Xfrin: db_file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3" self._module_cc.remove_remote_config(AUTH_SPECFILE_LOCATION) return db_file - + def publish_xfrin_news(self, zone_name, zone_class, xfr_result): '''Send command to xfrout/zone manager module. - If xfrin has finished successfully for one zone, tell the good + If xfrin has finished successfully for one zone, tell the good news(command: zone_new_data_ready) to zone manager and xfrout. - if xfrin failed, just tell the bad news to zone manager, so that + if xfrin failed, just tell the bad news to zone manager, so that it can reset the refresh timer for that zone. ''' param = {'zone_name': zone_name, 'zone_class': zone_class.to_text()} if xfr_result == XFRIN_OK: @@ -531,8 +546,8 @@ class Xfrin: seq) except isc.cc.session.SessionTimeout: pass # for now we just ignore the failure - except socket.error as err: - log_error("Fail to send message to %s and %s, msgq may has been killed" + except socket.error as err: + log_error("Fail to send message to %s and %s, msgq may has been killed" % (XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME)) else: msg = create_command(ZONE_XFRIN_FAILED, param) @@ -545,14 +560,14 @@ class Xfrin: except isc.cc.session.SessionTimeout: pass # for now we just ignore the failure except socket.error as err: - log_error("Fail to send message to %s, msgq may has been killed" + log_error("Fail to send message to %s, msgq may has been killed" % ZONE_MANAGER_MODULE_NAME) def startup(self): while not self._shutdown_event.is_set(): self._cc_check_command() - def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo, + def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo, tsig_key_str, check_soa = True): if "pydnspp" not in sys.modules: return (1, "xfrin failed, can't load dns message python library: 'pydnspp'") @@ -571,7 +586,8 @@ class Xfrin: db_file, self._shutdown_event, master_addrinfo, check_soa, - self._verbose)) + self._verbose, + tsig_key_str)) xfrin_thread.start() return (0, 'zone xfrin is started') diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec index 61ddaadb22..46bad69f48 100644 --- a/src/bin/xfrin/xfrin.spec +++ b/src/bin/xfrin/xfrin.spec @@ -19,6 +19,11 @@ "item_type": "integer", "item_optional": false, "item_default": 53 + }, + { "item_name": "tsig_key", + "item_type": "string", + "item_optional": true, + "item_default": "" } ], "commands": [ diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am index 1ec3c064d9..11916afbfe 100644 --- a/src/bin/xfrout/tests/Makefile.am +++ b/src/bin/xfrout/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py index c6d151db17..479ca687ff 100644 --- a/src/bin/zonemgr/tests/zonemgr_test.py +++ b/src/bin/zonemgr/tests/zonemgr_test.py @@ -21,11 +21,12 @@ import os import tempfile from zonemgr import * -ZONE_NAME_CLASS1_IN = ("sd.cn.", "IN") -ZONE_NAME_CLASS2_CH = ("tw.cn.", "CH") -ZONE_NAME_CLASS3_IN = ("example.com", "IN") -ZONE_NAME_CLASS1_CH = ("sd.cn.", "CH") -ZONE_NAME_CLASS2_IN = ("tw.cn.", "IN") +ZONE_NAME_CLASS1_IN = ("example.net.", "IN") +ZONE_NAME_CLASS1_CH = ("example.net.", "CH") +ZONE_NAME_CLASS2_IN = ("example.org.", "IN") +ZONE_NAME_CLASS2_CH = ("example.org.", "CH") +ZONE_NAME_CLASS3_IN = ("example.com.", "IN") +ZONE_NAME_CLASS3_CH = ("example.com.", "CH") MAX_TRANSFER_TIMEOUT = 14400 LOWERBOUND_REFRESH = 10 @@ -80,12 +81,12 @@ class MyZonemgrRefresh(ZonemgrRefresh): self._refresh_jitter = 0.25 def get_zone_soa(zone_name, db_file): - if zone_name == 'sd.cn.': - return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None, - 'a.dns.cn. root.cnnic.cn. 2009073106 7200 3600 2419200 21600') - elif zone_name == 'tw.cn.': - return (1, 2, 'tw.cn.', 'cn.sd.', 21600, 'SOA', None, - 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600') + if zone_name == 'example.net.': + return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None, + 'a.example.net. root.example.net. 2009073106 7200 3600 2419200 21600') + elif zone_name == 'example.org.': + return (1, 2, 'example.org.', 'example.org.sd.', 21600, 'SOA', None, + 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600') else: return None sqlite3_ds.get_zone_soa = get_zone_soa @@ -94,15 +95,15 @@ class MyZonemgrRefresh(ZonemgrRefresh): self._slave_socket, FakeConfig()) current_time = time.time() self._zonemgr_refresh_info = { - ('sd.cn.', 'IN'): { + ('example.net.', 'IN'): { 'last_refresh_time': current_time, 'next_refresh_time': current_time + 6500, - 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600', + 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600', 'zone_state': 0}, - ('tw.cn.', 'CH'): { + ('example.org.', 'CH'): { 'last_refresh_time': current_time, 'next_refresh_time': current_time + 6900, - 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600', + 'zone_soa_rdata': 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600', 'zone_state': 0} } @@ -157,6 +158,7 @@ class TestZonemgrRefresh(unittest.TestCase): self.assertFalse(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS2_CH)) self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS2_IN)) self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS3_IN)) + self.assertTrue(self.zone_refresh._zone_not_exist(ZONE_NAME_CLASS3_CH)) def test_set_zone_notify_timer(self): time1 = time.time() @@ -179,20 +181,20 @@ class TestZonemgrRefresh(unittest.TestCase): self.assertTrue(self.zone_refresh._zone_is_expired(ZONE_NAME_CLASS1_IN)) def test_get_zone_soa_rdata(self): - soa_rdata1 = 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600' - soa_rdata2 = 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600' + soa_rdata1 = 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600' + soa_rdata2 = 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600' self.assertEqual(soa_rdata1, self.zone_refresh._get_zone_soa_rdata(ZONE_NAME_CLASS1_IN)) self.assertRaises(KeyError, self.zone_refresh._get_zone_soa_rdata, ZONE_NAME_CLASS1_CH) self.assertEqual(soa_rdata2, self.zone_refresh._get_zone_soa_rdata(ZONE_NAME_CLASS2_CH)) self.assertRaises(KeyError, self.zone_refresh._get_zone_soa_rdata, ZONE_NAME_CLASS2_IN) def test_zonemgr_reload_zone(self): - soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600' + soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600' # We need to restore this not to harm other tests old_get_zone_soa = sqlite3_ds.get_zone_soa def get_zone_soa(zone_name, db_file): - return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None, - 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600') + return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None, + 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600') sqlite3_ds.get_zone_soa = get_zone_soa self.zone_refresh.zonemgr_reload_zone(ZONE_NAME_CLASS1_IN) @@ -274,15 +276,15 @@ class TestZonemgrRefresh(unittest.TestCase): self.assertTrue(self.zone_refresh._zone_mgr_is_empty()) def test_zonemgr_add_zone(self): - soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600' + soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600' # This needs to be restored. The following test actually failed if we left # this unclean old_get_zone_soa = sqlite3_ds.get_zone_soa time1 = time.time() def get_zone_soa(zone_name, db_file): - return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None, - 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600') + return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None, + 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600') sqlite3_ds.get_zone_soa = get_zone_soa @@ -314,15 +316,15 @@ class TestZonemgrRefresh(unittest.TestCase): current_time = time.time() self.assertTrue(zone_timeout <= current_time) self.assertRaises(ZonemgrException, self.zone_refresh.zone_handle_notify,\ - ("org.cn.", "IN"), "127.0.0.1") + ZONE_NAME_CLASS3_CH, "127.0.0.1") self.assertRaises(ZonemgrException, self.zone_refresh.zone_handle_notify,\ ZONE_NAME_CLASS3_IN, "127.0.0.1") def test_zone_refresh_success(self): - soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600' + soa_rdata = 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600' def get_zone_soa(zone_name, db_file): - return (1, 2, 'sd.cn.', 'cn.sd.', 21600, 'SOA', None, - 'a.dns.cn. root.cnnic.cn. 2009073106 1800 900 2419200 21600') + return (1, 2, 'example.net.', 'example.net.sd.', 21600, 'SOA', None, + 'a.example.net. root.example.net. 2009073106 1800 900 2419200 21600') sqlite3_ds.get_zone_soa = get_zone_soa time1 = time.time() self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING @@ -337,11 +339,11 @@ class TestZonemgrRefresh(unittest.TestCase): last_refresh_time = self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["last_refresh_time"] self.assertTrue(time1 <= last_refresh_time) self.assertTrue(last_refresh_time <= time2) - self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ("org.cn.", "CH")) + self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ("example.test.", "CH")) self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_success, ZONE_NAME_CLASS3_IN) def test_zone_refresh_fail(self): - soa_rdata = 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600' + soa_rdata = 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600' time1 = time.time() self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"] = ZONE_REFRESHING self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN) @@ -357,22 +359,22 @@ class TestZonemgrRefresh(unittest.TestCase): self.zone_refresh.zone_refresh_fail(ZONE_NAME_CLASS1_IN) self.assertEqual(ZONE_EXPIRED, self.zone_refresh._zonemgr_refresh_info[ZONE_NAME_CLASS1_IN]["zone_state"]) - self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ("org.cn.", "CH")) + self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_CH) self.assertRaises(ZonemgrException, self.zone_refresh.zone_refresh_fail, ZONE_NAME_CLASS3_IN) def test_find_need_do_refresh_zone(self): time1 = time.time() self.zone_refresh._zonemgr_refresh_info = { - ("sd.cn.","IN"):{ + ("example.net.","IN"):{ 'last_refresh_time': time1, 'next_refresh_time': time1 + 7200, - 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600', + 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600', 'zone_state': ZONE_OK}, - ("tw.cn.","CH"):{ + ("example.org.","CH"):{ 'last_refresh_time': time1 - 7200, 'next_refresh_time': time1, 'refresh_timeout': time1 + MAX_TRANSFER_TIMEOUT, - 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073112 7200 3600 2419200 21600', + 'zone_soa_rdata': 'a.example.org. root.example.org. 2009073112 7200 3600 2419200 21600', 'zone_state': ZONE_REFRESHING} } zone_need_refresh = self.zone_refresh._find_need_do_refresh_zone() @@ -385,10 +387,10 @@ class TestZonemgrRefresh(unittest.TestCase): def test_do_refresh(self): time1 = time.time() self.zone_refresh._zonemgr_refresh_info = { - ("sd.cn.", "IN"):{ + ("example.net.", "IN"):{ 'last_refresh_time': time1 - 7200, 'next_refresh_time': time1 - 1, - 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600', + 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600', 'zone_state': ZONE_OK} } self.zone_refresh._do_refresh(ZONE_NAME_CLASS1_IN) @@ -416,10 +418,10 @@ class TestZonemgrRefresh(unittest.TestCase): """ time1 = time.time() self.zone_refresh._zonemgr_refresh_info = { - ("sd.cn.", "IN"):{ + ("example.net.", "IN"):{ 'last_refresh_time': time1 - 7200, 'next_refresh_time': time1 - 1, - 'zone_soa_rdata': 'a.dns.cn. root.cnnic.cn. 2009073105 7200 3600 2419200 21600', + 'zone_soa_rdata': 'a.example.net. root.example.net. 2009073105 7200 3600 2419200 21600', 'zone_state': ZONE_OK} } self.zone_refresh._check_sock = self.zone_refresh._master_socket @@ -465,19 +467,19 @@ class TestZonemgrRefresh(unittest.TestCase): # Put something in config.set_zone_list_from_name_classes([ZONE_NAME_CLASS1_IN]) self.zone_refresh.update_config_data(config) - self.assertTrue(("sd.cn.", "IN") in + self.assertTrue(("example.net.", "IN") in self.zone_refresh._zonemgr_refresh_info) # This one does not exist config.set_zone_list_from_name_classes(["example.net", "CH"]) self.assertRaises(ZonemgrException, self.zone_refresh.update_config_data, config) # So it should not affect the old ones - self.assertTrue(("sd.cn.", "IN") in + self.assertTrue(("example.net.", "IN") in self.zone_refresh._zonemgr_refresh_info) # Make sure it works even when we "accidentally" forget the final dot - config.set_zone_list_from_name_classes([("sd.cn", "IN")]) + config.set_zone_list_from_name_classes([("example.net", "IN")]) self.zone_refresh.update_config_data(config) - self.assertTrue(("sd.cn.", "IN") in + self.assertTrue(("example.net.", "IN") in self.zone_refresh._zonemgr_refresh_info) def tearDown(self): @@ -532,7 +534,7 @@ class TestZonemgr(unittest.TestCase): self.assertEqual(self.zonemgr.config_handler(config_data1), {"result": [0]}) self.assertEqual(config_data1, self.zonemgr._config_data) - config_data2 = {"zone_name" : "sd.cn.", "port" : "53", "master" : "192.168.1.1"} + config_data2 = {"zone_name" : "example.net.", "port" : "53", "master" : "192.168.1.1"} self.zonemgr.config_handler(config_data2) self.assertEqual(config_data1, self.zonemgr._config_data) # jitter should not be bigger than half of the original value @@ -553,11 +555,11 @@ class TestZonemgr(unittest.TestCase): self.assertEqual("initdb.file", self.zonemgr.get_db_file()) def test_parse_cmd_params(self): - params1 = {"zone_name" : "org.cn", "zone_class" : "CH", "master" : "127.0.0.1"} - answer1 = (("org.cn", "CH"), "127.0.0.1") + params1 = {"zone_name" : "example.com.", "zone_class" : "CH", "master" : "127.0.0.1"} + answer1 = (ZONE_NAME_CLASS3_CH, "127.0.0.1") self.assertEqual(answer1, self.zonemgr._parse_cmd_params(params1, ZONE_NOTIFY_COMMAND)) - params2 = {"zone_name" : "org.cn", "zone_class" : "CH"} - answer2 = ("org.cn", "CH") + params2 = {"zone_name" : "example.com.", "zone_class" : "IN"} + answer2 = ZONE_NAME_CLASS3_IN self.assertEqual(answer2, self.zonemgr._parse_cmd_params(params2, ZONE_XFRIN_SUCCESS_COMMAND)) self.assertRaises(ZonemgrException, self.zonemgr._parse_cmd_params, params2, ZONE_NOTIFY_COMMAND) params1 = {"zone_class" : "CH"} diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst index e9c4beb23a..4659dc4971 100644 --- a/src/cppcheck-suppress.lst +++ b/src/cppcheck-suppress.lst @@ -12,4 +12,4 @@ functionConst:src/lib/cache/rrset_cache.h // Intentional self assignment tests. Suppress warning about them. selfAssignment:src/lib/dns/tests/name_unittest.cc:293 selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228 -selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:104 +selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:125 diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 81455c454e..81ddd18f21 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,2 +1,3 @@ -SUBDIRS = exceptions util dns cc config python xfr bench log asiolink \ - asiodns nsas cache resolve testutils datasrc server_common +SUBDIRS = exceptions util log cryptolink dns cc config python xfr \ + bench asiolink asiodns nsas cache resolve testutils datasrc \ + server_common diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am index 4bcdde6125..2a6c3ac975 100644 --- a/src/lib/asiodns/Makefile.am +++ b/src/lib/asiodns/Makefile.am @@ -8,12 +8,17 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util AM_CXXFLAGS = $(B10_CXXFLAGS) -CLEANFILES = *.gcno *.gcda +CLEANFILES = *.gcno *.gcda asiodef.h asiodef.cc + +# Define rule to build logging source files from message file +asiodef.h asiodef.cc: asiodef.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/asiodns/asiodef.mes + +BUILT_SOURCES = asiodef.h asiodef.cc lib_LTLIBRARIES = libasiodns.la libasiodns_la_SOURCES = dns_answer.h libasiodns_la_SOURCES += asiodns.h -libasiodns_la_SOURCES += asiodef.cc asiodef.h libasiodns_la_SOURCES += dns_lookup.h libasiodns_la_SOURCES += dns_server.h libasiodns_la_SOURCES += dns_service.cc dns_service.h @@ -21,7 +26,9 @@ libasiodns_la_SOURCES += tcp_server.cc tcp_server.h libasiodns_la_SOURCES += udp_server.cc udp_server.h libasiodns_la_SOURCES += io_fetch.cc io_fetch.h -EXTRA_DIST = asiodef.msg +nodist_libasiodns_la_SOURCES = asiodef.cc asiodef.h + +EXTRA_DIST = asiodef.mes # Note: the ordering matters: -Wno-... must follow -Wextra (defined in # B10_CXXFLAGS) diff --git a/src/lib/asiodns/asiodef.cc b/src/lib/asiodns/asiodef.cc deleted file mode 100644 index 127e848767..0000000000 --- a/src/lib/asiodns/asiodef.cc +++ /dev/null @@ -1,39 +0,0 @@ -// File created from asiodef.msg on Mon Feb 28 17:15:30 2011 - -#include -#include -#include - -namespace isc { -namespace asiodns { - -extern const isc::log::MessageID ASIODNS_FETCHCOMP = "FETCHCOMP"; -extern const isc::log::MessageID ASIODNS_FETCHSTOP = "FETCHSTOP"; -extern const isc::log::MessageID ASIODNS_OPENSOCK = "OPENSOCK"; -extern const isc::log::MessageID ASIODNS_RECVSOCK = "RECVSOCK"; -extern const isc::log::MessageID ASIODNS_RECVTMO = "RECVTMO"; -extern const isc::log::MessageID ASIODNS_SENDSOCK = "SENDSOCK"; -extern const isc::log::MessageID ASIODNS_UNKORIGIN = "UNKORIGIN"; -extern const isc::log::MessageID ASIODNS_UNKRESULT = "UNKRESULT"; - -} // namespace asiodns -} // namespace isc - -namespace { - -const char* values[] = { - "FETCHCOMP", "upstream fetch to %s(%d) has now completed", - "FETCHSTOP", "upstream fetch to %s(%d) has been stopped", - "OPENSOCK", "error %d opening %s socket to %s(%d)", - "RECVSOCK", "error %d reading %s data from %s(%d)", - "RECVTMO", "receive timeout while waiting for data from %s(%d)", - "SENDSOCK", "error %d sending data using %s to %s(%d)", - "UNKORIGIN", "unknown origin for ASIO error code %d (protocol: %s, address %s)", - "UNKRESULT", "unknown result (%d) when IOFetch::stop() was executed for I/O to %s(%d)", - NULL -}; - -const isc::log::MessageInitializer initializer(values); - -} // Anonymous namespace - diff --git a/src/lib/asiodns/asiodef.h b/src/lib/asiodns/asiodef.h deleted file mode 100644 index 50aa8a99f3..0000000000 --- a/src/lib/asiodns/asiodef.h +++ /dev/null @@ -1,23 +0,0 @@ -// File created from asiodef.msg on Mon Feb 28 17:15:30 2011 - -#ifndef __ASIODEF_H -#define __ASIODEF_H - -#include - -namespace isc { -namespace asiodns { - -extern const isc::log::MessageID ASIODNS_FETCHCOMP; -extern const isc::log::MessageID ASIODNS_FETCHSTOP; -extern const isc::log::MessageID ASIODNS_OPENSOCK; -extern const isc::log::MessageID ASIODNS_RECVSOCK; -extern const isc::log::MessageID ASIODNS_RECVTMO; -extern const isc::log::MessageID ASIODNS_SENDSOCK; -extern const isc::log::MessageID ASIODNS_UNKORIGIN; -extern const isc::log::MessageID ASIODNS_UNKRESULT; - -} // namespace asiodns -} // namespace isc - -#endif // __ASIODEF_H diff --git a/src/lib/asiodns/asiodef.mes b/src/lib/asiodns/asiodef.mes new file mode 100644 index 0000000000..3f2e80cfa5 --- /dev/null +++ b/src/lib/asiodns/asiodef.mes @@ -0,0 +1,56 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +$PREFIX ASIODNS_ +$NAMESPACE isc::asiodns + +% FETCHCOMP upstream fetch to %1(%2) has now completed +A debug message, this records the the upstream fetch (a query made by the +resolver on behalf of its client) to the specified address has completed. + +% FETCHSTOP upstream fetch to %1(%2) has been stopped +An external component has requested the halting of an upstream fetch. This +is an allowed operation, and the message should only appear if debug is +enabled. + +% OPENSOCK error %1 opening %2 socket to %3(%4) +The asynchronous I/O code encountered an error when trying to open a socket +of the specified protocol in order to send a message to the target address. +The the number of the system error that cause the problem is given in the +message. + +% RECVSOCK error %1 reading %2 data from %3(%4) +The asynchronous I/O code encountered an error when trying read data from +the specified address on the given protocol. The the number of the system +error that cause the problem is given in the message. + +% SENDSOCK error %1 sending data using %2 to %3(%4) +The asynchronous I/O code encountered an error when trying send data to +the specified address on the given protocol. The the number of the system +error that cause the problem is given in the message. + +% RECVTMO receive timeout while waiting for data from %1(%2) +An upstream fetch from the specified address timed out. This may happen for +any number of reasons and is most probably a problem at the remote server +or a problem on the network. The message will only appear if debug is +enabled. + +% UNKORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3) +This message should not appear and indicates an internal error if it does. +Please enter a bug report. + +% UNKRESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3) +The termination method of the resolver's upstream fetch class was called with +an unknown result code (which is given in the message). This message should +not appear and may indicate an internal error. Please enter a bug report. diff --git a/src/lib/asiodns/asiodef.msg b/src/lib/asiodns/asiodef.msg deleted file mode 100644 index 7f86acb7ee..0000000000 --- a/src/lib/asiodns/asiodef.msg +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. - -$PREFIX ASIODNS_ -$NAMESPACE isc::asiodns - -FETCHCOMP upstream fetch to %s(%d) has now completed -+ A debug message, this records the the upstream fetch (a query made by the -+ resolver on behalf of its client) to the specified address has completed. - -FETCHSTOP upstream fetch to %s(%d) has been stopped -+ An external component has requested the halting of an upstream fetch. This -+ is an allowed operation, and the message should only appear if debug is -+ enabled. - -OPENSOCK error %d opening %s socket to %s(%d) -+ The asynchronous I/O code encountered an error when trying to open a socket -+ of the specified protocol in order to send a message to the target address. -+ The the number of the system error that cause the problem is given in the -+ message. - -RECVSOCK error %d reading %s data from %s(%d) -+ The asynchronous I/O code encountered an error when trying read data from -+ the specified address on the given protocol. The the number of the system -+ error that cause the problem is given in the message. - -SENDSOCK error %d sending data using %s to %s(%d) -+ The asynchronous I/O code encountered an error when trying send data to -+ the specified address on the given protocol. The the number of the system -+ error that cause the problem is given in the message. - -RECVTMO receive timeout while waiting for data from %s(%d) -+ An upstream fetch from the specified address timed out. This may happen for -+ any number of reasons and is most probably a problem at the remote server -+ or a problem on the network. The message will only appear if debug is -+ enabled. - -UNKORIGIN unknown origin for ASIO error code %d (protocol: %s, address %s) -+ This message should not appear and indicates an internal error if it does. -+ Please enter a bug report. - -UNKRESULT unknown result (%d) when IOFetch::stop() was executed for I/O to %s(%d) -+ The termination method of the resolver's upstream fetch class was called with -+ an unknown result code (which is given in the message). This message should -+ not appear and may indicate an internal error. Please enter a bug report. diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc index 8f57d8e127..e535381223 100644 --- a/src/lib/asiodns/io_fetch.cc +++ b/src/lib/asiodns/io_fetch.cc @@ -35,11 +35,11 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -61,7 +61,17 @@ namespace asiodns { /// Use the ASIO logger +namespace { + isc::log::Logger logger("asiolink"); +// Log debug verbosity +enum { + DBG_IMPORTANT = 1, + DBG_COMMON = 20, + DBG_ALL = 50 +}; + +} /// \brief IOFetch Data /// @@ -80,7 +90,6 @@ struct IOFetchData { ///< Socket to use for I/O boost::scoped_ptr remote_snd;///< Where the fetch is sent boost::scoped_ptr remote_rcv;///< Where the response came from - isc::dns::Question question; ///< Question to be asked OutputBufferPtr msgbuf; ///< Wire buffer for question OutputBufferPtr received; ///< Received data put here IOFetch::Callback* callback; ///< Called on I/O Completion @@ -110,7 +119,6 @@ struct IOFetchData { /// \param proto Either IOFetch::TCP or IOFetch::UDP. /// \param service I/O Service object to handle the asynchronous /// operations. - /// \param query DNS question to send to the upstream server. /// \param address IP address of upstream server /// \param port Port to use for the query /// \param buff Output buffer into which the response (in wire format) @@ -122,8 +130,8 @@ struct IOFetchData { /// /// TODO: May need to alter constructor (see comment 4 in Trac ticket #554) IOFetchData(IOFetch::Protocol proto, IOService& service, - const isc::dns::Question& query, const IOAddress& address, - uint16_t port, OutputBufferPtr& buff, IOFetch::Callback* cb, int wait) + const IOAddress& address, uint16_t port, OutputBufferPtr& buff, + IOFetch::Callback* cb, int wait) : socket((proto == IOFetch::UDP) ? static_cast*>( @@ -139,7 +147,6 @@ struct IOFetchData { static_cast(new UDPEndpoint(address, port)) : static_cast(new TCPEndpoint(address, port)) ), - question(query), msgbuf(new OutputBuffer(512)), received(buff), callback(cb), @@ -174,10 +181,10 @@ struct IOFetchData { IOFetch::IOFetch(Protocol protocol, IOService& service, const isc::dns::Question& question, const IOAddress& address, uint16_t port, OutputBufferPtr& buff, Callback* cb, int wait) - : - data_(new IOFetchData(protocol, service, question, address, - port, buff, cb, wait)) { + MessagePtr query_msg(new Message(Message::RENDER)); + initIOFetch(query_msg, protocol, service, question, address, port, buff, + cb, wait); } IOFetch::IOFetch(Protocol protocol, IOService& service, @@ -185,14 +192,56 @@ IOFetch::IOFetch(Protocol protocol, IOService& service, OutputBufferPtr& buff, Callback* cb, int wait) : data_(new IOFetchData(protocol, service, - isc::dns::Question(isc::dns::Name("dummy.example.org"), - isc::dns::RRClass::IN(), isc::dns::RRType::A()), address, port, buff, cb, wait)) { data_->msgbuf = outpkt; data_->packet = true; } +IOFetch::IOFetch(Protocol protocol, IOService& service, + ConstMessagePtr query_message, const IOAddress& address, uint16_t port, + OutputBufferPtr& buff, Callback* cb, int wait) +{ + MessagePtr msg(new Message(Message::RENDER)); + + msg->setHeaderFlag(Message::HEADERFLAG_RD, + query_message->getHeaderFlag(Message::HEADERFLAG_RD)); + msg->setHeaderFlag(Message::HEADERFLAG_CD, + query_message->getHeaderFlag(Message::HEADERFLAG_CD)); + + ConstEDNSPtr edns(query_message->getEDNS()); + const bool dnssec_ok = edns && edns->getDNSSECAwareness(); + if (edns) { + EDNSPtr edns_response(new EDNS()); + edns_response->setDNSSECAwareness(dnssec_ok); + // TODO: We should make our own edns bufsize length configurable + edns_response->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE); + msg->setEDNS(edns_response); + } + + initIOFetch(msg, protocol, service, + **(query_message->beginQuestion()), + address, port, buff, cb, wait); +} + +void +IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& service, + const isc::dns::Question& question, + const IOAddress& address, uint16_t port, + OutputBufferPtr& buff, Callback* cb, int wait) +{ + data_ = boost::shared_ptr(new IOFetchData( + protocol, service, address, port, buff, cb, wait)); + + query_msg->setQid(data_->qid); + query_msg->setOpcode(Opcode::QUERY()); + query_msg->setRcode(Rcode::NOERROR()); + query_msg->setHeaderFlag(Message::HEADERFLAG_RD); + query_msg->addQuestion(question); + MessageRenderer renderer(*data_->msgbuf); + query_msg->toWire(renderer); +} + // Return protocol in use. IOFetch::Protocol @@ -224,17 +273,7 @@ IOFetch::operator()(asio::error_code ec, size_t length) { // first two bytes of the packet). data_->msgbuf->writeUint16At(data_->qid, 0); - } else { - // A question was given, construct the packet - Message msg(Message::RENDER); - msg.setQid(data_->qid); - msg.setOpcode(Opcode::QUERY()); - msg.setRcode(Rcode::NOERROR()); - msg.setHeaderFlag(Message::HEADERFLAG_RD); - msg.addQuestion(data_->question); - MessageRenderer renderer(*data_->msgbuf); - msg.toWire(renderer); - } + } } // If we timeout, we stop, which will can cancel outstanding I/Os and @@ -331,42 +370,34 @@ IOFetch::stop(Result result) { // numbers indicating the most important information. The relative // values are somewhat arbitrary. // - // Although Logger::debug checks the debug flag internally, doing it - // below before calling Logger::debug avoids the overhead of a string - // conversion in the common case when debug is not enabled. - // // TODO: Update testing of stopped_ if threads are used. data_->stopped = true; switch (result) { case TIME_OUT: - if (logger.isDebugEnabled(1)) { - logger.debug(20, ASIODNS_RECVTMO, - data_->remote_snd->getAddress().toText().c_str(), - static_cast(data_->remote_snd->getPort())); - } + LOG_DEBUG(logger, DBG_COMMON, ASIODNS_RECVTMO). + arg(data_->remote_snd->getAddress().toText()). + arg(data_->remote_snd->getPort()); break; case SUCCESS: - if (logger.isDebugEnabled(50)) { - logger.debug(30, ASIODNS_FETCHCOMP, - data_->remote_rcv->getAddress().toText().c_str(), - static_cast(data_->remote_rcv->getPort())); - } + LOG_DEBUG(logger, DBG_ALL, ASIODNS_FETCHCOMP). + arg(data_->remote_rcv->getAddress().toText()). + arg(data_->remote_rcv->getPort()); break; case STOPPED: // Fetch has been stopped for some other reason. This is // allowed but as it is unusual it is logged, but with a lower // debug level than a timeout (which is totally normal). - logger.debug(1, ASIODNS_FETCHSTOP, - data_->remote_snd->getAddress().toText().c_str(), - static_cast(data_->remote_snd->getPort())); + LOG_DEBUG(logger, DBG_IMPORTANT, ASIODNS_FETCHSTOP). + arg(data_->remote_snd->getAddress().toText()). + arg(data_->remote_snd->getPort()); break; default: - logger.error(ASIODNS_UNKRESULT, static_cast(result), - data_->remote_snd->getAddress().toText().c_str(), - static_cast(data_->remote_snd->getPort())); + LOG_ERROR(logger, ASIODNS_UNKRESULT). + arg(data_->remote_snd->getAddress().toText()). + arg(data_->remote_snd->getPort()); } // Stop requested, cancel and I/O's on the socket and shut it down, @@ -394,14 +425,12 @@ void IOFetch::logIOFailure(asio::error_code ec) { (data_->origin == ASIODNS_UNKORIGIN)); static const char* PROTOCOL[2] = {"TCP", "UDP"}; - logger.error(data_->origin, - ec.value(), - ((data_->remote_snd->getProtocol() == IPPROTO_TCP) ? - PROTOCOL[0] : PROTOCOL[1]), - data_->remote_snd->getAddress().toText().c_str(), - static_cast(data_->remote_snd->getPort())); + LOG_ERROR(logger, data_->origin).arg(ec.value()). + arg((data_->remote_snd->getProtocol() == IPPROTO_TCP) ? + PROTOCOL[0] : PROTOCOL[1]). + arg(data_->remote_snd->getAddress().toText()). + arg(data_->remote_snd->getPort()); } } // namespace asiodns } // namespace isc { - diff --git a/src/lib/asiodns/io_fetch.h b/src/lib/asiodns/io_fetch.h index 98c917d548..9626ffe979 100644 --- a/src/lib/asiodns/io_fetch.h +++ b/src/lib/asiodns/io_fetch.h @@ -29,6 +29,7 @@ #include #include +#include namespace isc { namespace asiodns { @@ -136,6 +137,20 @@ public: uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb, int wait = -1); + /// \brief Constructor + /// This constructor has one parameter "query_message", which + /// is the shared_ptr to a full query message. It's different + /// with above contructor which has only question section. All + /// other parameters are same. + /// + /// \param query_message the shared_ptr to a full query message + /// got from a query client. + IOFetch(Protocol protocol, isc::asiolink::IOService& service, + isc::dns::ConstMessagePtr query_message, + const isc::asiolink::IOAddress& address, + uint16_t port, isc::util::OutputBufferPtr& buff, Callback* cb, + int wait = -1); + /// \brief Constructor. /// /// Creates the object that will handle the upstream fetch. @@ -184,6 +199,15 @@ public: void stop(Result reason = STOPPED); private: + /// \brief IOFetch Initialization Function. + /// All the parameters are same with the constructor, except + /// parameter "query_message" + /// \param query_message the message to be sent out. + void initIOFetch(isc::dns::MessagePtr& query_message, Protocol protocol, + isc::asiolink::IOService& service, const isc::dns::Question& question, + const isc::asiolink::IOAddress& address, uint16_t port, + isc::util::OutputBufferPtr& buff, Callback* cb, int wait); + /// \brief Log I/O Failure /// /// Records an I/O failure to the log file diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am index 66d5eda3e7..22b3a8e183 100644 --- a/src/lib/asiolink/Makefile.am +++ b/src/lib/asiolink/Makefile.am @@ -2,7 +2,6 @@ SUBDIRS = . tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) -AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns AM_CXXFLAGS = $(B10_CXXFLAGS) diff --git a/src/lib/asiolink/interval_timer.h b/src/lib/asiolink/interval_timer.h index 0831d449f5..8de16cb095 100644 --- a/src/lib/asiolink/interval_timer.h +++ b/src/lib/asiolink/interval_timer.h @@ -22,7 +22,7 @@ namespace isc { namespace asiolink { -struct IntervalTimerImpl; +class IntervalTimerImpl; /// \brief The \c IntervalTimer class is a wrapper for the ASIO /// \c asio::deadline_timer class. diff --git a/src/lib/asiolink/io_service.h b/src/lib/asiolink/io_service.h index 438667c927..75aaee67e8 100644 --- a/src/lib/asiolink/io_service.h +++ b/src/lib/asiolink/io_service.h @@ -22,7 +22,7 @@ namespace asio { namespace isc { namespace asiolink { -struct IOServiceImpl; +class IOServiceImpl; /// \brief The \c IOService class is a wrapper for the ASIO \c io_service /// class. diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am index 37d9ef39e1..c0d7af63a2 100644 --- a/src/lib/asiolink/tests/Makefile.am +++ b/src/lib/asiolink/tests/Makefile.am @@ -1,6 +1,5 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) -AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin AM_CPPFLAGS += -I$(top_builddir)/src/lib/util -I$(top_srcdir)/src/util AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\" @@ -11,14 +10,18 @@ if USE_STATIC_LINK AM_LDFLAGS = -static endif +# Some versions of GCC warn about some versions of Boost regarding +# missing initializer for members in its posix_time. +# https://svn.boost.org/trac/boost/ticket/3477 +# But older GCC compilers don't have the flag. +AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) + CLEANFILES = *.gcno *.gcda TESTS = if HAVE_GTEST TESTS += run_unittests run_unittests_SOURCES = run_unittests.cc -run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h -run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc run_unittests_SOURCES += io_address_unittest.cc run_unittests_SOURCES += io_endpoint_unittest.cc run_unittests_SOURCES += io_socket_unittest.cc @@ -32,7 +35,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDADD = $(GTEST_LDADD) run_unittests_LDADD += $(SQLITE_LIBS) -run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la diff --git a/src/lib/asiolink/tests/interval_timer_unittest.cc b/src/lib/asiolink/tests/interval_timer_unittest.cc index c24e60effa..8e8ef81101 100644 --- a/src/lib/asiolink/tests/interval_timer_unittest.cc +++ b/src/lib/asiolink/tests/interval_timer_unittest.cc @@ -18,7 +18,7 @@ #include #include -#include +#include namespace { // TODO: Consider this margin @@ -166,16 +166,22 @@ TEST_F(IntervalTimerTest, startIntervalTimer) { io_service_.run(); // reaches here after timer expired // delta: difference between elapsed time and 100 milliseconds. + boost::posix_time::time_duration test_runtime = + boost::posix_time::microsec_clock::universal_time() - start; + EXPECT_FALSE(test_runtime.is_negative()) << + "test duration " << test_runtime << + " negative - clock skew?"; boost::posix_time::time_duration delta = - (boost::posix_time::microsec_clock::universal_time() - start) - - boost::posix_time::millisec(100); + test_runtime - boost::posix_time::milliseconds(100); if (delta.is_negative()) { delta.invert_sign(); } // expect TimerCallBack is called; timer_called_ is true EXPECT_TRUE(timer_called_); // expect interval is 100 milliseconds +/- TIMER_MARGIN_MSEC. - EXPECT_TRUE(delta < TIMER_MARGIN_MSEC); + EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) << + "delta " << delta.total_milliseconds() << "msec " << + ">= " << TIMER_MARGIN_MSEC.total_milliseconds(); } TEST_F(IntervalTimerTest, destructIntervalTimer) { @@ -283,14 +289,20 @@ TEST_F(IntervalTimerTest, overwriteIntervalTimer) { // + 400 milliseconds for TimerCallBackOverwriter (stop) // = 800 milliseconds. // delta: difference between elapsed time and 400 + 100 milliseconds + boost::posix_time::time_duration test_runtime = + boost::posix_time::microsec_clock::universal_time() - start; + EXPECT_FALSE(test_runtime.is_negative()) << + "test duration " << test_runtime << + " negative - clock skew?"; boost::posix_time::time_duration delta = - (boost::posix_time::microsec_clock::universal_time() - start) - - boost::posix_time::millisec(400 + 100); + test_runtime - boost::posix_time::milliseconds(400 + 100); if (delta.is_negative()) { delta.invert_sign(); } // expect callback function is updated: TimerCallBack is called EXPECT_TRUE(timer_called_); // expect interval is updated - EXPECT_TRUE(delta < TIMER_MARGIN_MSEC); + EXPECT_TRUE(delta < TIMER_MARGIN_MSEC) << + "delta " << delta.total_milliseconds() << " msec " << + ">= " << TIMER_MARGIN_MSEC.total_milliseconds(); } diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc index c285f9e8c8..97bcb65782 100644 --- a/src/lib/asiolink/tests/run_unittests.cc +++ b/src/lib/asiolink/tests/run_unittests.cc @@ -22,7 +22,6 @@ main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); // Initialize Google test isc::log::setRootLoggerName("unittest"); // Set a root logger name - isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data return (RUN_ALL_TESTS()); } diff --git a/src/lib/cc/Makefile.am b/src/lib/cc/Makefile.am index 44033e9130..9d5b188ebb 100644 --- a/src/lib/cc/Makefile.am +++ b/src/lib/cc/Makefile.am @@ -11,6 +11,7 @@ if USE_GXX # avoid the error. As a short term workaround we suppress this warning # for the entire this module. See also src/bin/auth/Makefile.am. AM_CXXFLAGS += -Wno-unused-parameter +AM_CXXFLAGS += -fno-strict-aliasing endif if USE_CLANGPP # Likewise, ASIO header files will trigger various warnings with clang++. diff --git a/src/lib/cc/tests/Makefile.am b/src/lib/cc/tests/Makefile.am index 535c46422a..71e6988fa4 100644 --- a/src/lib/cc/tests/Makefile.am +++ b/src/lib/cc/tests/Makefile.am @@ -26,7 +26,6 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(GTEST_LDADD) run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la -run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la endif diff --git a/src/lib/config/Makefile.am b/src/lib/config/Makefile.am index 580f240463..52337ad2ed 100644 --- a/src/lib/config/Makefile.am +++ b/src/lib/config/Makefile.am @@ -2,10 +2,24 @@ SUBDIRS = . tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc +AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log AM_CPPFLAGS += $(BOOST_INCLUDES) -AM_CXXFLAGS = $(B10_CXXFLAGS) -Wno-strict-aliasing + +# Define rule to build logging source files from message file +configdef.h configdef.cc: configdef.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/config/configdef.mes + +BUILT_SOURCES = configdef.h configdef.cc lib_LTLIBRARIES = libcfgclient.la -libcfgclient_la_SOURCES = config_data.h config_data.cc module_spec.h module_spec.cc ccsession.cc ccsession.h +libcfgclient_la_SOURCES = config_data.h config_data.cc +libcfgclient_la_SOURCES += module_spec.h module_spec.cc +libcfgclient_la_SOURCES += ccsession.cc ccsession.h +libcfgclient_la_SOURCES += config_log.h config_log.cc -CLEANFILES = *.gcno *.gcda +nodist_libcfgclient_la_SOURCES = configdef.h configdef.cc + +# The message file should be in the distribution. +EXTRA_DIST = configdef.mes + +CLEANFILES = *.gcno *.gcda configdef.h configdef.cc diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc index 69621a4ad1..1b5e47d11a 100644 --- a/src/lib/config/ccsession.cc +++ b/src/lib/config/ccsession.cc @@ -12,12 +12,6 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -// -// todo: generalize this and make it into a specific API for all modules -// to use (i.e. connect to cc, send config and commands, get config, -// react on config change announcements) -// - #include #include @@ -38,6 +32,7 @@ #include #include +#include #include using namespace std; @@ -164,18 +159,18 @@ ModuleCCSession::readModuleSpecification(const std::string& filename) { // this file should be declared in a @something@ directive file.open(filename.c_str()); if (!file) { - cout << "error opening " << filename << ": " << strerror(errno) << endl; - exit(1); + LOG_ERROR(config_logger, CONFIG_FOPEN_ERR).arg(filename).arg(strerror(errno)); + isc_throw(CCSessionInitError, strerror(errno)); } try { module_spec = moduleSpecFromFile(file, true); } catch (const JSONError& pe) { - cout << "Error parsing module specification file: " << pe.what() << endl; - exit(1); + LOG_ERROR(config_logger, CONFIG_JSON_PARSE).arg(filename).arg(pe.what()); + isc_throw(CCSessionInitError, pe.what()); } catch (const ModuleSpecError& dde) { - cout << "Error reading module specification file: " << dde.what() << endl; - exit(1); + LOG_ERROR(config_logger, CONFIG_MODULE_SPEC).arg(filename).arg(dde.what()); + isc_throw(CCSessionInitError, dde.what()); } file.close(); return (module_spec); @@ -223,7 +218,8 @@ ModuleCCSession::ModuleCCSession( int rcode; ConstElementPtr err = parseAnswer(rcode, answer); if (rcode != 0) { - std::cerr << "[" << module_name_ << "] Error in specification: " << answer << std::endl; + LOG_ERROR(config_logger, CONFIG_MANAGER_MOD_SPEC).arg(answer->str()); + isc_throw(CCSessionInitError, answer->str()); } setLocalConfig(Element::fromJSON("{}")); @@ -236,7 +232,8 @@ ModuleCCSession::ModuleCCSession( if (rcode == 0) { handleConfigUpdate(new_config); } else { - std::cerr << "[" << module_name_ << "] Error getting config: " << new_config << std::endl; + LOG_ERROR(config_logger, CONFIG_MANAGER_CONFIG).arg(new_config->str()); + isc_throw(CCSessionInitError, answer->str()); } } @@ -348,15 +345,13 @@ ModuleCCSession::checkCommand() { answer = checkModuleCommand(cmd_str, target_module, arg); } } catch (const CCSessionError& re) { - // TODO: Once we have logging and timeouts, we should not - // answer here (potential interference) - answer = createAnswer(1, re.what()); + LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what()); } if (!isNull(answer)) { session_.reply(routing, answer); } } - + return (0); } diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h index d8d1eebf2e..73648765ae 100644 --- a/src/lib/config/ccsession.h +++ b/src/lib/config/ccsession.h @@ -134,6 +134,15 @@ public: isc::Exception(file, line, what) {} }; +/// +/// \brief This exception is thrown if the constructor fails +/// +class CCSessionInitError : public isc::Exception { +public: + CCSessionInitError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + /// /// \brief This module keeps a connection to the command channel, /// holds configuration information, and handles messages from @@ -154,6 +163,11 @@ public: * AbstractSession without establishing the session. * Note: the design decision on who is responsible for establishing the * session is in flux, and may change in near future. + * + * \exception CCSessionInitError when the initialization fails, + * either because the file cannot be read or there is + * a communication problem with the config manager. + * * @param command_handler A callback function pointer to be called when * a control command from a remote agent needs to be performed on the * local module. diff --git a/src/lib/config/config_log.cc b/src/lib/config/config_log.cc new file mode 100644 index 0000000000..672b9f1cba --- /dev/null +++ b/src/lib/config/config_log.cc @@ -0,0 +1,26 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +/// Defines the logger used by the config lib + +#include "config/config_log.h" + +namespace isc { +namespace config { + +isc::log::Logger config_logger("config"); + +} // namespace nsas +} // namespace isc + diff --git a/src/lib/config/config_log.h b/src/lib/config/config_log.h new file mode 100644 index 0000000000..22e5a5c6e0 --- /dev/null +++ b/src/lib/config/config_log.h @@ -0,0 +1,38 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __CONFIG_LOG__H +#define __CONFIG_LOG__H + +#include +#include "configdef.h" + +namespace isc { +namespace config { + +/// \brief Config Logging +/// +/// Defines logger object for config log messages + +/// \brief Config Logger +/// +/// Define the logger used to log messages. We could define it in multiple +/// modules, but defining in a single module and linking to it saves time and +/// space. +extern isc::log::Logger config_logger; // isc::config::config_logger is the CONFIG logger + +} // namespace config +} // namespace isc + +#endif // __CONFIG_LOG__H diff --git a/src/lib/config/configdef.mes b/src/lib/config/configdef.mes new file mode 100644 index 0000000000..4c3c991ee2 --- /dev/null +++ b/src/lib/config/configdef.mes @@ -0,0 +1,50 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +$PREFIX CONFIG_ +$NAMESPACE isc::config + +% FOPEN_ERR error opening %1: %2 +There was an error opening the given file. + +% JSON_PARSE JSON parse error in %1: %2 +There was a parse error in the JSON file. The given file does not appear +to be in valid JSON format. Please verify that the filename is correct +and that the contents are valid JSON. + +% MODULE_SPEC module specification error in %1: %2 +The given file does not appear to be a valid specification file. Please +verify that the filename is correct and that its contents are a valid +BIND10 module specification. + +% MANAGER_MOD_SPEC module specification not accepted by cfgmgr: %1 +The module specification file for this module was rejected by the +configuration manager. The full error message answer from the +configuration manager is appended to the log error. The most likely +cause is that the module is of a different (specification file) version +than the running configuration manager. + +% MANAGER_CONFIG error getting configuration from cfgmgr: %1 +The configuration manager returned an error when this module requested +the configuration. The full error message answer from the configuration +manager is appended to the log error. The most likely cause is that +the module is of a different (command specification) version than the +running configuration manager. + +% CCSESSION_MSG error in CC session message: %1 +There was a problem with an incoming message on the command and control +channel. The message does not appear to be a valid command, and is +missing a required element or contains an unknown data format. This +most likely means that another BIND10 module is sending a bad message. +The message itself is ignored by this module. diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am index 0cebdbf176..0d2c29b421 100644 --- a/src/lib/config/tests/Makefile.am +++ b/src/lib/config/tests/Makefile.am @@ -24,6 +24,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(GTEST_LDADD) run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la +run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += libfake_session.la run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc index 43663bda69..f566949419 100644 --- a/src/lib/config/tests/ccsession_unittests.cc +++ b/src/lib/config/tests/ccsession_unittests.cc @@ -264,10 +264,7 @@ TEST_F(CCSessionTest, checkCommand) { session.addMessage(el("{ \"command\": \"bad_command\" }"), "Spec29", "*"); result = mccs.checkCommand(); - EXPECT_EQ(1, session.getMsgQueue()->size()); - msg = session.getFirstMessage(group, to); - EXPECT_EQ("{ \"result\": [ 1, \"Command part in command message missing, empty, or not a list\" ] }", msg->str()); - EXPECT_EQ(0, result); + EXPECT_EQ(0, session.getMsgQueue()->size()); session.addMessage(el("{ \"command\": [ \"bad_command\" ] }"), "Spec29", "*"); @@ -432,4 +429,24 @@ TEST_F(CCSessionTest, ignoreRemoteConfigCommands) { EXPECT_EQ(0, session.getMsgQueue()->size()); } +TEST_F(CCSessionTest, initializationFail) { + // bad specification + EXPECT_THROW(ModuleCCSession(ccspecfile("spec8.spec"), session, + NULL, NULL), CCSessionInitError); + + // file that does not exist + EXPECT_THROW(ModuleCCSession(ccspecfile("does_not_exist_spec"), + session, NULL, NULL), + CCSessionInitError); + + + session.getMessages()->add(createAnswer(1, el("\"just an error\""))); + + EXPECT_FALSE(session.haveSubscription("Spec29", "*")); + EXPECT_THROW(ModuleCCSession(ccspecfile("spec29.spec"), session, + my_config_handler, my_command_handler), + CCSessionInitError); + EXPECT_TRUE(session.haveSubscription("Spec29", "*")); +} + } diff --git a/src/lib/config/tests/fake_session.cc b/src/lib/config/tests/fake_session.cc index 29744c7478..5f79d4839f 100644 --- a/src/lib/config/tests/fake_session.cc +++ b/src/lib/config/tests/fake_session.cc @@ -52,16 +52,14 @@ listContains(ConstElementPtr list, ConstElementPtr el) { void listRemove(ElementPtr list, ConstElementPtr el) { - int i = -1; + int i = 0; BOOST_FOREACH(ConstElementPtr s_el, list->listValue()) { if (*el == *s_el) { - i = 0; + list->remove(i); + return; } i++; } - if (i >= 0) { - list->remove(i); - } } // endwant diff --git a/src/lib/config/tests/run_unittests.cc b/src/lib/config/tests/run_unittests.cc index 0908071bac..fab90f5715 100644 --- a/src/lib/config/tests/run_unittests.cc +++ b/src/lib/config/tests/run_unittests.cc @@ -13,9 +13,16 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); + + // TODO: UNCOMMENT ON MERGE + // (this is the call we want in master, but branch point does not + // have this yet) + //isc::log::initLogger(); + return (RUN_ALL_TESTS()); } diff --git a/src/lib/cryptolink/Makefile.am b/src/lib/cryptolink/Makefile.am new file mode 100644 index 0000000000..93f34438b9 --- /dev/null +++ b/src/lib/cryptolink/Makefile.am @@ -0,0 +1,14 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) $(BOTAN_INCLUDES) +AM_CXXFLAGS = $(B10_CXXFLAGS) + +CLEANFILES = *.gcno *.gcda + +lib_LTLIBRARIES = libcryptolink.la + +libcryptolink_la_SOURCES = cryptolink.h cryptolink.cc +libcryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc + +libcryptolink_la_LIBADD = ${BOTAN_LDFLAGS} ${BOTAN_RPATH} diff --git a/src/lib/cryptolink/crypto_hmac.cc b/src/lib/cryptolink/crypto_hmac.cc new file mode 100644 index 0000000000..d20c85bc3f --- /dev/null +++ b/src/lib/cryptolink/crypto_hmac.cc @@ -0,0 +1,246 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include + +#include +#include +#include +#include + +namespace { +const char* +getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) { + switch (algorithm) { + case isc::cryptolink::MD5: + return ("MD5"); + break; + case isc::cryptolink::SHA1: + return ("SHA-1"); + break; + case isc::cryptolink::SHA256: + return ("SHA-256"); + break; + case isc::cryptolink::UNKNOWN_HASH: + return ("Unknown"); + break; + } + // compiler should have prevented us to reach this, since we have + // no default. But we need a return value anyway + return ("Unknown"); +} + +} // local namespace + + +namespace isc { +namespace cryptolink { + +class HMACImpl { +public: + explicit HMACImpl(const void* secret, size_t secret_len, + const HashAlgorithm hash_algorithm) { + Botan::HashFunction* hash; + try { + hash = Botan::get_hash( + getBotanHashAlgorithmName(hash_algorithm)); + } catch (const Botan::Algorithm_Not_Found&) { + isc_throw(isc::cryptolink::UnsupportedAlgorithm, + "Unknown hash algorithm: " + hash_algorithm); + } catch (const Botan::Exception& exc) { + isc_throw(isc::cryptolink::LibraryError, exc.what()); + } + + hmac_.reset(new Botan::HMAC(hash)); + + // If the key length is larger than the block size, we hash the + // key itself first. + try { + if (secret_len > hash->HASH_BLOCK_SIZE) { + Botan::SecureVector hashed_key = + hash->process(static_cast(secret), + secret_len); + hmac_->set_key(hashed_key.begin(), hashed_key.size()); + } else { + hmac_->set_key(static_cast(secret), + secret_len); + } + } catch (const Botan::Invalid_Key_Length& ikl) { + isc_throw(BadKey, ikl.what()); + } catch (const Botan::Exception& exc) { + isc_throw(isc::cryptolink::LibraryError, exc.what()); + } + } + + ~HMACImpl() { } + + size_t getOutputLength() const { + return (hmac_->OUTPUT_LENGTH); + } + + void update(const void* data, const size_t len) { + try { + hmac_->update(static_cast(data), len); + } catch (const Botan::Exception& exc) { + isc_throw(isc::cryptolink::LibraryError, exc.what()); + } + } + + void sign(isc::util::OutputBuffer& result, size_t len) { + try { + Botan::SecureVector b_result(hmac_->final()); + + if (len == 0 || len > b_result.size()) { + len = b_result.size(); + } + result.writeData(b_result.begin(), len); + } catch (const Botan::Exception& exc) { + isc_throw(isc::cryptolink::LibraryError, exc.what()); + } + } + + void sign(void* result, size_t len) { + try { + Botan::SecureVector b_result(hmac_->final()); + size_t output_size = getOutputLength(); + if (output_size > len) { + output_size = len; + } + memcpy(result, b_result.begin(), output_size); + } catch (const Botan::Exception& exc) { + isc_throw(isc::cryptolink::LibraryError, exc.what()); + } + } + + std::vector sign(size_t len) { + try { + Botan::SecureVector b_result(hmac_->final()); + if (len == 0 || len > b_result.size()) { + return (std::vector(b_result.begin(), b_result.end())); + } else { + return (std::vector(b_result.begin(), &b_result[len])); + } + } catch (const Botan::Exception& exc) { + isc_throw(isc::cryptolink::LibraryError, exc.what()); + } + } + + + bool verify(const void* sig, size_t len) { + // Botan's verify_mac checks if len matches the output_length, + // which causes it to fail for truncated signatures, so we do + // the check ourselves + // SEE BELOW FOR TEMPORARY CHANGE + try { + Botan::SecureVector our_mac = hmac_->final(); + if (len < getOutputLength()) { + // Currently we don't support truncated signature. To avoid + // validating too short signature accidently, we enforce the + // standard signature size for the moment. + // Once we support truncation correctly, this if-clause should + // (and the capitalized comment above) be removed. + return (false); + } + if (len == 0 || len > getOutputLength()) { + len = getOutputLength(); + } + return (Botan::same_mem(&our_mac[0], + static_cast(sig), + len)); + } catch (const Botan::Exception& exc) { + isc_throw(isc::cryptolink::LibraryError, exc.what()); + } + } + +private: + boost::scoped_ptr hmac_; +}; + +HMAC::HMAC(const void* secret, size_t secret_length, + const HashAlgorithm hash_algorithm) +{ + impl_ = new HMACImpl(secret, secret_length, hash_algorithm); +} + +HMAC::~HMAC() { + delete impl_; +} + +size_t +HMAC::getOutputLength() const { + return (impl_->getOutputLength()); +} + +void +HMAC::update(const void* data, const size_t len) { + impl_->update(data, len); +} + +void +HMAC::sign(isc::util::OutputBuffer& result, size_t len) { + impl_->sign(result, len); +} + +void +HMAC::sign(void* result, size_t len) { + impl_->sign(result, len); +} + +std::vector +HMAC::sign(size_t len) { + return impl_->sign(len); +} + +bool +HMAC::verify(const void* sig, const size_t len) { + return (impl_->verify(sig, len)); +} + +void +signHMAC(const void* data, size_t data_len, const void* secret, + size_t secret_len, const HashAlgorithm hash_algorithm, + isc::util::OutputBuffer& result, size_t len) +{ + boost::scoped_ptr hmac( + CryptoLink::getCryptoLink().createHMAC(secret, + secret_len, + hash_algorithm)); + hmac->update(data, data_len); + hmac->sign(result, len); +} + + +bool +verifyHMAC(const void* data, const size_t data_len, const void* secret, + size_t secret_len, const HashAlgorithm hash_algorithm, + const void* sig, const size_t sig_len) +{ + boost::scoped_ptr hmac( + CryptoLink::getCryptoLink().createHMAC(secret, + secret_len, + hash_algorithm)); + hmac->update(data, data_len); + return (hmac->verify(sig, sig_len)); +} + +void +deleteHMAC(HMAC* hmac) { + delete hmac; +} + +} // namespace cryptolink +} // namespace isc diff --git a/src/lib/cryptolink/crypto_hmac.h b/src/lib/cryptolink/crypto_hmac.h new file mode 100644 index 0000000000..2eb0d0ec8f --- /dev/null +++ b/src/lib/cryptolink/crypto_hmac.h @@ -0,0 +1,209 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include + +#include + +#ifndef _ISC_CRYPTO_HMAC_H +#define _ISC_CRYPTO_HMAC_H + +namespace isc { +namespace cryptolink { + +/// Forward declaration, pimpl style +class HMACImpl; + +/// \brief HMAC support +/// +/// This class is used to create and verify HMAC signatures. Instances +/// can be created with CryptoLink::createHMAC() +/// +class HMAC : private boost::noncopyable { +private: + /// \brief Constructor from a secret and a hash algorithm + /// + /// \exception UnsupportedAlgorithmException if the given algorithm + /// is unknown or not supported by the underlying library + /// \exception InvalidKeyLength if the given key secret_len is bad + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// Notes: if the secret is longer than the block size of its + /// algorithm, the constructor will run it through the hash + /// algorithm, and use the digest as the secret for this HMAC + /// operation + /// + /// \param secret The secret to sign with + /// \param len The length of the secret + /// \param hash_algorithm The hash algorithm + HMAC(const void* secret, size_t secret_len, + const HashAlgorithm hash_algorithm); + + friend HMAC* CryptoLink::createHMAC(const void*, size_t, + const HashAlgorithm); + +public: + /// \brief Destructor + ~HMAC(); + + /// \brief Returns the output size of the digest + /// + /// \return output size of the digest + size_t getOutputLength() const; + + /// \brief Add data to digest + /// + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// \param data The data to add + /// \param len The size of the data + void update(const void* data, const size_t len); + + /// \brief Calculate the final signature + /// + /// The result will be appended to the given outputbuffer + /// + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// \param result The OutputBuffer to append the result to + /// \param len The number of bytes from the result to copy. If this + /// value is smaller than the algorithms output size, the + /// result will be truncated. If this value is larger, or 0 + /// (the default), it will be ignored + void sign(isc::util::OutputBuffer& result, size_t len = 0); + + /// \brief Calculate the final signature + /// + /// len bytes of data from the result will be copied to *result + /// If len is larger than the output size, only output_size bytes + /// will be copied. If it is smaller, the output will be truncated + /// + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// At least len bytes of data must be available for writing at + /// result + void sign(void* result, size_t len); + + /// \brief Calculate the final signatre + /// + /// The result will be returned as a std::vector + /// + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// \param len The number of bytes from the result to copy. If this + /// value is smaller than the algorithms output size, the + /// result will be truncated. If this value is larger, or 0 + /// (the default), it will be ignored + /// \return a vector containing the signature + std::vector sign(size_t len = 0); + + /// \brief Verify an existing signature + /// + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// \param sig The signature to verify + /// \param len The length of the signature. If this is non-zero, + /// and smaller than the output length of the algorithm, + /// only len bytes will be checked + /// \return true if the signature is correct, false otherwise + bool verify(const void* sig, size_t len); + +private: + HMACImpl* impl_; +}; + +/// \brief Create an HMAC signature for the given data +/// +/// This is a convenience function that calculates the hmac signature, +/// given a fixed amount of data. Internally it does the same as +/// creating an HMAC object, feeding it the data, and calculating the +/// resulting signature. +/// +/// \exception UnsupportedAlgorithm if the given algorithm is unknown +/// or not supported by the underlying library +/// \exception BadKey if the given key secret_len is bad +/// \exception LibraryError if there was any unexpected exception +/// in the underlying library +/// +/// Notes: if the secret is longer than the block size of its +/// algorithm, the constructor will run it through the hash +/// algorithm, and use the digest as the secret for this HMAC +/// operation +/// +/// \param data The data to sign +/// \param data_len The length of the data +/// \param secret The secret to sign with +/// \param secret_len The length of the secret +/// \param hash_algorithm The hash algorithm +/// \param result The signature will be appended to this buffer +/// \param len If this is non-zero and less than the output size, +/// the result will be truncated to len bytes +void signHMAC(const void* data, + const size_t data_len, + const void* secret, + size_t secret_len, + const HashAlgorithm hash_algorithm, + isc::util::OutputBuffer& result, + size_t len = 0); + +/// \brief Verify an HMAC signature for the given data +/// +/// This is a convenience function that verifies an hmac signature, +/// given a fixed amount of data. Internally it does the same as +/// creating an HMAC object, feeding it the data, and checking the +/// resulting signature. +/// +/// \exception UnsupportedAlgorithm if the given algorithm is unknown +/// or not supported by the underlying library +/// \exception BadKey if the given key secret_len is bad +/// \exception LibraryError if there was any unexpected exception +/// in the underlying library +/// +/// Notes: if the secret is longer than the block size of its +/// algorithm, the constructor will run it through the hash +/// algorithm, and use the digest as the secret for this HMAC +/// operation +/// +/// \param data The data to verify +/// \param data_len The length of the data +/// \param secret The secret to sign with +/// \param secret_len The length of the secret +/// \param hash_algorithm The hash algorithm +/// \param sig The signature to verify +/// \param sig_len The length of the signature +/// \return True if the signature verifies, false if not +bool verifyHMAC(const void* data, + const size_t data_len, + const void* secret, + size_t secret_len, + const HashAlgorithm hash_algorithm, + const void* sig, + const size_t sig_len); + +/// \brief Delete an HMAC object +void deleteHMAC(HMAC* hmac); + +} // namespace cryptolink +} // namespace isc + +#endif // __ISC_CRYPTO_HMAC + diff --git a/src/lib/cryptolink/cryptolink.cc b/src/lib/cryptolink/cryptolink.cc new file mode 100644 index 0000000000..d1c375d4a6 --- /dev/null +++ b/src/lib/cryptolink/cryptolink.cc @@ -0,0 +1,69 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include + +namespace isc { +namespace cryptolink { + +// For Botan, we use the CryptoLink class object in RAII style +class CryptoLinkImpl { +private: + Botan::LibraryInitializer botan_init_; +}; + +CryptoLink::~CryptoLink() { + delete impl_; +} + +CryptoLink& +CryptoLink::getCryptoLink() { + CryptoLink& c = getCryptoLinkInternal(); + if (c.impl_ == NULL) { + c.initialize(); + } + return (c); +} + +CryptoLink& +CryptoLink::getCryptoLinkInternal() { + static CryptoLink instance; + return (instance); +} + +void +CryptoLink::initialize() { + CryptoLink& c = getCryptoLinkInternal(); + if (c.impl_ == NULL) { + try { + c.impl_ = new CryptoLinkImpl(); + } catch (const Botan::Exception& ex) { + isc_throw(InitializationError, ex.what()); + } + } +} + +HMAC* +CryptoLink::createHMAC(const void* secret, size_t secret_len, + const HashAlgorithm hash_algorithm) +{ + return (new HMAC(secret, secret_len, hash_algorithm)); +} + +} // namespace cryptolink +} // namespace isc + diff --git a/src/lib/cryptolink/cryptolink.h b/src/lib/cryptolink/cryptolink.h new file mode 100644 index 0000000000..15831369c8 --- /dev/null +++ b/src/lib/cryptolink/cryptolink.h @@ -0,0 +1,204 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef _ISC_CRYPTO_H +#define _ISC_CRYPTO_H + +#include +#include +#include + +#include +#include + +#include + +namespace isc { +namespace cryptolink { + +/// \brief Hash algorithm identifiers +enum HashAlgorithm { + MD5 = 0, ///< MD5 + SHA1 = 1, ///< SHA-1 + SHA256 = 2, ///< SHA-256 + UNKNOWN_HASH = 3 ///< This value can be used in conversion + /// functions, to be returned when the + /// input is unknown (but a value MUST be + /// returned), for instance when the input + /// is a Name or a string, and the return + /// value is a HashAlgorithm. +}; + +// Forward declaration for createHMAC() +class HMAC; + +/// General exception class that is the base for all crypto-related +/// exceptions +class CryptoLinkError : public Exception { +public: + CryptoLinkError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// This exception is thrown if there was a problem initializing the +/// crypto library +class InitializationError : public CryptoLinkError { +public: + InitializationError(const char* file, size_t line, const char* what) : + CryptoLinkError(file, line, what) {} +}; + +/// This exception is thrown when a cryptographic action is requested +/// for an algorithm that is not supported by the underlying library. +class UnsupportedAlgorithm : public CryptoLinkError { +public: + UnsupportedAlgorithm(const char* file, size_t line, const char* what) : + CryptoLinkError(file, line, what) {} +}; + +/// This exception is thrown when the underlying library could not +/// handle the key data. +class BadKey : public CryptoLinkError { +public: + BadKey(const char* file, size_t line, const char* what) : + CryptoLinkError(file, line, what) {} +}; + +/// This exception is raised when a general error that was not +/// specifically caught is thrown by the underlying library. It +/// is replaced by this one so as not have 'external' exceptions +/// bubbling up +class LibraryError : public CryptoLinkError { +public: + LibraryError(const char* file, size_t line, const char* what) : + CryptoLinkError(file, line, what) {} +}; + +/// Forward declaration for pimpl +class CryptoLinkImpl; + +/// \brief Singleton entry point and factory class +/// +/// This is a singleton class that serves as the entry point to +/// the underlying cryptography library, and as a factory for objects +/// within the cryptolink library. +/// +/// There is only one way to access it, through getCryptoLink(), which +/// returns a reference to the initialized library. On the first call, +/// it will be initialized automatically. You can however initialize it +/// manually through a call to the initalize(), before your first call +/// to getCryptoLink. Any subsequent call to initialize() will be a +/// noop. +/// +/// In order for the CryptoLink library to be sure that the underlying +/// library has been initialized, and because we do not want to add +/// such a check to every class and function within it, we have made +/// the constructors of all classes within cryptolink private. This way +/// a caller cannot instantiate an object before the library is +/// initialized, but must use CryptoLink's create method (e.g. +/// createHMAC()), which enforces (automatic) initialization. +/// +/// In order for the CryptoLink class to be able to create objects that +/// have private constructors, it is declared a friend class of these +/// classes. +/// +/// Since these factory functions return bare pointers, we also provide +/// deleter functions for them (e.g. deleteHMAC()), so that a caller +/// can use that to make sure it uses the correct delete operator (the +/// one defined at compilation time of this library). A way to make +/// sure you do not forget this, is to place the result of the create +/// functions in a shared_ptr with the corresponding deleter function. +/// +/// \note All other classes within cryptolink should have private +/// constructors as well, and should have a factory function from +/// CryptoLink, and a deleter function. +/// +// Internal note: we can use this class later to initialize and manage +// dynamic (PKCS#11) libs +class CryptoLink : private boost::noncopyable { +public: + /// \brief Returns a reference to the singleton instance + /// + /// If the library has not been initialized yet, it will be + /// initialized with some default values. + /// + /// Since this class is noncopyable, you must use the return + /// value directly, or store it in a reference variable. + /// + /// \exception InitializationError if initialization fails + /// + /// \return Reference to the singleton instance + static CryptoLink& getCryptoLink(); + + /// \brief Initialize the library manually + /// + /// If the library has already been initialized (either by a call + /// to initialize() or automatically in getCryptoLink()), this + /// function does nothing. + /// + /// \note A call to initialize() is not strictly necessary with + /// the current implementation. + /// + /// \exception InitializationError if initialization fails + /// + static void initialize(); + + /// \brief Factory function for HMAC objects + /// + /// CryptoLink objects cannot be constructed directly. This + /// function creates a new HMAC object usable for signing or + /// verification. + /// + /// The caller is responsible for deleting the object, and it is + /// therefore highly recommended to place the return value of this + /// function in a scoped_ptr or shared_ptr. + /// + /// Notes: if the secret is longer than the block size of its + /// algorithm, the constructor will run it through the hash + /// algorithm, and use the digest as the secret for this HMAC + /// operation + /// + /// If you want to safely delete objects created with this method, + /// you can use the function deleteHMAC() as defined in + /// crypto_hmac.h + /// + /// \exception UnsupportedAlgorithmException if the given algorithm + /// is unknown or not supported by the underlying library + /// \exception InvalidKeyLength if the given key secret_len is bad + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// \param secret The secret to sign with + /// \param secret_len The length of the secret + /// \param hash_algorithm The hash algorithm + HMAC* createHMAC(const void* secret, size_t secret_len, + const HashAlgorithm hash_algorithm); + +private: + // To enable us to use an optional explicit initialization call, + // the 'real' instance getter is private + static CryptoLink& getCryptoLinkInternal(); + + // To prevent people constructing their own, we make the constructor + // private too. + CryptoLink() : impl_(NULL) {} + ~CryptoLink(); + + CryptoLinkImpl* impl_; +}; + +} // namespace cryptolink +} // namespace isc + +#endif // _ISC_CRYPTO_H diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am new file mode 100644 index 0000000000..c8b5e266f4 --- /dev/null +++ b/src/lib/cryptolink/tests/Makefile.am @@ -0,0 +1,26 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(B10_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS = +if HAVE_GTEST +TESTS += run_unittests +run_unittests_SOURCES = run_unittests.cc +run_unittests_SOURCES += crypto_unittests.cc +run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) +run_unittests_LDADD = $(GTEST_LDADD) +run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la +run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/cryptolink/tests/crypto_unittests.cc b/src/lib/cryptolink/tests/crypto_unittests.cc new file mode 100644 index 0000000000..a1ffaabaf1 --- /dev/null +++ b/src/lib/cryptolink/tests/crypto_unittests.cc @@ -0,0 +1,519 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include +#include + +#include +#include + +#include + +using namespace isc::util; +using namespace isc::cryptolink; + +namespace { + void checkData(const uint8_t* data, const uint8_t* expected, + size_t len) { + for (size_t i = 0; i < len; ++i) { + ASSERT_EQ(expected[i], data[i]); + } + } + + void checkBuffer(const OutputBuffer& buf, const uint8_t* expected, + size_t len) + { + ASSERT_EQ(len, buf.getLength()); + checkData(static_cast(buf.getData()), expected, + len); + } + + // Sign and verify with the convenience functions + void doHMACTestConv(const std::string& data, + const void* secret, + size_t secret_len, + const HashAlgorithm hash_algorithm, + const uint8_t* expected_hmac, + size_t hmac_len) { + OutputBuffer data_buf(data.size()); + data_buf.writeData(data.c_str(), data.size()); + OutputBuffer hmac_sig(0); + + // Sign it + signHMAC(data_buf.getData(), data_buf.getLength(), + secret, secret_len, hash_algorithm, hmac_sig, hmac_len); + + // Check if the signature is what we expect + checkBuffer(hmac_sig, expected_hmac, hmac_len); + + // Check whether we can verify it ourselves + EXPECT_TRUE(verifyHMAC(data_buf.getData(), data_buf.getLength(), + secret, secret_len, hash_algorithm, + hmac_sig.getData(), + hmac_sig.getLength())); + + // Change the sig by flipping the first octet, and check + // whether verification fails then + hmac_sig.writeUint8At(~hmac_sig[0], 0); + EXPECT_FALSE(verifyHMAC(data_buf.getData(), data_buf.getLength(), + secret, secret_len, hash_algorithm, + hmac_sig.getData(), + hmac_sig.getLength())); + } + + // Sign and verify with an instantiation of an HMAC object + void doHMACTestDirect(const std::string& data, + const void* secret, + size_t secret_len, + const HashAlgorithm hash_algorithm, + const uint8_t* expected_hmac, + size_t hmac_len) { + OutputBuffer data_buf(data.size()); + data_buf.writeData(data.c_str(), data.size()); + OutputBuffer hmac_sig(1); + CryptoLink& crypto = CryptoLink::getCryptoLink(); + + // Sign it + boost::shared_ptr hmac_sign(crypto.createHMAC(secret, + secret_len, + hash_algorithm), + deleteHMAC); + hmac_sign->update(data_buf.getData(), data_buf.getLength()); + hmac_sign->sign(hmac_sig, hmac_len); + + // Check if the signature is what we expect + checkBuffer(hmac_sig, expected_hmac, hmac_len); + + // Check whether we can verify it ourselves + boost::shared_ptr hmac_verify(crypto.createHMAC(secret, + secret_len, + hash_algorithm), + deleteHMAC); + hmac_verify->update(data_buf.getData(), data_buf.getLength()); + EXPECT_TRUE(hmac_verify->verify(hmac_sig.getData(), + hmac_sig.getLength())); + + // Change the sig by flipping the first octet, and check + // whether verification fails then + hmac_sig.writeUint8At(~hmac_sig[0], 0); + EXPECT_FALSE(hmac_verify->verify(hmac_sig.getData(), + hmac_sig.getLength())); + } + + void doHMACTestVector(const std::string& data, + const void* secret, + size_t secret_len, + const HashAlgorithm hash_algorithm, + const uint8_t* expected_hmac, + size_t hmac_len) { + CryptoLink& crypto = CryptoLink::getCryptoLink(); + boost::shared_ptr hmac_sign(crypto.createHMAC(secret, + secret_len, + hash_algorithm), + deleteHMAC); + hmac_sign->update(data.c_str(), data.size()); + std::vector sig = hmac_sign->sign(hmac_len); + ASSERT_EQ(hmac_len, sig.size()); + checkData(&sig[0], expected_hmac, hmac_len); + + boost::shared_ptr hmac_verify(crypto.createHMAC(secret, + secret_len, + hash_algorithm), + deleteHMAC); + hmac_verify->update(data.c_str(), data.size()); + EXPECT_TRUE(hmac_verify->verify(&sig[0], sig.size())); + + sig[0] = ~sig[0]; + EXPECT_FALSE(hmac_verify->verify(&sig[0], sig.size())); + } + + void doHMACTestArray(const std::string& data, + const void* secret, + size_t secret_len, + const HashAlgorithm hash_algorithm, + const uint8_t* expected_hmac, + size_t hmac_len) { + CryptoLink& crypto = CryptoLink::getCryptoLink(); + boost::shared_ptr hmac_sign(crypto.createHMAC(secret, + secret_len, + hash_algorithm), + deleteHMAC); + hmac_sign->update(data.c_str(), data.size()); + + // note: this is not exception-safe, and can leak, but + // if there is an unexpected exception in the code below we + // have more important things to fix. + uint8_t* sig = new uint8_t[hmac_len]; + + hmac_sign->sign(sig, hmac_len); + checkData(sig, expected_hmac, hmac_len); + + boost::shared_ptr hmac_verify(crypto.createHMAC(secret, + secret_len, + hash_algorithm), + deleteHMAC); + hmac_verify->update(data.c_str(), data.size()); + EXPECT_TRUE(hmac_verify->verify(sig, hmac_len)); + + sig[0] = ~sig[0]; + EXPECT_FALSE(hmac_verify->verify(sig, hmac_len)); + + delete[] sig; + } + + void doHMACTest(const std::string& data, + const void* secret, + size_t secret_len, + const HashAlgorithm hash_algorithm, + const uint8_t* expected_hmac, + size_t hmac_len) { + doHMACTestConv(data, secret, secret_len, hash_algorithm, + expected_hmac, hmac_len); + doHMACTestDirect(data, secret, secret_len, hash_algorithm, + expected_hmac, hmac_len); + doHMACTestVector(data, secret, secret_len, hash_algorithm, + expected_hmac, hmac_len); + doHMACTestArray(data, secret, secret_len, hash_algorithm, + expected_hmac, hmac_len); + } +} + +// +// Test values taken from RFC 2202 +// +TEST(CryptoLinkTest, HMAC_MD5_RFC2202_SIGN) { + const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b }; + const uint8_t hmac_expected[] = { 0x92, 0x94, 0x72, 0x7a, 0x36, + 0x38, 0xbb, 0x1c, 0x13, 0xf4, + 0x8e, 0xf8, 0x15, 0x8b, 0xfc, + 0x9d }; + doHMACTest("Hi There", secret, 16, MD5, hmac_expected, 16); + + const uint8_t hmac_expected2[] = { 0x75, 0x0c, 0x78, 0x3e, 0x6a, + 0xb0, 0xb5, 0x03, 0xea, 0xa8, + 0x6e, 0x31, 0x0a, 0x5d, 0xb7, + 0x38 }; + doHMACTest("what do ya want for nothing?", "Jefe", 4, MD5, + hmac_expected2, 16); + + const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa }; + const uint8_t hmac_expected3[] = { 0x56, 0xbe, 0x34, 0x52, 0x1d, + 0x14, 0x4c, 0x88, 0xdb, 0xb8, + 0xc7, 0x33, 0xf0, 0xe8, 0xb3, + 0xf6}; + doHMACTest(std::string(50, 0xdd), secret3, 16, MD5, hmac_expected3, 16); + + const std::string data4(50, 0xcd); + const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19 }; + const uint8_t hmac_expected4[] = { 0x69, 0x7e, 0xaf, 0x0a, 0xca, + 0x3a, 0x3a, 0xea, 0x3a, 0x75, + 0x16, 0x47, 0x46, 0xff, 0xaa, + 0x79 }; + doHMACTest(data4, secret4, 25, MD5, hmac_expected4, 16); + + const uint8_t hmac_expected6[] = { 0x6b, 0x1a, 0xb7, 0xfe, 0x4b, + 0xd7, 0xbf, 0x8f, 0x0b, 0x62, + 0xe6, 0xce, 0x61, 0xb9, 0xd0, + 0xcd }; + doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First", + std::string(80, 0xaa).c_str(), 80, MD5, hmac_expected6, 16); + + const uint8_t hmac_expected7[] = { 0x6f, 0x63, 0x0f, 0xad, 0x67, + 0xcd, 0xa0, 0xee, 0x1f, 0xb1, + 0xf5, 0x62, 0xdb, 0x3a, 0xa5, + 0x3e }; + doHMACTest("Test Using Larger Than Block-Size Key and Larger Than " + "One Block-Size Data", + std::string(80, 0xaa).c_str(), 80, MD5, hmac_expected7, 16); +} + +// Temporarily disabled +TEST(CryptoLinkTest, DISABLED_HMAC_MD5_RFC2202_SIGN_TRUNCATED) { + const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c }; + const uint8_t hmac_expected5[] = { 0x56, 0x46, 0x1e, 0xf2, 0x34, + 0x2e, 0xdc, 0x00, 0xf9, 0xba, + 0xb9, 0x95, 0x69, 0x0e, 0xfd, + 0x4c }; + doHMACTest("Test With Truncation", secret5, 16, MD5, + hmac_expected5, 16); + doHMACTest("Test With Truncation", secret5, 16, MD5, + hmac_expected5, 12); +} + +// +// Test values taken from RFC 2202 +// +TEST(CryptoLinkTest, HMAC_SHA1_RFC2202_SIGN) { + const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; + const uint8_t hmac_expected[] = { 0xb6, 0x17, 0x31, 0x86, 0x55, + 0x05, 0x72, 0x64, 0xe2, 0x8b, + 0xc0, 0xb6, 0xfb, 0x37, 0x8c, + 0x8e, 0xf1, 0x46, 0xbe, 0x00 }; + doHMACTest("Hi There", secret, 20, SHA1, hmac_expected, 20); + + const uint8_t hmac_expected2[] = { 0xef, 0xfc, 0xdf, 0x6a, 0xe5, + 0xeb, 0x2f, 0xa2, 0xd2, 0x74, + 0x16, 0xd5, 0xf1, 0x84, 0xdf, + 0x9c, 0x25, 0x9a, 0x7c, 0x79 }; + doHMACTest("what do ya want for nothing?", "Jefe", 4, SHA1, + hmac_expected2, 20); + + const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa }; + const uint8_t hmac_expected3[] = { 0x12, 0x5d, 0x73, 0x42, 0xb9, + 0xac, 0x11, 0xcd, 0x91, 0xa3, + 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, + 0x4f, 0x63, 0xf1, 0x75, 0xd3 }; + doHMACTest(std::string(50, 0xdd), secret3, 20, SHA1, hmac_expected3, 20); + + const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19 }; + const uint8_t hmac_expected4[] = { 0x4c, 0x90, 0x07, 0xf4, 0x02, + 0x62, 0x50, 0xc6, 0xbc, 0x84, + 0x14, 0xf9, 0xbf, 0x50, 0xc8, + 0x6c, 0x2d, 0x72, 0x35, 0xda }; + doHMACTest(std::string(50, 0xcd), secret4, 25, SHA1, hmac_expected4, 20); + + const uint8_t hmac_expected6[] = { 0xaa, 0x4a, 0xe5, 0xe1, 0x52, + 0x72, 0xd0, 0x0e, 0x95, 0x70, + 0x56, 0x37, 0xce, 0x8a, 0x3b, + 0x55, 0xed, 0x40, 0x21, 0x12 }; + doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First", + std::string(80, 0xaa).c_str(), 80, SHA1, hmac_expected6, 20); + + const uint8_t hmac_expected7[] = { 0xe8, 0xe9, 0x9d, 0x0f, 0x45, + 0x23, 0x7d, 0x78, 0x6d, 0x6b, + 0xba, 0xa7, 0x96, 0x5c, 0x78, + 0x08, 0xbb, 0xff, 0x1a, 0x91 }; + doHMACTest("Test Using Larger Than Block-Size Key and Larger Than " + "One Block-Size Data", + std::string(80, 0xaa).c_str(), 80, SHA1, hmac_expected7, 20); +} + +// Temporarily disabled +TEST(CryptoLinkTest, DISABLED_HMAC_SHA1_RFC2202_SIGN_TRUNCATED) { + const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c }; + const uint8_t hmac_expected5[] = { 0x4c, 0x1a, 0x03, 0x42, 0x4b, + 0x55, 0xe0, 0x7f, 0xe7, 0xf2, + 0x7b, 0xe1, 0xd5, 0x8b, 0xb9, + 0x32, 0x4a, 0x9a, 0x5a, 0x04 }; + doHMACTest("Test With Truncation", secret5, 20, SHA1, + hmac_expected5, 20); + doHMACTest("Test With Truncation", secret5, 20, SHA1, + hmac_expected5, 12); +} + +// +// Test values taken from RFC 4231 +// +TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) { + const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }; + const uint8_t hmac_expected[] = { 0xb0, 0x34, 0x4c, 0x61, 0xd8, + 0xdb, 0x38, 0x53, 0x5c, 0xa8, + 0xaf, 0xce, 0xaf, 0x0b, 0xf1, + 0x2b, 0x88, 0x1d, 0xc2, 0x00, + 0xc9, 0x83, 0x3d, 0xa7, 0x26, + 0xe9, 0x37, 0x6c, 0x2e, 0x32, + 0xcf, 0xf7 }; + doHMACTest("Hi There", secret, 20, SHA256, hmac_expected, 32); + + const uint8_t hmac_expected2[] = { 0x5b, 0xdc, 0xc1, 0x46, 0xbf, + 0x60, 0x75, 0x4e, 0x6a, 0x04, + 0x24, 0x26, 0x08, 0x95, 0x75, + 0xc7, 0x5a, 0x00, 0x3f, 0x08, + 0x9d, 0x27, 0x39, 0x83, 0x9d, + 0xec, 0x58, 0xb9, 0x64, 0xec, + 0x38, 0x43 }; + doHMACTest("what do ya want for nothing?", "Jefe", 4, SHA256, + hmac_expected2, 32); + + const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa }; + const uint8_t hmac_expected3[] = { 0x77, 0x3e, 0xa9, 0x1e, 0x36, + 0x80, 0x0e, 0x46, 0x85, 0x4d, + 0xb8, 0xeb, 0xd0, 0x91, 0x81, + 0xa7, 0x29, 0x59, 0x09, 0x8b, + 0x3e, 0xf8, 0xc1, 0x22, 0xd9, + 0x63, 0x55, 0x14, 0xce, 0xd5, + 0x65, 0xfe }; + doHMACTest(std::string(50, 0xdd), secret3, 20, SHA256, hmac_expected3, 32); + + const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19 }; + const uint8_t hmac_expected4[] = { 0x82, 0x55, 0x8a, 0x38, 0x9a, + 0x44, 0x3c, 0x0e, 0xa4, 0xcc, + 0x81, 0x98, 0x99, 0xf2, 0x08, + 0x3a, 0x85, 0xf0, 0xfa, 0xa3, + 0xe5, 0x78, 0xf8, 0x07, 0x7a, + 0x2e, 0x3f, 0xf4, 0x67, 0x29, + 0x66, 0x5b }; + doHMACTest(std::string(50, 0xcd), secret4, 25, SHA256, hmac_expected4, 32); + + const uint8_t hmac_expected6[] = { 0x60, 0xe4, 0x31, 0x59, 0x1e, + 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, + 0x26, 0xaa, 0xcb, 0xf5, 0xb7, + 0x7f, 0x8e, 0x0b, 0xc6, 0x21, + 0x37, 0x28, 0xc5, 0x14, 0x05, + 0x46, 0x04, 0x0f, 0x0e, 0xe3, + 0x7f, 0x54 }; + doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First", + std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected6, 32); + + const uint8_t hmac_expected7[] = { 0x9b, 0x09, 0xff, 0xa7, 0x1b, + 0x94, 0x2f, 0xcb, 0x27, 0x63, + 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, + 0x44, 0xbf, 0xdc, 0x63, 0x64, + 0x4f, 0x07, 0x13, 0x93, 0x8a, + 0x7f, 0x51, 0x53, 0x5c, 0x3a, + 0x35, 0xe2 }; + doHMACTest("This is a test using a larger than block-size key and a" + " larger than block-size data. The key needs to be hashe" + "d before being used by the HMAC algorithm.", + std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected7, 32); +} + +TEST(CryptoLinkTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) { + const uint8_t secret5[] = { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c }; + const uint8_t hmac_expected5[] = { 0xa3, 0xb6, 0x16, 0x74, 0x73, + 0x10, 0x0e, 0xe0, 0x6e, 0x0c, + 0x79, 0x6c, 0x29, 0x55, 0x55, + 0x2b }; + doHMACTest("Test With Truncation", secret5, 20, SHA256, + hmac_expected5, 16); +} + +namespace { + size_t + sigVectorLength(HashAlgorithm alg, size_t len) { + boost::shared_ptr hmac_sign( + CryptoLink::getCryptoLink().createHMAC("asdf", 4, alg), + deleteHMAC); + hmac_sign->update("asdf", 4); + const std::vector sig = hmac_sign->sign(len); + return (sig.size()); + } + + size_t + sigBufferLength(HashAlgorithm alg, size_t len) { + boost::shared_ptr hmac_sign( + CryptoLink::getCryptoLink().createHMAC("asdf", 4, alg), + deleteHMAC); + hmac_sign->update("asdf", 4); + OutputBuffer sig(0); + hmac_sign->sign(sig, len); + return (sig.getLength()); + } +} + +TEST(CryptoLinkTest, HMACSigLengthArgument) { + std::vector sig; + + EXPECT_EQ(16, sigVectorLength(MD5, 0)); + EXPECT_EQ(8, sigVectorLength(MD5, 8)); + EXPECT_EQ(16, sigVectorLength(MD5, 16)); + EXPECT_EQ(16, sigVectorLength(MD5, 40)); + EXPECT_EQ(16, sigVectorLength(MD5, 2000)); + + EXPECT_EQ(20, sigBufferLength(SHA1, 0)); + EXPECT_EQ(8, sigBufferLength(SHA1, 8)); + EXPECT_EQ(20, sigBufferLength(SHA1, 20)); + EXPECT_EQ(20, sigBufferLength(SHA1, 40)); + EXPECT_EQ(20, sigBufferLength(SHA1, 2000)); + + EXPECT_EQ(32, sigBufferLength(SHA256, 0)); + EXPECT_EQ(8, sigBufferLength(SHA256, 8)); + EXPECT_EQ(32, sigBufferLength(SHA256, 32)); + EXPECT_EQ(32, sigBufferLength(SHA256, 40)); + EXPECT_EQ(32, sigBufferLength(SHA256, 3200)); + + EXPECT_EQ(16, sigBufferLength(MD5, 0)); + EXPECT_EQ(8, sigBufferLength(MD5, 8)); + EXPECT_EQ(16, sigBufferLength(MD5, 16)); + EXPECT_EQ(16, sigBufferLength(MD5, 40)); + EXPECT_EQ(16, sigBufferLength(MD5, 2000)); + + EXPECT_EQ(20, sigBufferLength(SHA1, 0)); + EXPECT_EQ(8, sigBufferLength(SHA1, 8)); + EXPECT_EQ(20, sigBufferLength(SHA1, 20)); + EXPECT_EQ(20, sigBufferLength(SHA1, 40)); + EXPECT_EQ(20, sigBufferLength(SHA1, 2000)); + + EXPECT_EQ(32, sigBufferLength(SHA256, 0)); + EXPECT_EQ(8, sigBufferLength(SHA256, 8)); + EXPECT_EQ(32, sigBufferLength(SHA256, 32)); + EXPECT_EQ(32, sigBufferLength(SHA256, 40)); + EXPECT_EQ(32, sigBufferLength(SHA256, 3200)); +} + +TEST(CryptoLinkTest, BadKey) { + OutputBuffer data_buf(0); + OutputBuffer hmac_sig(0); + CryptoLink& crypto = CryptoLink::getCryptoLink(); + + EXPECT_THROW(crypto.createHMAC(NULL, 0, MD5), BadKey); + EXPECT_THROW(crypto.createHMAC(NULL, 0, UNKNOWN_HASH), UnsupportedAlgorithm); + + EXPECT_THROW(signHMAC(data_buf.getData(), data_buf.getLength(), + NULL, 0, MD5, hmac_sig), BadKey); + EXPECT_THROW(signHMAC(data_buf.getData(), data_buf.getLength(), + NULL, 0, UNKNOWN_HASH, hmac_sig), + UnsupportedAlgorithm); + + EXPECT_THROW(verifyHMAC(data_buf.getData(), data_buf.getLength(), + NULL, 0, MD5, hmac_sig.getData(), + hmac_sig.getLength()), BadKey); + EXPECT_THROW(verifyHMAC(data_buf.getData(), data_buf.getLength(), + NULL, 0, UNKNOWN_HASH, hmac_sig.getData(), + hmac_sig.getLength()), + UnsupportedAlgorithm); +} + +TEST(CryptoLinkTest, Singleton) { + const CryptoLink& c1 = CryptoLink::getCryptoLink(); + const CryptoLink& c2 = CryptoLink::getCryptoLink(); + ASSERT_EQ(&c1, &c2); +} diff --git a/src/lib/cryptolink/tests/run_unittests.cc b/src/lib/cryptolink/tests/run_unittests.cc new file mode 100644 index 0000000000..d16327e0d3 --- /dev/null +++ b/src/lib/cryptolink/tests/run_unittests.cc @@ -0,0 +1,22 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return (RUN_ALL_TESTS()); +} diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am index adb1d41e87..e028186aee 100644 --- a/src/lib/datasrc/Makefile.am +++ b/src/lib/datasrc/Makefile.am @@ -7,7 +7,7 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS) AM_CXXFLAGS = $(B10_CXXFLAGS) -CLEANFILES = *.gcno *.gcda +CLEANFILES = *.gcno *.gcda messagedef.h messagedef.cc lib_LTLIBRARIES = libdatasrc.la libdatasrc_la_SOURCES = data_source.h data_source.cc @@ -20,3 +20,16 @@ libdatasrc_la_SOURCES += zonetable.h zonetable.cc libdatasrc_la_SOURCES += memory_datasrc.h memory_datasrc.cc libdatasrc_la_SOURCES += zone.h libdatasrc_la_SOURCES += result.h +libdatasrc_la_SOURCES += logger.h logger.cc +nodist_libdatasrc_la_SOURCES = messagedef.h messagedef.cc + +libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la +libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la +libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la +libdatasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la + +BUILT_SOURCES = messagedef.h messagedef.cc +messagedef.h messagedef.cc: Makefile messagedef.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/datasrc/messagedef.mes + +EXTRA_DIST = messagedef.mes diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc index 6fff754956..8e9487dfa7 100644 --- a/src/lib/datasrc/cache.cc +++ b/src/lib/datasrc/cache.cc @@ -24,6 +24,7 @@ #include #include +#include using namespace std; using namespace isc::dns; @@ -204,16 +205,21 @@ public: // HotCacheImpl constructor HotCacheImpl::HotCacheImpl(int slots, bool enabled) : enabled_(enabled), slots_(slots), count_(0) -{} +{ + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_CREATE); +} // Insert a cache node into the cache inline void HotCacheImpl::insert(const CacheNodePtr node) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_INSERT). + arg(node->getRRset()->getName()); std::map::const_iterator iter; iter = map_.find(node->question); if (iter != map_.end()) { CacheNodePtr old = iter->second; if (old && old->isValid()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND); remove(old); } } @@ -225,6 +231,7 @@ HotCacheImpl::insert(const CacheNodePtr node) { ++count_; if (slots_ != 0 && count_ > slots_) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FULL); remove(lru_.back()); } } @@ -245,6 +252,8 @@ HotCacheImpl::promote(CacheNodePtr node) { // Remove a node from the LRU list and the map void HotCacheImpl::remove(ConstCacheNodePtr node) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_REMOVE). + arg(node->getRRset()->getName()); lru_.erase(node->lru_entry_); map_.erase(node->question); --count_; @@ -257,6 +266,7 @@ HotCache::HotCache(const int slots) { // HotCache destructor HotCache::~HotCache() { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_DESTROY); delete impl_; } @@ -303,18 +313,21 @@ HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t, std::map::const_iterator iter; iter = impl_->map_.find(Question(n, c, t)); if (iter == impl_->map_.end()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_NOT_FOUND).arg(n); return (false); } CacheNodePtr node = iter->second; if (node->isValid()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FOUND).arg(n); impl_->promote(node); rrset = node->getRRset(); flags = node->getFlags(); return (true); } + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_EXPIRED).arg(n); impl_->remove(node); return (false); } @@ -328,6 +341,9 @@ HotCache::setSlots(const int slots) { return; } + logger.info(DATASRC_CACHE_SLOTS).arg(slots).arg(max(0, impl_->count_ - + slots)); + while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) { impl_->remove(impl_->lru_.back()); } @@ -343,6 +359,11 @@ HotCache::getSlots() const { void HotCache::setEnabled(const bool e) { impl_->enabled_ = e; + if (e) { + logger.info(DATASRC_CACHE_ENABLE); + } else { + logger.info(DATASRC_CACHE_DISABLE); + } } /// Indicate whether the cache is enabled diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc index 90e1dd71c7..4e1fcde202 100644 --- a/src/lib/datasrc/data_source.cc +++ b/src/lib/datasrc/data_source.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -83,7 +84,7 @@ class ZoneInfo { public: ZoneInfo(DataSrc* ts, const isc::dns::Name& n, - const isc::dns::RRClass& c, + const isc::dns::RRClass& c, const isc::dns::RRType& t = isc::dns::RRType::ANY()) : top_source_(ts), dsm_(((t == RRType::DS() && n.getLabelCount() != 1) @@ -123,6 +124,8 @@ getAdditional(Query& q, ConstRRsetPtr rrset) { const Rdata& rd(it->getCurrent()); if (rrset->getType() == RRType::NS()) { const generic::NS& ns = dynamic_cast(rd); + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_NS_ADDITIONAL). + arg(ns.getNSName()).arg(rrset->getName()); q.tasks().push(QueryTaskPtr( new QueryTask(q, ns.getNSName(), Message::SECTION_ADDITIONAL, @@ -130,6 +133,8 @@ getAdditional(Query& q, ConstRRsetPtr rrset) { QueryTask::GETADDITIONAL))); } else if (rrset->getType() == RRType::MX()) { const generic::MX& mx = dynamic_cast(rd); + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_MX_ADDITIONAL). + arg(mx.getMXName()).arg(rrset->getName()); q.tasks().push(QueryTaskPtr( new QueryTask(q, mx.getMXName(), Message::SECTION_ADDITIONAL, @@ -143,11 +148,14 @@ getAdditional(Query& q, ConstRRsetPtr rrset) { // understand DNAME void synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_SYNTH_CNAME). + arg(rrset->getName()); RdataIteratorPtr it = rrset->getRdataIterator(); // More than one DNAME RR in the RRset is illegal, so we only have // to process the first one. if (it->isLast()) { + logger.error(DATASRC_QUERY_EMPTY_DNAME).arg(rrset->getName()); return; } @@ -171,16 +179,20 @@ synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) { // to by a CNAME record void chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_FOLLOW_CNAME). + arg(rrset->getName()); RdataIteratorPtr it = rrset->getRdataIterator(); // More than one CNAME RR in the RRset is illegal, so we only have // to process the first one. if (it->isLast()) { + logger.error(DATASRC_QUERY_EMPTY_CNAME).arg(rrset->getName()); return; } // Stop chasing CNAMES after 16 lookups, to prevent loops if (q.tooMany()) { + logger.error(DATASRC_QUERY_TOO_MANY_CNAMES).arg(rrset->getName()); return; } @@ -194,6 +206,8 @@ chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) { // Check the cache for data which can answer the current query task. bool checkCache(QueryTask& task, RRsetList& target) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CHECK_CACHE). + arg(task.qname).arg(task.qtype); HotCache& cache = task.q.getCache(); RRsetList rrsets; RRsetPtr rrset; @@ -206,6 +220,9 @@ checkCache(QueryTask& task, RRsetList& target) { // ANY queries must be handled by the low-level data source, // or the results won't be guaranteed to be complete if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_QUERY_NO_CACHE_ANY_SIMPLE).arg(task.qname). + arg(task.qtype).arg(task.qclass); break; } @@ -235,6 +252,8 @@ checkCache(QueryTask& task, RRsetList& target) { case QueryTask::AUTH_QUERY: // Find exact RRset or CNAME if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_NO_CACHE_ANY_AUTH). + arg(task.qname).arg(task.qtype).arg(task.qclass); break; } @@ -353,30 +372,43 @@ DataSrc::Result doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) { HotCache& cache = task.q.getCache(); RRsetPtr rrset; + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_DO_QUERY).arg(task.qname). + arg(task.qtype); - // First, check the cache for matching data + // First off, make sure at least we have a matching zone in some data + // source. We must do this before checking the cache, because it can + // happen that the matching zone has been removed after an RRset of that + // zone is cached. Such inconsistency will cause various problems, + // including a crash. + const DataSrc* ds = zoneinfo.getDataSource(); + const Name* const zonename = zoneinfo.getEnclosingZone(); + if (ds == NULL) { + task.flags |= DataSrc::NO_SUCH_ZONE; + logger.info(DATASRC_QUERY_NO_ZONE).arg(task.qname).arg(task.qclass); + return (DataSrc::SUCCESS); + } + + // Then check the cache for matching data if (checkCache(task, target)) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CACHED). + arg(task.qname).arg(task.qtype); return (DataSrc::SUCCESS); } // Requested data weren't in the cache (or were, but had expired), // so now we proceed with the low-level data source lookup, and cache // whatever we find. - const DataSrc* ds = zoneinfo.getDataSource(); - const Name* const zonename = zoneinfo.getEnclosingZone(); - - if (ds == NULL) { - task.flags |= DataSrc::NO_SUCH_ZONE; - return (DataSrc::SUCCESS); - } DataSrc::Result result; switch (task.op) { case QueryTask::SIMPLE_QUERY: + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_SIMPLE). + arg(task.qname).arg(task.qtype); result = ds->findExactRRset(task.qname, task.qclass, task.qtype, target, task.flags, zonename); if (result != DataSrc::SUCCESS) { + logger.error(DATASRC_QUERY_SIMPLE_FAIL).arg(result); return (result); } @@ -398,10 +430,13 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) { return (result); case QueryTask::AUTH_QUERY: + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_AUTH). + arg(task.qname).arg(task.qtype); result = ds->findRRset(task.qname, task.qclass, task.qtype, target, task.flags, zonename); if (result != DataSrc::SUCCESS) { + logger.error(DATASRC_QUERY_AUTH_FAIL).arg(result); return (result); } @@ -434,10 +469,16 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) { case QueryTask::GLUE_QUERY: case QueryTask::NOGLUE_QUERY: + LOG_DEBUG(logger, DBG_TRACE_DATA, task.op == QueryTask::GLUE_QUERY ? + DATASRC_QUERY_IS_GLUE : DATASRC_QUERY_IS_NOGLUE). + arg(task.qname).arg(task.qtype); result = ds->findAddrs(task.qname, task.qclass, target, task.flags, zonename); if (result != DataSrc::SUCCESS) { + logger.error(task.op == QueryTask::GLUE_QUERY ? + DATASRC_QUERY_GLUE_FAIL : DATASRC_QUERY_NOGLUE_FAIL). + arg(result); return (result); } @@ -463,10 +504,13 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) { return (result); case QueryTask::REF_QUERY: + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_REF). + arg(task.qname).arg(task.qtype); result = ds->findReferral(task.qname, task.qclass, target, task.flags, zonename); if (result != DataSrc::SUCCESS) { + logger.error(DATASRC_QUERY_REF_FAIL).arg(result); return (result); } @@ -500,6 +544,7 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) { } // Not reached + logger.error(DATASRC_QUERY_INVALID_OP); return (DataSrc::ERROR); } @@ -511,6 +556,8 @@ inline void addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset, bool no_dnssec = false) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_RRSET). + arg(rrset->getName()).arg(rrset->getType()); Message& m = q.message(); if (no_dnssec) { if (rrset->getType() == RRType::RRSIG() || @@ -529,6 +576,7 @@ addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset, // Copy referral information into the authority section of a message inline void copyAuth(Query& q, RRsetList& auth) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_COPY_AUTH); BOOST_FOREACH(RRsetPtr rrset, auth) { if (rrset->getType() == RRType::DNAME()) { continue; @@ -566,6 +614,9 @@ refQuery(const Query& q, const Name& name, ZoneInfo& zoneinfo, // they'll be handled in a normal lookup in the zone. inline bool hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_DELEGATION). + arg(task->qname); + const Name* const zonename = zoneinfo.getEnclosingZone(); if (zonename == NULL) { if (task->state == QueryTask::GETANSWER) { @@ -631,6 +682,7 @@ addSOA(Query& q, ZoneInfo& zoneinfo) { RRsetList soa; const Name* const zonename = zoneinfo.getEnclosingZone(); + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_SOA).arg(*zonename); QueryTask newtask(q, *zonename, RRType::SOA(), QueryTask::SIMPLE_QUERY); RETERR(doQueryTask(newtask, zoneinfo, soa)); if (newtask.flags != 0) { @@ -644,6 +696,7 @@ addSOA(Query& q, ZoneInfo& zoneinfo) { inline DataSrc::Result addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC).arg(name); RRsetList nsec; QueryTask newtask(q, name, RRType::NSEC(), QueryTask::SIMPLE_QUERY); @@ -660,9 +713,11 @@ inline DataSrc::Result getNsec3(Query& q, ZoneInfo& zoneinfo, string& hash, RRsetPtr& target) { const DataSrc* ds = zoneinfo.getDataSource(); const Name* const zonename = zoneinfo.getEnclosingZone(); + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC3).arg(*zonename); if (ds == NULL) { q.message().setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_NO_DS_NSEC3).arg(*zonename); return (DataSrc::ERROR); } @@ -765,6 +820,7 @@ proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) { const DataSrc* ds = zoneinfo.getDataSource(); if (ds == NULL) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_NO_DS_NSEC).arg(*zonename); return (DataSrc::ERROR); } ds->findPreviousName(task->qname, nsecname, zonename); @@ -793,6 +849,7 @@ proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) { // Attempt a wildcard lookup inline DataSrc::Result tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_WILDCARD).arg(task->qname); Message& m = q.message(); DataSrc::Result result; found = false; @@ -846,6 +903,8 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) { result = proveNX(q, task, zoneinfo, true); if (result != DataSrc::SUCCESS) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_WILDCARD_PROVENX_FAIL). + arg(task->qname).arg(result); return (DataSrc::ERROR); } } @@ -868,6 +927,8 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) { RRsetList auth; if (!refQuery(q, *zonename, zoneinfo, auth)) { + logger.error(DATASRC_QUERY_WILDCARD_REFERRAL).arg(task->qname). + arg(result); return (DataSrc::ERROR); } @@ -883,6 +944,8 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) { // void DataSrc::doQuery(Query& q) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_PROCESS).arg(q.qname()). + arg(q.qclass()); Message& m = q.message(); vector additional; @@ -900,6 +963,7 @@ DataSrc::doQuery(Query& q) { // Can't query directly for RRSIG. if (task->qtype == RRType::RRSIG()) { m.setRcode(Rcode::REFUSED()); + logger.warn(DATASRC_QUERY_RRSIG).arg(task->qname); return; } @@ -907,6 +971,7 @@ DataSrc::doQuery(Query& q) { if (task->op == QueryTask::SIMPLE_QUERY || task->op == QueryTask::REF_QUERY) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_MISPLACED_TASK); return; } @@ -926,6 +991,7 @@ DataSrc::doQuery(Query& q) { result = doQueryTask(*task, zoneinfo, data); if (result != SUCCESS) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_TASK_FAIL).arg(result); return; } @@ -935,6 +1001,7 @@ DataSrc::doQuery(Query& q) { if (task->flags == NO_SUCH_ZONE) { if (task->state == QueryTask::GETANSWER) { m.setRcode(Rcode::REFUSED()); + // No need to log it here, it was already logged in doQueryTask return; } continue; @@ -976,6 +1043,7 @@ DataSrc::doQuery(Query& q) { RRsetList auth; if (!refQuery(q, Name(*zonename), zoneinfo, auth) || !findRRsetFromList(auth, RRType::NS())) { + logger.error(DATASRC_QUERY_MISSING_NS).arg(*zonename); isc_throw(DataSourceError, "NS RR not found in " << *zonename << "/" << q.qclass()); @@ -1000,10 +1068,12 @@ DataSrc::doQuery(Query& q) { continue; default: + logger.error(DATASRC_UNEXPECTED_QUERY_STATE); isc_throw (Unexpected, "unexpected query state"); } } else if (result == ERROR || result == NOT_IMPLEMENTED) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_FAIL); return; } else if ((task->flags & CNAME_FOUND) != 0) { // The qname node contains a CNAME. Add a new task to the @@ -1021,6 +1091,7 @@ DataSrc::doQuery(Query& q) { m.setHeaderFlag(Message::HEADERFLAG_AA, false); if (!refQuery(q, task->qname, zoneinfo, auth)) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_BAD_REFERRAL).arg(task->qname); return; } BOOST_FOREACH (RRsetPtr rrset, auth) { @@ -1052,6 +1123,7 @@ DataSrc::doQuery(Query& q) { result = tryWildcard(q, task, zoneinfo, wildcard_found); if (result != SUCCESS) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_WILDCARD_FAIL).arg(task->qname); return; } @@ -1073,6 +1145,7 @@ DataSrc::doQuery(Query& q) { result = addSOA(q, zoneinfo); if (result != SUCCESS) { + logger.error(DATASRC_QUERY_MISSING_SOA).arg(*zonename); isc_throw(DataSourceError, "SOA RR not found in " << *zonename << "/" << q.qclass()); @@ -1089,6 +1162,7 @@ DataSrc::doQuery(Query& q) { result = proveNX(q, task, zoneinfo, false); if (result != DataSrc::SUCCESS) { m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_PROVENX_FAIL).arg(task->qname); return; } } @@ -1097,6 +1171,7 @@ DataSrc::doQuery(Query& q) { } else { // Should never be reached! m.setRcode(Rcode::SERVFAIL()); + logger.error(DATASRC_QUERY_UNKNOWN_RESULT); return; } } @@ -1192,7 +1267,10 @@ DataSrc::findReferral(const Name& qname, const RRClass& qclass, void MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_ADD); if (getClass() != RRClass::ANY() && data_src->getClass() != getClass()) { + logger.error(DATASRC_META_ADD_CLASS_MISMATCH). + arg(data_src->getClass()).arg(getClass()); isc_throw(Unexpected, "class mismatch"); } @@ -1201,6 +1279,7 @@ MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) { void MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_REMOVE); std::vector::iterator it, itr; for (it = data_sources.begin(); it != data_sources.end(); ++it) { if (*it == data_src) { diff --git a/src/lib/datasrc/logger.cc b/src/lib/datasrc/logger.cc new file mode 100644 index 0000000000..846e43b4b0 --- /dev/null +++ b/src/lib/datasrc/logger.cc @@ -0,0 +1,23 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +namespace isc { +namespace datasrc { + +isc::log::Logger logger("datasrc"); + +} +} diff --git a/src/lib/datasrc/logger.h b/src/lib/datasrc/logger.h new file mode 100644 index 0000000000..7c2828dea6 --- /dev/null +++ b/src/lib/datasrc/logger.h @@ -0,0 +1,46 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __DATASRC_LOGGER_H +#define __DATASRC_LOGGER_H + +#include +#include + +/// \file logger.h +/// \brief Data Source library global logger +/// +/// This holds the logger for the data source library. It is a private header +/// and should not be included in any publicly used header, only in local +/// cc files. + +namespace isc { +namespace datasrc { + +/// \brief The logger for this library +extern isc::log::Logger logger; + +enum { + /// \brief Trace basic operations + DBG_TRACE_BASIC = 10, + /// \brief Trace data changes and lookups as well + DBG_TRACE_DATA = 20, + /// \brief Detailed even about how the lookups happen + DBG_TRACE_DETAILED = 50 +}; + +} +} + +#endif diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 5230ced0db..3c57d1b087 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/src/lib/datasrc/memory_datasrc.cc @@ -24,6 +24,7 @@ #include #include +#include using namespace std; using namespace isc::dns; @@ -94,6 +95,8 @@ struct MemoryZone::MemoryZoneImpl { l > origin_labels; --l, wname = wname.split(1)) { if (wname.isWildcard()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_WILDCARD). + arg(name); // Ensure a separate level exists for the "wildcarding" name, // and mark the node as "wild". DomainNode* node; @@ -130,10 +133,13 @@ struct MemoryZone::MemoryZoneImpl { // (depending on how we support DNSSEC). We should revisit it // at that point. if (!domain->empty()) { + LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY). + arg(rrset->getName()); isc_throw(AddError, "CNAME can't be added with other data for " << rrset->getName()); } } else if (domain->find(RRType::CNAME()) != domain->end()) { + LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset->getName()); isc_throw(AddError, "CNAME and " << rrset->getType() << " can't coexist for " << rrset->getName()); } @@ -151,6 +157,7 @@ struct MemoryZone::MemoryZoneImpl { (rrset->getType() == RRType::NS() && domain->find(RRType::DNAME()) != domain->end()))) { + LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset->getName()); isc_throw(AddError, "DNAME can't coexist with NS in non-apex " "domain " << rrset->getName()); } @@ -172,6 +179,8 @@ struct MemoryZone::MemoryZoneImpl { // XXX: this is not only for CNAME or DNAME. We should generalize // this code for all other "singleton RR types" (such as SOA) in a // separate task. + LOG_ERROR(logger, DATASRC_MEM_SINGLETON).arg(rrset->getName()). + arg(rrset->getType()); isc_throw(AddError, "multiple RRs of singleton type for " << rrset->getName()); } @@ -180,6 +189,8 @@ struct MemoryZone::MemoryZoneImpl { if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN && compare.getRelation() != NameComparisonResult::EQUAL) { + LOG_ERROR(logger, DATASRC_MEM_OUT_OF_ZONE).arg(rrset->getName()). + arg(origin_); isc_throw(OutOfZone, "The name " << rrset->getName() << " is not contained in zone " << origin_); } @@ -194,10 +205,14 @@ struct MemoryZone::MemoryZoneImpl { // behavior. if (rrset->getName().isWildcard()) { if (rrset->getType() == RRType::NS()) { + LOG_ERROR(logger, DATASRC_MEM_WILDCARD_NS). + arg(rrset->getName()); isc_throw(AddError, "Invalid NS owner name (wildcard): " << rrset->getName()); } if (rrset->getType() == RRType::DNAME()) { + LOG_ERROR(logger, DATASRC_MEM_WILDCARD_DNAME). + arg(rrset->getName()); isc_throw(AddError, "Invalid DNAME owner name (wildcard): " << rrset->getName()); } @@ -210,6 +225,8 @@ struct MemoryZone::MemoryZoneImpl { */ // Implementation of MemoryZone::add result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET). + arg(rrset->getName()).arg(rrset->getType()).arg(origin_); // Sanitize input addValidation(rrset); @@ -271,6 +288,8 @@ struct MemoryZone::MemoryZoneImpl { void addFromLoad(const ConstRRsetPtr& set, DomainTree* domains) { switch (add(set, domains)) { case result::EXIST: + LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET). + arg(set->getName()).arg(set->getType()); isc_throw(dns::MasterLoadError, "Duplicate rrset: " << set->toText()); case result::SUCCESS: @@ -307,6 +326,8 @@ struct MemoryZone::MemoryZoneImpl { const Domain::const_iterator foundDNAME(node.getData()->find( RRType::DNAME())); if (foundDNAME != node.getData()->end()) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, + DATASRC_MEM_DNAME_ENCOUNTERED); state->dname_node_ = &node; state->rrset_ = foundDNAME->second; // No more processing below the DNAME (RFC 2672, section 3 @@ -328,6 +349,8 @@ struct MemoryZone::MemoryZoneImpl { return (false); } + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED); + // BIND 9 checks if this node is not the origin. That's probably // because it can support multiple versions for dynamic updates // and IXFR, and it's possible that the callback is called at @@ -363,6 +386,8 @@ struct MemoryZone::MemoryZoneImpl { rrset, bool rename) { if (rename) { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME). + arg(rrset->getName()).arg(name); /* * We lose a signature here. But it would be wrong anyway, because * the name changed. This might turn out to be unimportant in @@ -385,6 +410,8 @@ struct MemoryZone::MemoryZoneImpl { FindResult find(const Name& name, RRType type, RRsetList* target, const FindOptions options) const { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name). + arg(type); // Get the node DomainNode* node(NULL); FindState state(options); @@ -411,12 +438,16 @@ struct MemoryZone::MemoryZoneImpl { * is NULL. */ if (state.dname_node_ != NULL) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND). + arg(state.rrset_->getName()); // We were traversing a DNAME node (and wanted to go // lower below it), so return the DNAME return (FindResult(DNAME, prepareRRset(name, state.rrset_, rename))); } if (state.zonecut_node_ != NULL) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND). + arg(state.rrset_->getName()); return (FindResult(DELEGATION, prepareRRset(name, state.rrset_, rename))); } @@ -426,6 +457,8 @@ struct MemoryZone::MemoryZoneImpl { // the zone but is empty. Treat it as NXRRSET. if (node_path.getLastComparisonResult().getRelation() == NameComparisonResult::SUPERDOMAIN) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP). + arg(node_path.getAbsoluteName()).arg(name); return (FindResult(NXRRSET, ConstRRsetPtr())); } @@ -463,6 +496,8 @@ struct MemoryZone::MemoryZoneImpl { if (node_path.getLastComparisonResult().getRelation() == NameComparisonResult::COMMONANCESTOR && node_path. getLastComparisonResult().getCommonLabels() > 1) { + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_MEM_WILDCARD_CANCEL).arg(name); return (FindResult(NXDOMAIN, ConstRRsetPtr())); } Name wildcard(Name("*").concatenate( @@ -485,6 +520,8 @@ struct MemoryZone::MemoryZoneImpl { // fall through case DomainTree::NOTFOUND: + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOTFOUND). + arg(name); return (FindResult(NXDOMAIN, ConstRRsetPtr())); case DomainTree::EXACTMATCH: // This one is OK, handle it break; @@ -496,6 +533,8 @@ struct MemoryZone::MemoryZoneImpl { // If there is an exact match but the node is empty, it's equivalent // to NXRRSET. if (node->isEmpty()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY). + arg(name); return (FindResult(NXRRSET, ConstRRsetPtr())); } @@ -506,6 +545,8 @@ struct MemoryZone::MemoryZoneImpl { if (node->getFlag(DomainNode::FLAG_CALLBACK) && node != origin_data_) { found = node->getData()->find(RRType::NS()); if (found != node->getData()->end()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, + DATASRC_MEM_EXACT_DELEGATION).arg(name); return (FindResult(DELEGATION, prepareRRset(name, found->second, rename))); } @@ -521,23 +562,30 @@ struct MemoryZone::MemoryZoneImpl { boost::const_pointer_cast(prepareRRset(name, found->second, rename))); } + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS). + arg(name); return (FindResult(SUCCESS, ConstRRsetPtr())); } found = node->getData()->find(type); if (found != node->getData()->end()) { // Good, it is here + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name). + arg(type); return (FindResult(SUCCESS, prepareRRset(name, found->second, rename))); } else { // Next, try CNAME. found = node->getData()->find(RRType::CNAME()); if (found != node->getData()->end()) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name); return (FindResult(CNAME, prepareRRset(name, found->second, rename))); } } // No exact match or CNAME. Return NXRRSET. + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type). + arg(name); return (FindResult(NXRRSET, ConstRRsetPtr())); } }; @@ -545,9 +593,13 @@ struct MemoryZone::MemoryZoneImpl { MemoryZone::MemoryZone(const RRClass& zone_class, const Name& origin) : impl_(new MemoryZoneImpl(zone_class, origin)) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin). + arg(zone_class); } MemoryZone::~MemoryZone() { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()). + arg(getClass()); delete impl_; } @@ -576,6 +628,8 @@ MemoryZone::add(const ConstRRsetPtr& rrset) { void MemoryZone::load(const string& filename) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()). + arg(filename); // Load it into a temporary tree MemoryZoneImpl::DomainTree tmp; masterLoad(filename.c_str(), getOrigin(), getClass(), @@ -588,6 +642,8 @@ MemoryZone::load(const string& filename) { void MemoryZone::swap(MemoryZone& zone) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()). + arg(zone.getOrigin()); std::swap(impl_, zone.impl_); } @@ -628,6 +684,9 @@ MemoryDataSrc::addZone(ZonePtr zone) { "Null pointer is passed to MemoryDataSrc::addZone()"); } + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE). + arg(zone->getOrigin()).arg(zone->getClass().toText()); + const result::Result result = impl_->zone_table.addZone(zone); if (result == result::SUCCESS) { ++impl_->zone_count; @@ -637,6 +696,7 @@ MemoryDataSrc::addZone(ZonePtr zone) { MemoryDataSrc::FindResult MemoryDataSrc::findZone(const isc::dns::Name& name) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(name); return (FindResult(impl_->zone_table.findZone(name).code, impl_->zone_table.findZone(name).zone)); } diff --git a/src/lib/datasrc/messagedef.mes b/src/lib/datasrc/messagedef.mes new file mode 100644 index 0000000000..2fc5c6bbae --- /dev/null +++ b/src/lib/datasrc/messagedef.mes @@ -0,0 +1,498 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +$PREFIX DATASRC_ +$NAMESPACE isc::datasrc + +# \brief Messages for the data source library + +% CACHE_CREATE creating the hotspot cache +Debug information that the hotspot cache was created at startup. + +% CACHE_DESTROY destroying the hotspot cache +Debug information. The hotspot cache is being destroyed. + +% CACHE_INSERT inserting item '%1' into the cache +Debug information. It means a new item is being inserted into the hotspot +cache. + +% CACHE_OLD_FOUND older instance of cache item found, replacing +Debug information. While inserting an item into the hotspot cache, an older +instance of an item with the same name was found. The old instance will be +removed. This should be directly followed by CACHE_REMOVE. + +% CACHE_FULL cache is full, dropping oldest +Debug information. After inserting an item into the hotspot cache, the +maximum number of items was exceeded, so the least recently used item will +be dropped. This should be directly followed by CACHE_REMOVE. + +% CACHE_REMOVE removing '%1' from the cache +Debug information. An item is being removed from the hotspot cache. + +% CACHE_NOT_FOUND the item '%1' was not found +Debug information. It was attempted to look up an item in the hotspot cache, +but it is not there. + +% CACHE_FOUND the item '%1' was found +Debug information. An item was successfully looked up in the hotspot cache. + +% CACHE_EXPIRED the item '%1' is expired +Debug information. There was an attempt to look up an item in the hotspot +cache. And the item was actually there, but it was too old, so it was removed +instead and nothing is reported (the external behaviour is the same as with +CACHE_NOT_FOUND). + +% CACHE_SLOTS setting the cache size to '%1', dropping '%2' items +The maximum allowed number of items of the hotspot cache is set to the given +number. If there are too many, some of them will be dropped. The size of 0 +means no limit. + +% CACHE_ENABLE enabling the cache +The hotspot cache is enabled from now on. + +% CACHE_DISABLE disabling the cache +The hotspot cache is disabled from now on. It is not going to store +information or return anything. + +% QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1' +Debug information. While answering a query, a DNAME was met. The DNAME itself +will be returned, but along with it a CNAME for clients which don't understand +DNAMEs will be synthesized. + +% QUERY_EMPTY_DNAME the DNAME on '%1' is empty +During an attempt to synthesize CNAME from this DNAME it was discovered the +DNAME is empty (it has no records). This indicates problem with supplied data. + +% QUERY_GET_NS_ADDITIONAL addition of A/AAAA for '%1' requested by NS '%2' +Debug information. While processing a query, a NS record was met. It +references the mentioned address, so A/AAAA records for it are looked up +and put it into the additional section. + +% QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2' +Debug information. While processing a query, a MX record was met. It +references the mentioned address, so A/AAAA records for it are looked up +and put it into the additional section. + +% QUERY_FOLLOW_CNAME following CNAME at '%1' +Debug information. The domain is a CNAME (or a DNAME and we created a CNAME +for it already), so it's being followed. + +% QUERY_EMPTY_CNAME cNAME at '%1' is empty +There was an CNAME and it was being followed. But it contains no records, +so there's nowhere to go. There will be no answer. This indicates a problem +with supplied data. +We tried to follow + +% QUERY_TOO_MANY_CNAMES cNAME chain limit exceeded at '%1' +A CNAME led to another CNAME and it led to another, and so on. After 16 +CNAMEs, the software gave up. Long CNAME chains are discouraged, and this +might possibly be a loop as well. Note that some of the CNAMEs might have +been synthesized from DNAMEs. This indicates problem with supplied data. + +% QUERY_CHECK_CACHE checking cache for '%1/%2' +Debug information. While processing a query, lookup to the hotspot cache +is being made. + +% QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class) +Debug information. The hotspot cache is ignored for ANY queries for consistency +reasons. + +% QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class) +Debug information. The hotspot cache is ignored for authoritative ANY queries +for consistency reasons. + +% DO_QUERY handling query for '%1/%2' +Debug information. We're processing some internal query for given name and +type. + +% QUERY_NO_ZONE no zone containing '%1' in class '%2' +Lookup of domain failed because the data have no zone that contain the +domain. Maybe someone sent a query to the wrong server for some reason. + +% QUERY_CACHED data for %1/%2 found in cache +Debug information. The requested data were found in the hotspot cache, so +no query is sent to the real data source. + +% QUERY_IS_SIMPLE simple query (%1/%2) +Debug information. The last DO_QUERY is a simple query. + +% QUERY_IS_AUTH auth query (%1/%2) +Debug information. The last DO_QUERY is an auth query. + +% QUERY_IS_GLUE glue query (%1/%2) +Debug information. The last DO_QUERY is query for glue addresses. + +% QUERY_IS_NOGLUE query for non-glue addresses (%1/%2) +Debug information. The last DO_QUERY is query for addresses that are not +glue. + +% QUERY_IS_REF query for referral (%1/%2) +Debug information. The last DO_QUERY is query for referral information. + +% QUERY_SIMPLE_FAIL the underlying data source failed with %1 +The underlying data source failed to answer the simple query. 1 means some +error, 2 is not implemented. The data source should have logged the specific +error already. + +% QUERY_AUTH_FAIL the underlying data source failed with %1 +The underlying data source failed to answer the authoritative query. 1 means +some error, 2 is not implemented. The data source should have logged the +specific error already. + +% QUERY_GLUE_FAIL the underlying data source failed with %1 +The underlying data source failed to answer the glue query. 1 means some error, +2 is not implemented. The data source should have logged the specific error +already. + +% QUERY_NOGLUE_FAIL the underlying data source failed with %1 +The underlying data source failed to answer the no-glue query. 1 means some +error, 2 is not implemented. The data source should have logged the specific +error already. + +% QUERY_REF_FAIL the underlying data source failed with %1 +The underlying data source failed to answer the query for referral information. +1 means some error, 2 is not implemented. The data source should have logged +the specific error already. + +% QUERY_INVALID_OP invalid query operation requested +This indicates a programmer error. The DO_QUERY was called with unknown +operation code. + +% QUERY_ADD_RRSET adding RRset '%1/%2' to message +Debug information. An RRset is being added to the response message. + +% QUERY_COPY_AUTH copying authoritative section into message +Debug information. The whole referral information is being copied into the +response message. + +% QUERY_DELEGATION looking for delegation on the path to '%1' +Debug information. The software is trying to identify delegation points on the +way down to the given domain. + +% QUERY_ADD_SOA adding SOA of '%1' +Debug information. A SOA record of the given zone is being added to the +authority section of the response message. + +% QUERY_ADD_NSEC adding NSEC record for '%1' +Debug information. A NSEC record covering this zone is being added. + +% QUERY_ADD_NSEC3 adding NSEC3 record of zone '%1' +Debug information. A NSEC3 record for the given zone is being added to the +response message. + +% QUERY_NO_DS_NSEC3 there's no DS record in the '%1' zone +An attempt to add a NSEC3 record into the message failed, because the zone does +not have any DS record. This indicates problem with the provided data. + +% QUERY_NO_DS_NSEC there's no DS record in the '%1' zone +An attempt to add a NSEC record into the message failed, because the zone does +not have any DS record. This indicates problem with the provided data. + +% QUERY_WILDCARD looking for a wildcard covering '%1' +Debug information. A direct match wasn't found, so a wildcard covering the +domain is being looked for now. + +% QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2) +While processing a wildcard, it wasn't possible to prove nonexistence of the +given domain or record. The code is 1 for error and 2 for not implemented. + +% QUERY_WILDCARD_REFERRAL unable to find referral info for '%1' (%2) +While processing a wildcard, a referral was met. But it wasn't possible to get +enough information for it. The code is 1 for error, 2 for not implemented. + +% QUERY_PROCESS processing query '%1/%2' in the '%3' class +Debug information. A sure query is being processed now. + +% QUERY_RRSIG unable to answer RRSIG query +The server is unable to answer a direct query for RRSIG type, but was asked +to do so. + +% QUERY_MISPLACED_TASK task of this type should not be here +This indicates a programming error. A task was found in the internal task +queue, but this kind of task wasn't designed to be inside the queue (it should +be handled right away, not queued). + +% QUERY_TASK_FAIL task failed with %1 +The query subtask failed. The reason should have been reported by the subtask +already. The code is 1 for error, 2 for not implemented. + +% QUERY_MISSING_NS missing NS records for '%1' +NS records should have been put into the authority section. However, this zone +has none. This indicates problem with provided data. + +% UNEXPECTED_QUERY_STATE unexpected query state +This indicates a programming error. An internal task of unknown type was +generated. + +% QUERY_FAIL query failed +Some subtask of query processing failed. The reason should have been reported +already. We are returning SERVFAIL. + +% QUERY_BAD_REFERRAL bad referral to '%1' +The domain lives in another zone. But it is not possible to generate referral +information for it. + +% QUERY_WILDCARD_FAIL error processing wildcard for '%1' +During an attempt to cover the domain by a wildcard an error happened. The +exact kind was hopefully already reported. + +% QUERY_MISSING_SOA the zone '%1' has no SOA +The answer should have been a negative one (eg. of nonexistence of something). +To do so, a SOA record should be put into the authority section, but the zone +does not have one. This indicates problem with provided data. + +% QUERY_PROVENX_FAIL unable to prove nonexistence of '%1' +The user wants DNSSEC and we discovered the entity doesn't exist (either +domain or the record). But there was an error getting NSEC/NSEC3 record +to prove the nonexistence. + +% QUERY_UNKNOWN_RESULT unknown result of subtask +This indicates a programmer error. The answer of subtask doesn't look like +anything known. + +% META_ADD adding a data source into meta data source +Debug information. Yet another data source is being added into the meta data +source. (probably at startup or reconfiguration) + +% META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2' +It was attempted to add a data source into a meta data source. But their +classes do not match. + +% META_REMOVE removing data source from meta data source +Debug information. A data source is being removed from meta data source. + +% MEM_ADD_WILDCARD adding wildcards for '%1' +Debug information. Some special marks above each * in wildcard name are needed. +They are being added now for this name. + +% MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1' +Someone or something tried to add a CNAME into a domain that already contains +some other data. But the protocol forbids coexistence of CNAME with anything +(RFC 1034, section 3.6.2). This indicates a problem with provided data. + +% MEM_CNAME_COEXIST can't add data to CNAME in domain '%1' +This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the +other way around -- adding some outher data to CNAME. + +% MEM_DNAME_NS dNAME and NS can't coexist in non-apex domain '%1' +It was requested for DNAME and NS records to be put into the same domain +which is not the apex (the top of the zone). This is forbidden by RFC +2672, section 3. This indicates a problem with provided data. + +% MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2' +Some resource types are singletons -- only one is allowed in a domain +(for example CNAME or SOA). This indicates a problem with provided data. + +% MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2' +It was attempted to add the domain into a zone that shouldn't have it +(eg. the domain is not subdomain of the zone origin). This indicates a +problem with provided data. + +% MEM_WILDCARD_NS nS record in wildcard domain '%1' +The software refuses to load NS records into a wildcard domain. It isn't +explicitly forbidden, but the protocol is ambiguous about how this should +behave and BIND 9 refuses that as well. Please describe your intention using +different tools. + +% MEM_WILDCARD_DNAME dNAME record in wildcard domain '%1' +The software refuses to load DNAME records into a wildcard domain. It isn't +explicitly forbidden, but the protocol is ambiguous about how this should +behave and BIND 9 refuses that as well. Please describe your intention using +different tools. + +% MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3' +Debug information. An RRset is being added to the in-memory data source. + +% MEM_DUP_RRSET duplicate RRset '%1/%2' +An RRset is being inserted into in-memory data source for a second time. The +original version must be removed first. Note that loading master files where an +RRset is split into multiple locations is not supported yet. + +% MEM_DNAME_ENCOUNTERED encountered a DNAME +Debug information. While searching for the requested domain, a DNAME was +encountered on the way. This may lead to redirection to a different domain and +stop the search. + +% MEM_NS_ENCOUNTERED encountered a NS +Debug information. While searching for the requested domain, a NS was +encountered on the way (a delegation). This may lead to stop of the search. + +% MEM_RENAME renaming RRset from '%1' to '%2' +Debug information. A RRset is being generated from a different RRset (most +probably a wildcard). So it must be renamed to whatever the user asked for. In +fact, it's impossible to rename RRsets with our libraries, so a new one is +created and all resource records are copied over. + +% MEM_FIND find '%1/%2' +Debug information. A search for the requested RRset is being started. + +% MEM_DNAME_FOUND DNAME found at '%1' +Debug information. A DNAME was found instead of the requested information. + +% MEM_DELEG_FOUND delegation found at '%1' +Debug information. A delegation point was found above the requested record. + +% MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty +Debug information. The search stopped at a superdomain of the requested +domain. The domain is a empty nonterminal, therefore it is treated as NXRRSET +case (eg. the domain exists, but it doesn't have the requested record type). + +% MEM_WILDCARD_CANCEL wildcard match canceled for '%1' +Debug information. A domain above wildcard was reached, but there's something +below the requested domain. Therefore the wildcard doesn't apply here. This +behaviour is specified by RFC 1034, section 4.3.3 + +% MEM_NOTFOUND requested domain '%1' not found +Debug information. The requested domain does not exist. + +% MEM_DOMAIN_EMPTY requested domain '%1' is empty +Debug information. The requested domain exists in the tree of domains, but +it is empty. Therefore it doesn't contain the requested resource type. + +% MEM_EXACT_DELEGATION delegation at the exact domain '%1' +Debug information. There's a NS record at the requested domain. This means +this zone is not authoritative for the requested domain, but a delegation +should be followed. The requested domain is an apex of some zone. + +% MEM_ANY_SUCCESS ANY query for '%1' successful +Debug information. The domain was found and an ANY type query is being answered +by providing everything found inside the domain. + +% MEM_SUCCESS query for '%1/%2' successful +Debug information. The requested record was found. + +% MEM_CNAME CNAME at the domain '%1' +Debug information. The requested domain is an alias to a different domain, +returning the CNAME instead. + +% MEM_NXRRSET no such type '%1' at '%2' +Debug information. The domain exists, but it doesn't hold any record of the +requested type. + +% MEM_CREATE creating zone '%1' in '%2' class +Debug information. A representation of a zone for the in-memory data source is +being created. + +% MEM_DESTROY destroying zone '%1' in '%2' class +Debug information. A zone from in-memory data source is being destroyed. + +% MEM_LOAD loading zone '%1' from file '%2' +Debug information. The content of master file is being loaded into the memory. + +% MEM_SWAP swapping contents of two zone representations ('%1' and '%2') +Debug information. The contents of two in-memory zones are being exchanged. +This is usual practice to do some manipulation in exception-safe manner -- the +new data are prepared in a different zone object and when it works, they are +swapped. The old one contains the new data and the other one can be safely +destroyed. + +% MEM_ADD_ZONE adding zone '%1/%2' +Debug information. A zone is being added into the in-memory data source. + +% MEM_FIND_ZONE looking for zone '%1' +Debug information. A zone object for this zone is being searched for in the +in-memory data source. + +% STATIC_CREATE creating the static datasource +Debug information. The static data source (the one holding stuff like +version.bind) is being created. + +% STATIC_BAD_CLASS static data source can handle CH only +For some reason, someone asked the static data source a query that is not in +the CH class. + +% STATIC_FIND looking for '%1/%2' +Debug information. This resource record set is being looked up in the static +data source. + +% SQLITE_FINDREC looking for record '%1/%2' +Debug information. The SQLite data source is looking up records of given name +and type in the database. + +% SQLITE_ENCLOSURE looking for zone containing '%1' +Debug information. The SQLite data source is trying to identify, which zone +should hold this domain. + +% SQLITE_ENCLOSURE_BAD_CLASS class mismatch looking for a zone ('%1' and '%2') +The SQLite data source can handle only one class at a time and it was asked +to identify which zone is holding data of a different class. + +% SQLITE_ENCLOSURE_NOTFOUND no zone contains it +Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's +no such zone in our data. + +% SQLITE_PREVIOUS looking for name previous to '%1' +Debug information. We're trying to look up name preceding the supplied one. + +% SQLITE_PREVIOUS_NO_ZONE no zone containing '%1' +The SQLite data source tried to identify name preceding this one. But this +one is not contained in any zone in the data source. + +% SQLITE_FIND_NSEC3 looking for NSEC3 in zone '%1' for hash '%2' +Debug information. We're trying to look up a NSEC3 record in the SQLite data +source. + +% SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1' +The SQLite data source was asked to provide a NSEC3 record for given zone. +But it doesn't contain that zone. + +% SQLITE_FIND looking for RRset '%1/%2' +Debug information. The SQLite data source is looking up a resource record +set. + +% SQLITE_FIND_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2') +The SQLite data source was looking up an RRset, but the data source contains +different class than the query was for. + +% SQLITE_FINDEXACT looking for exact RRset '%1/%2' +Debug information. The SQLite data source is looking up an exact resource +record. + +% SQLITE_FINDEXACT_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2') +The SQLite data source was looking up an exact RRset, but the data source +contains different class than the query was for. + +% SQLITE_FINDADDRS looking for A/AAAA addresses for '%1' +Debug information. The data source is looking up the addresses for given +domain name. + +% SQLITE_FINDADDRS_BAD_CLASS class mismatch looking for addresses ('%1' and '%2') +The SQLite data source was looking up A/AAAA addresses, but the data source +contains different class than the query was for. + +% SQLITE_FINDREF looking for referral at '%1' +Debug information. The SQLite data source is identifying if this domain is +a referral and where it goes. + +% SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2') +The SQLite data source was trying to identify, if there's a referral. But +it contains different class than the query was for. + +% SQLITE_CREATE sQLite data source created +Debug information. An instance of SQLite data source is being created. + +% SQLITE_DESTROY sQLite data source destroyed +Debug information. An instance of SQLite data source is being destroyed. + +% SQLITE_SETUP setting up SQLite database +The database for SQLite data source was found empty. It is assumed this is the +first run and it is being initialized with current schema. It'll still contain +no data, but it will be ready for use. + +% SQLITE_OPEN opening SQLite database '%1' +Debug information. The SQLite data source is loading an SQLite database in +the provided file. + +% SQLITE_CLOSE closing SQLite database +Debug information. The SQLite data source is closing the database file. diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc index ab910baa0f..22f035bbcb 100644 --- a/src/lib/datasrc/sqlite3_datasrc.cc +++ b/src/lib/datasrc/sqlite3_datasrc.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -227,6 +228,8 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype, RRsetList& target, const Name* zonename, const Mode mode, uint32_t& flags) const { + LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_SQLITE_FINDREC).arg(name). + arg(rdtype); flags = 0; int zone_id = (zonename == NULL) ? findClosest(name, NULL) : findClosest(*zonename, NULL); @@ -345,12 +348,17 @@ Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const { void Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE). + arg(match.getName()); if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) { + LOG_ERROR(logger, DATASRC_SQLITE_ENCLOSURE_BAD_CLASS).arg(getClass()). + arg(match.getClass()); return; } unsigned int position; if (findClosest(match.getName(), &position) == -1) { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOTFOUND); return; } @@ -362,9 +370,11 @@ Sqlite3DataSrc::findPreviousName(const Name& qname, Name& target, const Name* zonename) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_PREVIOUS).arg(qname); const int zone_id = (zonename == NULL) ? findClosest(qname, NULL) : findClosest(*zonename, NULL); if (zone_id < 0) { + LOG_ERROR(logger, DATASRC_SQLITE_PREVIOUS_NO_ZONE).arg(qname.toText()); return (ERROR); } @@ -402,8 +412,11 @@ Sqlite3DataSrc::findCoveringNSEC3(const Name& zonename, string& hashstr, RRsetList& target) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND_NSEC3). + arg(zonename).arg(hashstr); const int zone_id = findClosest(zonename, NULL); if (zone_id < 0) { + LOG_ERROR(logger, DATASRC_SQLITE_FIND_NSEC3_NO_ZONE).arg(zonename); return (ERROR); } @@ -484,7 +497,11 @@ Sqlite3DataSrc::findRRset(const Name& qname, uint32_t& flags, const Name* zonename) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND).arg(qname). + arg(qtype); if (qclass != getClass() && qclass != RRClass::ANY()) { + LOG_ERROR(logger, DATASRC_SQLITE_FIND_BAD_CLASS).arg(getClass()). + arg(qclass); return (ERROR); } findRecords(qname, qtype, target, zonename, NORMAL, flags); @@ -499,7 +516,11 @@ Sqlite3DataSrc::findExactRRset(const Name& qname, uint32_t& flags, const Name* zonename) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDEXACT).arg(qname). + arg(qtype); if (qclass != getClass() && qclass != RRClass::ANY()) { + LOG_ERROR(logger, DATASRC_SQLITE_FINDEXACT_BAD_CLASS).arg(getClass()). + arg(qclass); return (ERROR); } findRecords(qname, qtype, target, zonename, NORMAL, flags); @@ -523,7 +544,10 @@ Sqlite3DataSrc::findAddrs(const Name& qname, uint32_t& flags, const Name* zonename) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDADDRS).arg(qname); if (qclass != getClass() && qclass != RRClass::ANY()) { + LOG_ERROR(logger, DATASRC_SQLITE_FINDADDRS_BAD_CLASS).arg(getClass()). + arg(qclass); return (ERROR); } findRecords(qname, RRType::ANY(), target, zonename, ADDRESS, flags); @@ -537,8 +561,11 @@ Sqlite3DataSrc::findReferral(const Name& qname, uint32_t& flags, const Name* zonename) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDREF).arg(qname); if (qclass != getClass() && qclass != RRClass::ANY()) { - return (ERROR); + LOG_ERROR(logger, DATASRC_SQLITE_FINDREF_BAD_CLASS).arg(getClass()). + arg(qclass); + return (ERROR); } findRecords(qname, RRType::ANY(), target, zonename, DELEGATION, flags); return (SUCCESS); @@ -546,9 +573,12 @@ Sqlite3DataSrc::findReferral(const Name& qname, Sqlite3DataSrc::Sqlite3DataSrc() : dbparameters(new Sqlite3Parameters) -{} +{ + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CREATE); +} Sqlite3DataSrc::~Sqlite3DataSrc() { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DESTROY); if (dbparameters->db_ != NULL) { close(); } @@ -635,6 +665,7 @@ checkAndSetupSchema(Sqlite3Initializer* initializer) { initializer->params_.version_ = sqlite3_column_int(prepared, 0); sqlite3_finalize(prepared); } else { + logger.info(DATASRC_SQLITE_SETUP); if (prepared != NULL) { sqlite3_finalize(prepared); } @@ -664,6 +695,7 @@ checkAndSetupSchema(Sqlite3Initializer* initializer) { // void Sqlite3DataSrc::open(const string& name) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_OPEN).arg(name); if (dbparameters->db_ != NULL) { isc_throw(DataSourceError, "Duplicate SQLite open with " << name); } @@ -683,6 +715,7 @@ Sqlite3DataSrc::open(const string& name) { // DataSrc::Result Sqlite3DataSrc::close(void) { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CLOSE); if (dbparameters->db_ == NULL) { isc_throw(DataSourceError, "SQLite data source is being closed before open"); diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc index 025078a077..dee14b9d1f 100644 --- a/src/lib/datasrc/static_datasrc.cc +++ b/src/lib/datasrc/static_datasrc.cc @@ -26,6 +26,7 @@ #include #include +#include using namespace std; using namespace isc::dns; @@ -112,6 +113,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() : } StaticDataSrc::StaticDataSrc() { + LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_STATIC_CREATE); setClass(RRClass::CH()); impl_ = new StaticDataSrcImpl; } @@ -155,8 +157,11 @@ StaticDataSrc::findRRset(const Name& qname, RRsetList& target, uint32_t& flags, const Name* const zonename) const { + LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_STATIC_FIND).arg(qname). + arg(qtype); flags = 0; if (qclass != getClass() && qclass != RRClass::ANY()) { + LOG_ERROR(logger, DATASRC_STATIC_BAD_CLASS); return (ERROR); } diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am index f09b4b7610..ad4374a0cb 100644 --- a/src/lib/datasrc/tests/Makefile.am +++ b/src/lib/datasrc/tests/Makefile.am @@ -27,15 +27,17 @@ run_unittests_SOURCES += test_datasrc.h test_datasrc.cc run_unittests_SOURCES += rbtree_unittest.cc run_unittests_SOURCES += zonetable_unittest.cc run_unittests_SOURCES += memory_datasrc_unittest.cc +run_unittests_SOURCES += logger_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(GTEST_LDADD) run_unittests_LDADD += $(SQLITE_LIBS) -run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la -run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la +run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la +run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la +run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la endif noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc index 01de77abd4..cd1a40c281 100644 --- a/src/lib/datasrc/tests/datasrc_unittest.cc +++ b/src/lib/datasrc/tests/datasrc_unittest.cc @@ -1092,6 +1092,22 @@ TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) { RRClass::IN(), RRType::A()); } +TEST_F(DataSrcTest, cacheDataInNonexistentZone) { + // This test emulates the situation where an RRset in some zone of some + // data source is cached and then the zone is removed from the data source. + // When there is such a substantial inconsistency between the cache and + // the real data source, we should honor the latter. More important, + // the inconsistency shouldn't cause any disruption such as a crash. + + const Name qname("nosuchzone.example"); + RRsetPtr rrset(new RRset(qname, RRClass::IN(), RRType::A(), RRTTL(0))); + cache.addPositive(rrset, DataSrc::REFERRAL); + + createAndProcessQuery(qname, RRClass::IN(), RRType::A(), false); + headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG, + 1, 0, 0, 0); +} + // Tests of the DataSrcMatch class start here class DataSrcMatchTest : public ::testing::Test { protected: diff --git a/src/lib/datasrc/tests/logger_unittest.cc b/src/lib/datasrc/tests/logger_unittest.cc new file mode 100644 index 0000000000..df5a41c359 --- /dev/null +++ b/src/lib/datasrc/tests/logger_unittest.cc @@ -0,0 +1,31 @@ +// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include + +using namespace isc::datasrc; + +namespace { + +TEST(CacheLogger, name) { + // This does not check the name only, but the fact the logger is created + // The dot is because of empty root logger + std::string name(logger.getName()); + EXPECT_EQ(name.size() - 8, name.rfind(".datasrc")) << + "Wrong logger name: " << name; +} + +} diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index 5a7151ed0e..887ac09fee 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -17,48 +17,52 @@ EXTRA_DIST += rrtype-placeholder.h # NOTE: when an rdata file is added, please also add to this list: EXTRA_DIST += rdata/any_255/tsig_250.cc EXTRA_DIST += rdata/any_255/tsig_250.h -EXTRA_DIST += rdata/in_1/aaaa_28.cc -EXTRA_DIST += rdata/in_1/aaaa_28.h -EXTRA_DIST += rdata/in_1/a_1.cc -EXTRA_DIST += rdata/in_1/a_1.h EXTRA_DIST += rdata/ch_3/a_1.cc EXTRA_DIST += rdata/ch_3/a_1.h -EXTRA_DIST += rdata/generic/mx_15.h -EXTRA_DIST += rdata/generic/rrsig_46.cc -EXTRA_DIST += rdata/generic/dname_39.cc -EXTRA_DIST += rdata/generic/rrsig_46.h -EXTRA_DIST += rdata/generic/dname_39.h -EXTRA_DIST += rdata/generic/ns_2.cc -EXTRA_DIST += rdata/generic/nsec_47.cc -EXTRA_DIST += rdata/generic/ns_2.h -EXTRA_DIST += rdata/generic/nsec_47.h -EXTRA_DIST += rdata/generic/opt_41.cc -EXTRA_DIST += rdata/generic/soa_6.cc EXTRA_DIST += rdata/generic/cname_5.cc -EXTRA_DIST += rdata/generic/dnskey_48.cc -EXTRA_DIST += rdata/generic/opt_41.h -EXTRA_DIST += rdata/generic/soa_6.h EXTRA_DIST += rdata/generic/cname_5.h +EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc +EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h +EXTRA_DIST += rdata/generic/dname_39.cc +EXTRA_DIST += rdata/generic/dname_39.h +EXTRA_DIST += rdata/generic/dnskey_48.cc EXTRA_DIST += rdata/generic/dnskey_48.h EXTRA_DIST += rdata/generic/ds_43.cc EXTRA_DIST += rdata/generic/ds_43.h -EXTRA_DIST += rdata/generic/txt_16.cc -EXTRA_DIST += rdata/generic/txt_16.h EXTRA_DIST += rdata/generic/mx_15.cc -EXTRA_DIST += rdata/generic/nsec3param_51.h -EXTRA_DIST += rdata/generic/nsec3param_51.cc +EXTRA_DIST += rdata/generic/mx_15.h +EXTRA_DIST += rdata/generic/ns_2.cc +EXTRA_DIST += rdata/generic/ns_2.h EXTRA_DIST += rdata/generic/nsec3_50.cc EXTRA_DIST += rdata/generic/nsec3_50.h +EXTRA_DIST += rdata/generic/nsec3param_51.cc +EXTRA_DIST += rdata/generic/nsec3param_51.h +EXTRA_DIST += rdata/generic/nsec_47.cc +EXTRA_DIST += rdata/generic/nsec_47.h +EXTRA_DIST += rdata/generic/opt_41.cc +EXTRA_DIST += rdata/generic/opt_41.h EXTRA_DIST += rdata/generic/ptr_12.cc EXTRA_DIST += rdata/generic/ptr_12.h +EXTRA_DIST += rdata/generic/rp_17.cc +EXTRA_DIST += rdata/generic/rp_17.h +EXTRA_DIST += rdata/generic/rrsig_46.cc +EXTRA_DIST += rdata/generic/rrsig_46.h +EXTRA_DIST += rdata/generic/soa_6.cc +EXTRA_DIST += rdata/generic/soa_6.h +EXTRA_DIST += rdata/generic/txt_16.cc +EXTRA_DIST += rdata/generic/txt_16.h EXTRA_DIST += rdata/hs_4/a_1.cc EXTRA_DIST += rdata/hs_4/a_1.h +EXTRA_DIST += rdata/in_1/a_1.cc +EXTRA_DIST += rdata/in_1/a_1.h +EXTRA_DIST += rdata/in_1/aaaa_28.cc +EXTRA_DIST += rdata/in_1/aaaa_28.h #EXTRA_DIST += rdata/template.cc #EXTRA_DIST += rdata/template.h # auto-generate by gen-rdatacode.py: BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc -#TODO: check this###BUILT_SOURCES = rdataclass.h rdataclass.cc +BUILT_SOURCES += rdataclass.h rdataclass.cc lib_LTLIBRARIES = libdns++.la @@ -80,15 +84,22 @@ libdns___la_SOURCES += rrsetlist.h rrsetlist.cc libdns___la_SOURCES += rrttl.h rrttl.cc libdns___la_SOURCES += rrtype.cc libdns___la_SOURCES += question.h question.cc +libdns___la_SOURCES += tsig.h tsig.cc +libdns___la_SOURCES += tsigerror.h tsigerror.cc libdns___la_SOURCES += tsigkey.h tsigkey.cc +libdns___la_SOURCES += tsigrecord.h tsigrecord.cc libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc libdns___la_CPPFLAGS = $(AM_CPPFLAGS) -libdns___la_LIBADD = $(top_builddir)/src/lib/util/libutil.la +# Most applications of libdns++ will only implicitly rely on libcryptolink, +# so we add the dependency here so that the applications don't have to link +# libcryptolink explicitly. +libdns___la_LIBADD = $(top_builddir)/src/lib/cryptolink/libcryptolink.la +libdns___la_LIBADD += $(top_builddir)/src/lib/util/libutil.la -nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h -nodist_libdns___la_SOURCES += rrparamregistry.cc +nodist_libdns___include_HEADERS = rdataclass.h rrclass.h rrtype.h +nodist_libdns___la_SOURCES = rdataclass.cc rrparamregistry.cc rrclass.h: rrclass-placeholder.h rrtype.h: rrtype-placeholder.h @@ -106,13 +117,10 @@ libdns___include_HEADERS = \ question.h \ rcode.h \ rdata.h \ - rdataclass.h \ - rrclass.h \ rrparamregistry.h \ rrset.h \ rrsetlist.h \ rrttl.h \ - rrtype.h \ tsigkey.h # Purposely not installing these headers: # util/*.h: used only internally, and not actually DNS specific diff --git a/src/lib/dns/edns.cc b/src/lib/dns/edns.cc index 5405dab515..447b47986d 100644 --- a/src/lib/dns/edns.cc +++ b/src/lib/dns/edns.cc @@ -110,19 +110,25 @@ EDNS::toText() const { return (ret); } +namespace { +/// Helper function to define unified implementation for the public versions +/// of toWire(). template int -EDNS::toWire(Output& output, const uint8_t extended_rcode) const { +toWireCommon(Output& output, const uint8_t version, + const uint16_t udp_size, const bool dnssec_aware, + const uint8_t extended_rcode) +{ // Render EDNS OPT RR uint32_t extrcode_flags = extended_rcode << EXTRCODE_SHIFT; - extrcode_flags |= (version_ << VERSION_SHIFT) & VERSION_MASK; - if (dnssec_aware_) { + extrcode_flags |= (version << VERSION_SHIFT) & VERSION_MASK; + if (dnssec_aware) { extrcode_flags |= EXTFLAG_DO; } // Construct an RRset corresponding to the EDNS. // We don't support any options for now, so the OPT RR can be empty. - RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size_), + RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size), RRType::OPT(), RRTTL(extrcode_flags))); edns_rrset->addRdata(ConstRdataPtr(new generic::OPT())); @@ -130,9 +136,12 @@ EDNS::toWire(Output& output, const uint8_t extended_rcode) const { return (1); } +} unsigned int -EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const { +EDNS::toWire(AbstractMessageRenderer& renderer, + const uint8_t extended_rcode) const +{ // If adding the OPT RR would exceed the size limit, don't do it. // 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte) // (RDATA is empty in this simple implementation) @@ -140,12 +149,16 @@ EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const { return (0); } - return (toWire(renderer, extended_rcode)); + return (toWireCommon(renderer, version_, udp_size_, dnssec_aware_, + extended_rcode)); } unsigned int -EDNS::toWire(isc::util::OutputBuffer& buffer, const uint8_t extended_rcode) const { - return (toWire(buffer, extended_rcode)); +EDNS::toWire(isc::util::OutputBuffer& buffer, + const uint8_t extended_rcode) const +{ + return (toWireCommon(buffer, version_, udp_size_, dnssec_aware_, + extended_rcode)); } EDNS* diff --git a/src/lib/dns/edns.h b/src/lib/dns/edns.h index b794183ad1..a7bc4c4695 100644 --- a/src/lib/dns/edns.h +++ b/src/lib/dns/edns.h @@ -32,7 +32,7 @@ namespace dns { class EDNS; class Name; -class MessageRenderer; +class AbstractMessageRenderer; class RRClass; class RRTTL; class RRType; @@ -314,7 +314,7 @@ public: /// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as /// part of the EDNS OPT RR. /// \return 1 if the OPT RR fits in the message size limit; otherwise 0. - unsigned int toWire(MessageRenderer& renderer, + unsigned int toWire(AbstractMessageRenderer& renderer, const uint8_t extended_rcode) const; /// \brief Render the \c EDNS in the wire format. @@ -354,12 +354,6 @@ public: // something like this. //void addOption(); -private: - /// Helper method to define unified implementation for the public versions - /// of toWire(). - template - int toWire(Output& output, const uint8_t extended_rcode) const; - public: /// \brief The highest EDNS version this implementation supports. static const uint8_t SUPPORTED_VERSION = 0; @@ -386,7 +380,7 @@ private: /// /// The intended usage of this function is to parse an OPT RR of an incoming /// DNS message, while updating the RCODE of the message. -/// One common usage patter is as follows: +/// One common usage pattern is as follows: /// /// \code Message msg; /// ... diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index d1025d10ba..bf7ccd52be 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include using namespace std; using namespace boost; @@ -81,7 +83,7 @@ const unsigned int HEADERFLAG_MASK = 0x87b0; const uint16_t MESSAGE_REPLYPRESERVE = (Message::HEADERFLAG_RD | Message::HEADERFLAG_CD); -const char *sectiontext[] = { +const char* const sectiontext[] = { "QUESTION", "ANSWER", "AUTHORITY", @@ -114,8 +116,8 @@ public: vector questions_; vector rrsets_[NUM_SECTIONS]; ConstEDNSPtr edns_; + ConstTSIGRecordPtr tsig_rr_; - // tsig/sig0: TODO // RRsetsSorter* sorter_; : TODO void init(); @@ -123,6 +125,17 @@ public: void setRcode(const Rcode& rcode); int parseQuestion(InputBuffer& buffer); int parseSection(const Message::Section section, InputBuffer& buffer); + void addRR(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, ConstRdataPtr rdata); + void addEDNS(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, const Rdata& rdata); + void addTSIG(Message::Section section, unsigned int count, + const InputBuffer& buffer, size_t start_position, + const Name& name, const RRClass& rrclass, + const RRTTL& ttl, const Rdata& rdata); + void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx); }; MessageImpl::MessageImpl(Message::Mode mode) : @@ -140,6 +153,7 @@ MessageImpl::init() { rcode_ = NULL; opcode_ = NULL; edns_ = EDNSPtr(); + tsig_rr_ = ConstTSIGRecordPtr(); for (int i = 0; i < NUM_SECTIONS; ++i) { counts_[i] = 0; @@ -164,6 +178,154 @@ MessageImpl::setRcode(const Rcode& rcode) { rcode_ = &rcode_placeholder_; } +namespace { +// This helper class is used by MessageImpl::toWire() to render a set of +// RRsets of a specific section of message to a given MessageRenderer. +// +// A RenderSection object is expected to be used with a QuestionIterator or +// SectionIterator. Its operator() is called for each RRset as the iterator +// iterates over the corresponding section, and it renders the RRset to +// the given MessageRenderer, while counting the number of RRs (note: not +// RRsets) successfully rendered. If the MessageRenderer reports the need +// for truncation (via its isTruncated() method), the RenderSection object +// stops rendering further RRsets. In addition, unless partial_ok (given on +// construction) is true, it removes any RRs that are partially rendered +// from the MessageRenderer. +// +// On the completion of rendering the entire section, the owner of the +// RenderSection object can get the number of rendered RRs via the +// getTotalCount() method. +template +struct RenderSection { + RenderSection(AbstractMessageRenderer& renderer, const bool partial_ok) : + counter_(0), renderer_(renderer), partial_ok_(partial_ok), + truncated_(false) + {} + void operator()(const T& entry) { + // If it's already truncated, ignore the rest of the section. + if (truncated_) { + return; + } + const size_t pos0 = renderer_.getLength(); + counter_ += entry->toWire(renderer_); + if (renderer_.isTruncated()) { + truncated_ = true; + if (!partial_ok_) { + // roll back to the end of the previous RRset. + renderer_.trim(renderer_.getLength() - pos0); + } + } + } + unsigned int getTotalCount() { return (counter_); } + unsigned int counter_; + AbstractMessageRenderer& renderer_; + const bool partial_ok_; + bool truncated_; +}; +} + +void +MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) { + if (mode_ != Message::RENDER) { + isc_throw(InvalidMessageOperation, + "Message rendering attempted in non render mode"); + } + if (rcode_ == NULL) { + isc_throw(InvalidMessageOperation, + "Message rendering attempted without Rcode set"); + } + if (opcode_ == NULL) { + isc_throw(InvalidMessageOperation, + "Message rendering attempted without Opcode set"); + } + + // reserve room for the header + renderer.skip(HEADERLEN); + + uint16_t qdcount = + for_each(questions_.begin(), questions_.end(), + RenderSection(renderer, false)).getTotalCount(); + + // TODO: sort RRsets in each section based on configuration policy. + uint16_t ancount = 0; + if (!renderer.isTruncated()) { + ancount = + for_each(rrsets_[Message::SECTION_ANSWER].begin(), + rrsets_[Message::SECTION_ANSWER].end(), + RenderSection(renderer, true)).getTotalCount(); + } + uint16_t nscount = 0; + if (!renderer.isTruncated()) { + nscount = + for_each(rrsets_[Message::SECTION_AUTHORITY].begin(), + rrsets_[Message::SECTION_AUTHORITY].end(), + RenderSection(renderer, true)).getTotalCount(); + } + uint16_t arcount = 0; + if (renderer.isTruncated()) { + flags_ |= Message::HEADERFLAG_TC; + } else { + arcount = + for_each(rrsets_[Message::SECTION_ADDITIONAL].begin(), + rrsets_[Message::SECTION_ADDITIONAL].end(), + RenderSection(renderer, false)).getTotalCount(); + } + + // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS + // has been explicitly set. However, if the RCODE would require it and + // no EDNS has been set we generate a temporary local EDNS and use it. + if (!renderer.isTruncated()) { + ConstEDNSPtr local_edns = edns_; + if (!local_edns && rcode_->getExtendedCode() != 0) { + local_edns = ConstEDNSPtr(new EDNS()); + } + if (local_edns) { + arcount += local_edns->toWire(renderer, rcode_->getExtendedCode()); + } + } + + // Adjust the counter buffer. + // XXX: these may not be equal to the number of corresponding entries + // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR + // was inserted. This is not good, and we should revisit the entire + // design. + counts_[Message::SECTION_QUESTION] = qdcount; + counts_[Message::SECTION_ANSWER] = ancount; + counts_[Message::SECTION_AUTHORITY] = nscount; + counts_[Message::SECTION_ADDITIONAL] = arcount; + + // fill in the header + size_t header_pos = 0; + renderer.writeUint16At(qid_, header_pos); + header_pos += sizeof(uint16_t); + + uint16_t codes_and_flags = + (opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK; + codes_and_flags |= (rcode_->getCode() & RCODE_MASK); + codes_and_flags |= (flags_ & HEADERFLAG_MASK); + renderer.writeUint16At(codes_and_flags, header_pos); + header_pos += sizeof(uint16_t); + // TODO: should avoid repeated pattern + renderer.writeUint16At(qdcount, header_pos); + header_pos += sizeof(uint16_t); + renderer.writeUint16At(ancount, header_pos); + header_pos += sizeof(uint16_t); + renderer.writeUint16At(nscount, header_pos); + header_pos += sizeof(uint16_t); + renderer.writeUint16At(arcount, header_pos); + + // Add TSIG, if necessary, at the end of the message. + // TODO: truncate case consideration + if (tsig_ctx != NULL) { + tsig_ctx->sign(qid_, renderer.getData(), + renderer.getLength())->toWire(renderer); + + // update the ARCOUNT for the TSIG RR. Note that for a sane DNS + // message arcount should never overflow to 0. + renderer.writeUint16At(++arcount, header_pos); + } +} + Message::Message(Mode mode) : impl_(new MessageImpl(mode)) {} @@ -262,6 +424,16 @@ Message::setEDNS(ConstEDNSPtr edns) { impl_->edns_ = edns; } +const TSIGRecord* +Message::getTSIGRecord() const { + if (impl_->mode_ != Message::PARSE) { + isc_throw(InvalidMessageOperation, + "getTSIGRecord performed in non-parse mode"); + } + + return (impl_->tsig_rr_.get()); +} + unsigned int Message::getRRCount(const Section section) const { if (section >= MessageImpl::NUM_SECTIONS) { @@ -363,129 +535,14 @@ Message::addQuestion(const Question& question) { addQuestion(QuestionPtr(new Question(question))); } -namespace { -template -struct RenderSection { - RenderSection(MessageRenderer& renderer, const bool partial_ok) : - counter_(0), renderer_(renderer), partial_ok_(partial_ok), - truncated_(false) - {} - void operator()(const T& entry) { - // If it's already truncated, ignore the rest of the section. - if (truncated_) { - return; - } - const size_t pos0 = renderer_.getLength(); - counter_ += entry->toWire(renderer_); - if (renderer_.isTruncated()) { - truncated_ = true; - if (!partial_ok_) { - // roll back to the end of the previous RRset. - renderer_.trim(renderer_.getLength() - pos0); - } - } - } - unsigned int getTotalCount() { return (counter_); } - unsigned int counter_; - MessageRenderer& renderer_; - const bool partial_ok_; - bool truncated_; -}; +void +Message::toWire(AbstractMessageRenderer& renderer) { + impl_->toWire(renderer, NULL); } void -Message::toWire(MessageRenderer& renderer) { - if (impl_->mode_ != Message::RENDER) { - isc_throw(InvalidMessageOperation, - "Message rendering attempted in non render mode"); - } - if (impl_->rcode_ == NULL) { - isc_throw(InvalidMessageOperation, - "Message rendering attempted without Rcode set"); - } - if (impl_->opcode_ == NULL) { - isc_throw(InvalidMessageOperation, - "Message rendering attempted without Opcode set"); - } - - // reserve room for the header - renderer.skip(HEADERLEN); - - uint16_t qdcount = - for_each(impl_->questions_.begin(), impl_->questions_.end(), - RenderSection(renderer, false)).getTotalCount(); - - // TBD: sort RRsets in each section based on configuration policy. - uint16_t ancount = 0; - if (!renderer.isTruncated()) { - ancount = - for_each(impl_->rrsets_[SECTION_ANSWER].begin(), - impl_->rrsets_[SECTION_ANSWER].end(), - RenderSection(renderer, true)).getTotalCount(); - } - uint16_t nscount = 0; - if (!renderer.isTruncated()) { - nscount = - for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(), - impl_->rrsets_[SECTION_AUTHORITY].end(), - RenderSection(renderer, true)).getTotalCount(); - } - uint16_t arcount = 0; - if (renderer.isTruncated()) { - setHeaderFlag(HEADERFLAG_TC, true); - } else { - arcount = - for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(), - impl_->rrsets_[SECTION_ADDITIONAL].end(), - RenderSection(renderer, false)).getTotalCount(); - } - - // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS - // has been explicitly set. However, if the RCODE would require it and - // no EDNS has been set we generate a temporary local EDNS and use it. - if (!renderer.isTruncated()) { - ConstEDNSPtr local_edns = impl_->edns_; - if (!local_edns && impl_->rcode_->getExtendedCode() != 0) { - local_edns = ConstEDNSPtr(new EDNS()); - } - if (local_edns) { - arcount += local_edns->toWire(renderer, - impl_->rcode_->getExtendedCode()); - } - } - - // Adjust the counter buffer. - // XXX: these may not be equal to the number of corresponding entries - // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR - // was inserted. This is not good, and we should revisit the entire - // design. - impl_->counts_[SECTION_QUESTION] = qdcount; - impl_->counts_[SECTION_ANSWER] = ancount; - impl_->counts_[SECTION_AUTHORITY] = nscount; - impl_->counts_[SECTION_ADDITIONAL] = arcount; - - // TBD: TSIG, SIG(0) etc. - - // fill in the header - size_t header_pos = 0; - renderer.writeUint16At(impl_->qid_, header_pos); - header_pos += sizeof(uint16_t); - - uint16_t codes_and_flags = - (impl_->opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK; - codes_and_flags |= (impl_->rcode_->getCode() & RCODE_MASK); - codes_and_flags |= (impl_->flags_ & HEADERFLAG_MASK); - renderer.writeUint16At(codes_and_flags, header_pos); - header_pos += sizeof(uint16_t); - // XXX: should avoid repeated pattern (TODO) - renderer.writeUint16At(qdcount, header_pos); - header_pos += sizeof(uint16_t); - renderer.writeUint16At(ancount, header_pos); - header_pos += sizeof(uint16_t); - renderer.writeUint16At(nscount, header_pos); - header_pos += sizeof(uint16_t); - renderer.writeUint16At(arcount, header_pos); - header_pos += sizeof(uint16_t); +Message::toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx) { + impl_->toWire(renderer, &tsig_ctx); } void @@ -613,6 +670,9 @@ MessageImpl::parseSection(const Message::Section section, unsigned int added = 0; for (unsigned int count = 0; count < counts_[section]; ++count) { + // We need to remember the start position for TSIG processing + const size_t start_position = buffer.getPosition(); + const Name name(buffer); // buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN. @@ -630,32 +690,12 @@ MessageImpl::parseSection(const Message::Section section, ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen); if (rrtype == RRType::OPT()) { - if (section != Message::SECTION_ADDITIONAL) { - isc_throw(DNSMessageFORMERR, - "EDNS OPT RR found in an invalid section"); - } - if (edns_) { - isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found"); - } - - uint8_t extended_rcode; - edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, - *rdata, extended_rcode)); - setRcode(Rcode(rcode_->getCode(), extended_rcode)); - continue; + addEDNS(section, name, rrclass, rrtype, ttl, *rdata); + } else if (rrtype == RRType::TSIG()) { + addTSIG(section, count, buffer, start_position, name, rrclass, ttl, + *rdata); } else { - vector::iterator it = - find_if(rrsets_[section].begin(), rrsets_[section].end(), - MatchRR(name, rrtype, rrclass)); - if (it != rrsets_[section].end()) { - (*it)->setTTL(min((*it)->getTTL(), ttl)); - (*it)->addRdata(rdata); - } else { - RRsetPtr rrset = - RRsetPtr(new RRset(name, rrclass, rrtype, ttl)); - rrset->addRdata(rdata); - rrsets_[section].push_back(rrset); - } + addRR(section, name, rrclass, rrtype, ttl, rdata); ++added; } } @@ -663,6 +703,65 @@ MessageImpl::parseSection(const Message::Section section, return (added); } +void +MessageImpl::addRR(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, ConstRdataPtr rdata) +{ + vector::iterator it = + find_if(rrsets_[section].begin(), rrsets_[section].end(), + MatchRR(name, rrtype, rrclass)); + if (it != rrsets_[section].end()) { + (*it)->setTTL(min((*it)->getTTL(), ttl)); + (*it)->addRdata(rdata); + } else { + RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl)); + rrset->addRdata(rdata); + rrsets_[section].push_back(rrset); + } +} + +void +MessageImpl::addEDNS(Message::Section section, const Name& name, + const RRClass& rrclass, const RRType& rrtype, + const RRTTL& ttl, const Rdata& rdata) +{ + if (section != Message::SECTION_ADDITIONAL) { + isc_throw(DNSMessageFORMERR, + "EDNS OPT RR found in an invalid section"); + } + if (edns_) { + isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found"); + } + + uint8_t extended_rcode; + edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata, + extended_rcode)); + setRcode(Rcode(rcode_->getCode(), extended_rcode)); +} + +void +MessageImpl::addTSIG(Message::Section section, unsigned int count, + const InputBuffer& buffer, size_t start_position, + const Name& name, const RRClass& rrclass, + const RRTTL& ttl, const Rdata& rdata) +{ + if (section != Message::SECTION_ADDITIONAL) { + isc_throw(DNSMessageFORMERR, + "TSIG RR found in an invalid section"); + } + if (count != counts_[section] - 1) { + isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record"); + } + if (tsig_rr_) { + isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found"); + } + tsig_rr_ = ConstTSIGRecordPtr(new TSIGRecord(name, rrclass, + ttl, rdata, + buffer.getPosition() - + start_position)); +} + namespace { template struct SectionFormatter { @@ -696,31 +795,31 @@ Message::toText() const { // for simplicity we don't consider extended rcode (unlike BIND9) s += ", status: " + impl_->rcode_->toText(); s += ", id: " + boost::lexical_cast(impl_->qid_); - s += "\n;; flags: "; + s += "\n;; flags:"; if (getHeaderFlag(HEADERFLAG_QR)) { - s += "qr "; + s += " qr"; } if (getHeaderFlag(HEADERFLAG_AA)) { - s += "aa "; + s += " aa"; } if (getHeaderFlag(HEADERFLAG_TC)) { - s += "tc "; + s += " tc"; } if (getHeaderFlag(HEADERFLAG_RD)) { - s += "rd "; + s += " rd"; } if (getHeaderFlag(HEADERFLAG_RA)) { - s += "ra "; + s += " ra"; } if (getHeaderFlag(HEADERFLAG_AD)) { - s += "ad "; + s += " ad"; } if (getHeaderFlag(HEADERFLAG_CD)) { - s += "cd "; + s += " cd"; } // for simplicity, don't consider the update case for now - s += "; QUESTION: " + + s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig lexical_cast(impl_->counts_[SECTION_QUESTION]); s += ", ANSWER: " + lexical_cast(impl_->counts_[SECTION_ANSWER]); @@ -731,6 +830,9 @@ Message::toText() const { if (impl_->edns_ != NULL) { ++arcount; } + if (impl_->tsig_rr_ != NULL) { + ++arcount; + } s += ", ADDITIONAL: " + lexical_cast(arcount) + "\n"; if (impl_->edns_ != NULL) { @@ -767,6 +869,11 @@ Message::toText() const { SectionFormatter(SECTION_ADDITIONAL, s)); } + if (impl_->tsig_rr_ != NULL) { + s += "\n;; TSIG PSEUDOSECTION:\n"; + s += impl_->tsig_rr_->toText(); + } + return (s); } diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h index 560129924f..fcc53e92a0 100644 --- a/src/lib/dns/message.h +++ b/src/lib/dns/message.h @@ -33,6 +33,8 @@ class InputBuffer; } namespace dns { +class TSIGContext; +class TSIGRecord; /// /// \brief A standard DNS module exception that is thrown if a wire format @@ -80,7 +82,7 @@ public: typedef uint16_t qid_t; -class MessageRenderer; +class AbstractMessageRenderer; class Message; class MessageImpl; class Opcode; @@ -368,6 +370,25 @@ public: /// \c Message. void setEDNS(ConstEDNSPtr edns); + /// \brief Return, if any, the TSIG record contained in the received + /// message. + /// + /// Currently, this method is only intended to return a TSIG record + /// for an incoming message built via the \c fromWire() method in the + /// PARSE mode. A call to this method in the RENDER mode is invalid and + /// result in an exception. Also, calling this method is meaningless + /// unless \c fromWire() is performed. + /// + /// The returned pointer is valid only during the lifetime of the + /// \c Message object and until \c clear() is called. The \c Message + /// object retains the ownership of \c TSIGRecord; the caller must not + /// try to delete it. + /// + /// \exception InvalidMessageOperation Message is not in the PARSE mode. + /// + /// \return A pointer to the stored \c TSIGRecord or \c NULL. + const TSIGRecord* getTSIGRecord() const; + /// \brief Returns the number of RRs contained in the given section. /// /// In the \c PARSE mode, the returned value may not be identical to @@ -523,13 +544,31 @@ public: /// class \c InvalidMessageOperation will be thrown. std::string toText() const; - /// \brief Render the message in wire formant into a \c MessageRenderer + /// \brief Render the message in wire formant into a message renderer /// object. /// /// This \c Message must be in the \c RENDER mode and both \c Opcode and /// \c Rcode must have been set beforehand; otherwise, an exception of /// class \c InvalidMessageOperation will be thrown. - void toWire(MessageRenderer& renderer); + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + void toWire(AbstractMessageRenderer& renderer); + + /// \brief Render the message in wire formant into a message renderer + /// object with TSIG. + /// + /// This method is similar to the other version of \c toWire(), but + /// it will also add a TSIG RR with (in many cases) the TSIG MAC for + /// the message along with the given TSIG context (\c tsig_ctx). + /// The TSIG RR will be placed at the end of \c renderer. + /// \c tsig_ctx will be updated based on the fact it was used for signing + /// and with the latest MAC. + /// + /// \param renderer See the other version + /// \param tsig_ctx A TSIG context that is to be used for signing the + /// message + void toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx); /// \brief Parse the header section of the \c Message. void parseHeader(isc::util::InputBuffer& buffer); @@ -562,7 +601,18 @@ private: /// that ongoing state information will not be lost if the object /// that originated the asynchronous call falls out of scope. typedef boost::shared_ptr MessagePtr; +typedef boost::shared_ptr ConstMessagePtr; +/// Insert the \c Message as a string into stream. +/// +/// This method convert \c message into a string and inserts it into the +/// output stream \c os. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param record A \c Message object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. std::ostream& operator<<(std::ostream& os, const Message& message); } } diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc index 6438fb0d32..767aca9a68 100644 --- a/src/lib/dns/messagerenderer.cc +++ b/src/lib/dns/messagerenderer.cc @@ -228,8 +228,9 @@ MessageRenderer::writeName(const Name& name, const bool compress) { name.toWire(impl_->nbuffer_); unsigned int i; - std::set::const_iterator notfound = impl_->nodeset_.end(); - std::set::const_iterator n = notfound; + std::set::const_iterator notfound = + impl_->nodeset_.end(); + std::set::const_iterator n = notfound; // Find the longest ancestor name in the rendered set that matches the // given name. diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am index 9d171cdf0a..1c9afc73ec 100644 --- a/src/lib/dns/python/Makefile.am +++ b/src/lib/dns/python/Makefile.am @@ -6,6 +6,9 @@ AM_CXXFLAGS = $(B10_CXXFLAGS) pyexec_LTLIBRARIES = pydnspp.la pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc +pydnspp_la_SOURCES += rcode_python.cc rcode_python.h +pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h +pydnspp_la_SOURCES += tsigerror_python_inc.cc pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES) pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS) @@ -25,6 +28,7 @@ EXTRA_DIST += rrttl_python.cc EXTRA_DIST += rdata_python.cc EXTRA_DIST += rrtype_python.cc EXTRA_DIST += tsigkey_python.cc +EXTRA_DIST += tsig_python.cc # Python prefers .so, while some OSes (specifically MacOS) use a different # suffix for dynamic objects. -module is necessary to work this around. diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc index 058312e676..381b7bc613 100644 --- a/src/lib/dns/python/message_python.cc +++ b/src/lib/dns/python/message_python.cc @@ -17,15 +17,16 @@ using namespace isc::dns; using namespace isc::util; +namespace { // // Declaration of the custom exceptions // Initialization and addition of these go in the initModulePart // function at the end of this file // -static PyObject* po_MessageTooShort; -static PyObject* po_InvalidMessageSection; -static PyObject* po_InvalidMessageOperation; -static PyObject* po_InvalidMessageUDPSize; +PyObject* po_MessageTooShort; +PyObject* po_InvalidMessageSection; +PyObject* po_InvalidMessageOperation; +PyObject* po_InvalidMessageUDPSize; // // Definition of the classes @@ -35,10 +36,6 @@ static PyObject* po_InvalidMessageUDPSize; // and static wrappers around the methods we export), a list of methods, // and a type description -// -// Section -// - // // Message // @@ -55,36 +52,36 @@ public: // // General creation and destruction -static int Message_init(s_Message* self, PyObject* args); -static void Message_destroy(s_Message* self); +int Message_init(s_Message* self, PyObject* args); +void Message_destroy(s_Message* self); -static PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args); -static PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args); -static PyObject* Message_getQid(s_Message* self); -static PyObject* Message_setQid(s_Message* self, PyObject* args); -static PyObject* Message_getRcode(s_Message* self); -static PyObject* Message_setRcode(s_Message* self, PyObject* args); -static PyObject* Message_getOpcode(s_Message* self); -static PyObject* Message_setOpcode(s_Message* self, PyObject* args); -static PyObject* Message_getEDNS(s_Message* self); -static PyObject* Message_setEDNS(s_Message* self, PyObject* args); -static PyObject* Message_getRRCount(s_Message* self, PyObject* args); +PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args); +PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args); +PyObject* Message_getQid(s_Message* self); +PyObject* Message_setQid(s_Message* self, PyObject* args); +PyObject* Message_getRcode(s_Message* self); +PyObject* Message_setRcode(s_Message* self, PyObject* args); +PyObject* Message_getOpcode(s_Message* self); +PyObject* Message_setOpcode(s_Message* self, PyObject* args); +PyObject* Message_getEDNS(s_Message* self); +PyObject* Message_setEDNS(s_Message* self, PyObject* args); +PyObject* Message_getRRCount(s_Message* self, PyObject* args); // use direct iterators for these? (or simply lists for now?) -static PyObject* Message_getQuestion(s_Message* self); -static PyObject* Message_getSection(s_Message* self, PyObject* args); +PyObject* Message_getQuestion(s_Message* self); +PyObject* Message_getSection(s_Message* self, PyObject* args); //static PyObject* Message_beginQuestion(s_Message* self, PyObject* args); //static PyObject* Message_endQuestion(s_Message* self, PyObject* args); //static PyObject* Message_beginSection(s_Message* self, PyObject* args); //static PyObject* Message_endSection(s_Message* self, PyObject* args); -static PyObject* Message_addQuestion(s_Message* self, PyObject* args); -static PyObject* Message_addRRset(s_Message* self, PyObject* args); -static PyObject* Message_clear(s_Message* self, PyObject* args); -static PyObject* Message_makeResponse(s_Message* self); -static PyObject* Message_toText(s_Message* self); -static PyObject* Message_str(PyObject* self); -static PyObject* Message_toWire(s_Message* self, PyObject* args); -static PyObject* Message_fromWire(s_Message* self, PyObject* args); +PyObject* Message_addQuestion(s_Message* self, PyObject* args); +PyObject* Message_addRRset(s_Message* self, PyObject* args); +PyObject* Message_clear(s_Message* self, PyObject* args); +PyObject* Message_makeResponse(s_Message* self); +PyObject* Message_toText(s_Message* self); +PyObject* Message_str(PyObject* self); +PyObject* Message_toWire(s_Message* self, PyObject* args); +PyObject* Message_fromWire(s_Message* self, PyObject* args); // This list contains the actual set of functions we have in // python. Each entry has @@ -92,7 +89,7 @@ static PyObject* Message_fromWire(s_Message* self, PyObject* args); // 2. Our static function here // 3. Argument type // 4. Documentation -static PyMethodDef Message_methods[] = { +PyMethodDef Message_methods[] = { { "get_header_flag", reinterpret_cast(Message_getHeaderFlag), METH_VARARGS, "Return whether the specified header flag bit is set in the " @@ -175,7 +172,7 @@ static PyMethodDef Message_methods[] = { // This defines the complete type for reflection in python and // parsing of PyObject* to s_Message // Most of the functions are not actually implemented and NULL here. -static PyTypeObject message_type = { +PyTypeObject message_type = { PyVarObject_HEAD_INIT(NULL, 0) "pydnspp.Message", sizeof(s_Message), // tp_basicsize @@ -225,7 +222,7 @@ static PyTypeObject message_type = { 0 // tp_version_tag }; -static int +int Message_init(s_Message* self, PyObject* args) { int i; @@ -248,14 +245,14 @@ Message_init(s_Message* self, PyObject* args) { return (-1); } -static void +void Message_destroy(s_Message* self) { delete self->message; self->message = NULL; Py_TYPE(self)->tp_free(self); } -static PyObject* +PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args) { unsigned int messageflag; if (!PyArg_ParseTuple(args, "I", &messageflag)) { @@ -273,7 +270,7 @@ Message_getHeaderFlag(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args) { long messageflag; PyObject *on = Py_True; @@ -304,12 +301,12 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_getQid(s_Message* self) { return (Py_BuildValue("I", self->message->getQid())); } -static PyObject* +PyObject* Message_setQid(s_Message* self, PyObject* args) { long id; if (!PyArg_ParseTuple(args, "l", &id)) { @@ -333,21 +330,21 @@ Message_setQid(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_getRcode(s_Message* self) { s_Rcode* rcode; rcode = static_cast(rcode_type.tp_alloc(&rcode_type, 0)); if (rcode != NULL) { - rcode->rcode = NULL; + rcode->cppobj = NULL; try { - rcode->rcode = new Rcode(self->message->getRcode()); + rcode->cppobj = new Rcode(self->message->getRcode()); } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); } catch (...) { PyErr_SetString(po_IscException, "Unexpected exception"); } - if (rcode->rcode == NULL) { + if (rcode->cppobj == NULL) { Py_DECREF(rcode); return (NULL); } @@ -356,14 +353,14 @@ Message_getRcode(s_Message* self) { return (rcode); } -static PyObject* +PyObject* Message_setRcode(s_Message* self, PyObject* args) { s_Rcode* rcode; if (!PyArg_ParseTuple(args, "O!", &rcode_type, &rcode)) { return (NULL); } try { - self->message->setRcode(*rcode->rcode); + self->message->setRcode(*rcode->cppobj); Py_RETURN_NONE; } catch (const InvalidMessageOperation& imo) { PyErr_SetString(po_InvalidMessageOperation, imo.what()); @@ -371,7 +368,7 @@ Message_setRcode(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_getOpcode(s_Message* self) { s_Opcode* opcode; @@ -394,7 +391,7 @@ Message_getOpcode(s_Message* self) { return (opcode); } -static PyObject* +PyObject* Message_setOpcode(s_Message* self, PyObject* args) { s_Opcode* opcode; if (!PyArg_ParseTuple(args, "O!", &opcode_type, &opcode)) { @@ -409,7 +406,7 @@ Message_setOpcode(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_getEDNS(s_Message* self) { s_EDNS* edns; EDNS* edns_body; @@ -429,7 +426,7 @@ Message_getEDNS(s_Message* self) { return (edns); } -static PyObject* +PyObject* Message_setEDNS(s_Message* self, PyObject* args) { s_EDNS* edns; if (!PyArg_ParseTuple(args, "O!", &edns_type, &edns)) { @@ -444,7 +441,7 @@ Message_setEDNS(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_getRRCount(s_Message* self, PyObject* args) { unsigned int section; if (!PyArg_ParseTuple(args, "I", §ion)) { @@ -463,7 +460,7 @@ Message_getRRCount(s_Message* self, PyObject* args) { } // TODO use direct iterators for these? (or simply lists for now?) -static PyObject* +PyObject* Message_getQuestion(s_Message* self) { QuestionIterator qi, qi_end; try { @@ -502,7 +499,7 @@ Message_getQuestion(s_Message* self) { return (list); } -static PyObject* +PyObject* Message_getSection(s_Message* self, PyObject* args) { unsigned int section; if (!PyArg_ParseTuple(args, "I", §ion)) { @@ -559,7 +556,7 @@ Message_getSection(s_Message* self, PyObject* args) { //static PyObject* Message_beginSection(s_Message* self, PyObject* args); //static PyObject* Message_endSection(s_Message* self, PyObject* args); //static PyObject* Message_addQuestion(s_Message* self, PyObject* args); -static PyObject* +PyObject* Message_addQuestion(s_Message* self, PyObject* args) { s_Question *question; @@ -572,7 +569,7 @@ Message_addQuestion(s_Message* self, PyObject* args) { Py_RETURN_NONE; } -static PyObject* +PyObject* Message_addRRset(s_Message* self, PyObject* args) { PyObject *sign = Py_False; int section; @@ -599,7 +596,7 @@ Message_addRRset(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_clear(s_Message* self, PyObject* args) { int i; if (PyArg_ParseTuple(args, "i", &i)) { @@ -620,13 +617,13 @@ Message_clear(s_Message* self, PyObject* args) { } } -static PyObject* +PyObject* Message_makeResponse(s_Message* self) { self->message->makeResponse(); Py_RETURN_NONE; } -static PyObject* +PyObject* Message_toText(s_Message* self) { // Py_BuildValue makes python objects from native data try { @@ -641,7 +638,7 @@ Message_toText(s_Message* self) { } } -static PyObject* +PyObject* Message_str(PyObject* self) { // Simply call the to_text method we already defined return (PyObject_CallMethod(self, @@ -649,13 +646,20 @@ Message_str(PyObject* self) { const_cast(""))); } -static PyObject* +PyObject* Message_toWire(s_Message* self, PyObject* args) { s_MessageRenderer* mr; + s_TSIGContext* tsig_ctx = NULL; - if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) { + if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr, + &tsig_context_type, &tsig_ctx)) { try { - self->message->toWire(*mr->messagerenderer); + if (tsig_ctx == NULL) { + self->message->toWire(*mr->messagerenderer); + } else { + self->message->toWire(*mr->messagerenderer, + *tsig_ctx->tsig_ctx); + } // If we return NULL it is seen as an error, so use this for // None returns Py_RETURN_NONE; @@ -671,7 +675,7 @@ Message_toWire(s_Message* self, PyObject* args) { return (NULL); } -static PyObject* +PyObject* Message_fromWire(s_Message* self, PyObject* args) { const char* b; Py_ssize_t len; @@ -765,3 +769,4 @@ initModulePart_Message(PyObject* mod) { return (true); } +} // end of unnamed namespace diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc index 27dbae684a..0be346e3b2 100644 --- a/src/lib/dns/python/pydnspp.cc +++ b/src/lib/dns/python/pydnspp.cc @@ -32,20 +32,32 @@ #include #include + #include #include #include -#include +#include "pydnspp_common.h" +namespace isc { +namespace dns { +namespace python { // For our 'general' isc::Exceptions -static PyObject* po_IscException; -static PyObject* po_InvalidParameter; +PyObject* po_IscException; +PyObject* po_InvalidParameter; // For our own isc::dns::Exception -static PyObject* po_DNSMessageBADVERS; +PyObject* po_DNSMessageBADVERS; +} +} +} + +#include "rcode_python.h" +#include "tsigerror_python.h" // order is important here! +using namespace isc::dns::python; + #include #include // needs Messagerenderer #include // needs Messagerenderer @@ -56,15 +68,16 @@ static PyObject* po_DNSMessageBADVERS; #include // needs RRClass, RRType, RRTTL, // Name #include // needs Name +#include // needs tsigkey #include -#include #include // needs Messagerenderer, Rcode #include // needs RRset, Question // // Definition of the module // -static PyModuleDef pydnspp = { +namespace { +PyModuleDef pydnspp = { { PyObject_HEAD_INIT(NULL) NULL, 0, NULL}, "pydnspp", "Python bindings for the classes in the isc::dns namespace.\n\n" @@ -79,10 +92,11 @@ static PyModuleDef pydnspp = { NULL, NULL }; +} PyMODINIT_FUNC PyInit_pydnspp(void) { - PyObject *mod = PyModule_Create(&pydnspp); + PyObject* mod = PyModule_Create(&pydnspp); if (mod == NULL) { return (NULL); } @@ -153,6 +167,14 @@ PyInit_pydnspp(void) { return (NULL); } + if (!initModulePart_TSIGError(mod)) { + return (NULL); + } + + if (!initModulePart_TSIGContext(mod)) { + return (NULL); + } + return (mod); } diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc index 6c26367bc7..8ca763a969 100644 --- a/src/lib/dns/python/pydnspp_common.cc +++ b/src/lib/dns/python/pydnspp_common.cc @@ -15,6 +15,9 @@ #include #include +namespace isc { +namespace dns { +namespace python { int readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) { PyObject* el = NULL; @@ -44,8 +47,15 @@ readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) { } -void addClassVariable(PyTypeObject& c, const char* name, - PyObject* obj) -{ - PyDict_SetItemString(c.tp_dict, name, obj); +int +addClassVariable(PyTypeObject& c, const char* name, PyObject* obj) { + if (obj == NULL) { + PyErr_SetString(PyExc_ValueError, + "NULL object is specified for a class variable"); + return (-1); + } + return (PyDict_SetItemString(c.tp_dict, name, obj)); +} +} +} } diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h index 32e2b784e6..ed90998ccc 100644 --- a/src/lib/dns/python/pydnspp_common.h +++ b/src/lib/dns/python/pydnspp_common.h @@ -15,9 +15,22 @@ #ifndef __LIBDNS_PYTHON_COMMON_H #define __LIBDNS_PYTHON_COMMON_H 1 -// -// Shared functions for python/c API -// +#include + +#include +#include + +#include + +namespace isc { +namespace dns { +namespace python { +// For our 'general' isc::Exceptions +extern PyObject* po_IscException; +extern PyObject* po_InvalidParameter; + +// For our own isc::dns::Exception +extern PyObject* po_DNSMessageBADVERS; // This function reads 'bytes' from a sequence // This sequence can be anything that implements the Sequence interface, @@ -31,6 +44,12 @@ // case nothing is removed int readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence); -void addClassVariable(PyTypeObject& c, const char* name, PyObject* obj); - +int addClassVariable(PyTypeObject& c, const char* name, PyObject* obj); +} // namespace python +} // namespace dns +} // namespace isc #endif // __LIBDNS_PYTHON_COMMON_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc index b80a93c5d1..b594ad33b5 100644 --- a/src/lib/dns/python/rcode_python.cc +++ b/src/lib/dns/python/rcode_python.cc @@ -12,9 +12,17 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + +#include + #include +#include "pydnspp_common.h" +#include "rcode_python.h" + using namespace isc::dns; +using namespace isc::dns::python; // // Declaration of the custom exceptions (None for this class) @@ -27,25 +35,14 @@ using namespace isc::dns; // and static wrappers around the methods we export), a list of methods, // and a type description -namespace { // // Rcode // -// We added a helper variable static_code here -// Since we can create Rcodes dynamically with Rcode(int), but also -// use the static globals (Rcode::NOERROR() etc), we use this -// variable to see if the code came from one of the latter, in which -// case Rcode_destroy should not free it (the other option is to -// allocate new Rcodes for every use of the static ones, but this -// seems more efficient). -class s_Rcode : public PyObject { -public: - s_Rcode() : rcode(NULL), static_code(false) {} - const Rcode* rcode; - bool static_code; -}; +// Trivial constructor. +s_Rcode::s_Rcode() : cppobj(NULL), static_code(false) {} +namespace { int Rcode_init(s_Rcode* const self, PyObject* args); void Rcode_destroy(s_Rcode* const self); @@ -118,57 +115,6 @@ PyMethodDef Rcode_methods[] = { { NULL, NULL, 0, NULL } }; -PyTypeObject rcode_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "pydnspp.Rcode", - sizeof(s_Rcode), // tp_basicsize - 0, // tp_itemsize - (destructor)Rcode_destroy, // tp_dealloc - NULL, // tp_print - NULL, // tp_getattr - NULL, // tp_setattr - NULL, // tp_reserved - NULL, // tp_repr - NULL, // tp_as_number - NULL, // tp_as_sequence - NULL, // tp_as_mapping - NULL, // tp_hash - NULL, // tp_call - Rcode_str, // tp_str - NULL, // tp_getattro - NULL, // tp_setattro - NULL, // tp_as_buffer - Py_TPFLAGS_DEFAULT, // tp_flags - "The Rcode class objects represent standard RCODEs" - "of the header section of DNS messages.", - NULL, // tp_traverse - NULL, // tp_clear - (richcmpfunc)Rcode_richcmp, // tp_richcompare - 0, // tp_weaklistoffset - NULL, // tp_iter - NULL, // tp_iternext - Rcode_methods, // tp_methods - NULL, // tp_members - NULL, // tp_getset - NULL, // tp_base - NULL, // tp_dict - NULL, // tp_descr_get - NULL, // tp_descr_set - 0, // tp_dictoffset - (initproc)Rcode_init, // tp_init - NULL, // tp_alloc - PyType_GenericNew, // tp_new - NULL, // tp_free - NULL, // tp_is_gc - NULL, // tp_bases - NULL, // tp_mro - NULL, // tp_cache - NULL, // tp_subclasses - NULL, // tp_weaklist - NULL, // tp_del - 0 // tp_version_tag -}; - int Rcode_init(s_Rcode* const self, PyObject* args) { long code = 0; @@ -193,9 +139,9 @@ Rcode_init(s_Rcode* const self, PyObject* args) { } try { if (ext_code == -1) { - self->rcode = new Rcode(code); + self->cppobj = new Rcode(code); } else { - self->rcode = new Rcode(code, ext_code); + self->cppobj = new Rcode(code, ext_code); } self->static_code = false; } catch (const isc::OutOfRange& ex) { @@ -211,27 +157,27 @@ Rcode_init(s_Rcode* const self, PyObject* args) { void Rcode_destroy(s_Rcode* const self) { // Depending on whether we created the rcode or are referring - // to a global one, we do or do not delete self->rcode here + // to a global one, we do or do not delete self->cppobj here if (!self->static_code) { - delete self->rcode; + delete self->cppobj; } - self->rcode = NULL; + self->cppobj = NULL; Py_TYPE(self)->tp_free(self); } PyObject* Rcode_getCode(const s_Rcode* const self) { - return (Py_BuildValue("I", self->rcode->getCode())); + return (Py_BuildValue("I", self->cppobj->getCode())); } PyObject* Rcode_getExtendedCode(const s_Rcode* const self) { - return (Py_BuildValue("I", self->rcode->getExtendedCode())); + return (Py_BuildValue("I", self->cppobj->getExtendedCode())); } PyObject* Rcode_toText(const s_Rcode* const self) { - return (Py_BuildValue("s", self->rcode->toText().c_str())); + return (Py_BuildValue("s", self->cppobj->toText().c_str())); } PyObject* @@ -245,7 +191,7 @@ PyObject* Rcode_createStatic(const Rcode& rcode) { s_Rcode* ret = PyObject_New(s_Rcode, &rcode_type); if (ret != NULL) { - ret->rcode = &rcode; + ret->cppobj = &rcode; ret->static_code = true; } return (ret); @@ -357,10 +303,10 @@ Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other, PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode"); return (NULL); case Py_EQ: - c = (*self->rcode == *other->rcode); + c = (*self->cppobj == *other->cppobj); break; case Py_NE: - c = (*self->rcode != *other->rcode); + c = (*self->cppobj != *other->cppobj); break; case Py_GT: PyErr_SetString(PyExc_TypeError, "Unorderable type; Rcode"); @@ -374,6 +320,61 @@ Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other, else Py_RETURN_FALSE; } +} // end of unnamed namespace + +namespace isc { +namespace dns { +namespace python { +PyTypeObject rcode_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "pydnspp.Rcode", + sizeof(s_Rcode), // tp_basicsize + 0, // tp_itemsize + (destructor)Rcode_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + Rcode_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The Rcode class objects represent standard RCODEs" + "of the header section of DNS messages.", + NULL, // tp_traverse + NULL, // tp_clear + reinterpret_cast(Rcode_richcmp), // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + Rcode_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)Rcode_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; // Module Initialization, all statics are initialized here bool @@ -428,4 +429,6 @@ initModulePart_Rcode(PyObject* mod) { return (true); } -} // end of unnamed namespace +} // namespace python +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h new file mode 100644 index 0000000000..9b5e699e85 --- /dev/null +++ b/src/lib/dns/python/rcode_python.h @@ -0,0 +1,57 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __PYTHON_RCODE_H +#define __PYTHON_RCODE_H 1 + +#include + +namespace isc { +namespace dns { +class Rcode; + +namespace python { + +// The s_* Class simply covers one instantiation of the object. +// +// We added a helper variable static_code here +// Since we can create Rcodes dynamically with Rcode(int), but also +// use the static globals (Rcode::NOERROR() etc), we use this +// variable to see if the code came from one of the latter, in which +// case Rcode_destroy should not free it (the other option is to +// allocate new Rcodes for every use of the static ones, but this +// seems more efficient). +// +// Follow-up note: we don't have to use the proxy function in the python lib; +// we can just define class specific constants directly (see TSIGError). +// We should make this cleanup later. +class s_Rcode : public PyObject { +public: + s_Rcode(); + const Rcode* cppobj; + bool static_code; +}; + +extern PyTypeObject rcode_type; + +bool initModulePart_Rcode(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_RCODE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am index 272f1c1442..1149a47f02 100644 --- a/src/lib/dns/python/tests/Makefile.am +++ b/src/lib/dns/python/tests/Makefile.am @@ -11,6 +11,8 @@ PYTESTS += rrclass_python_test.py PYTESTS += rrset_python_test.py PYTESTS += rrttl_python_test.py PYTESTS += rrtype_python_test.py +PYTESTS += tsig_python_test.py +PYTESTS += tsigerror_python_test.py PYTESTS += tsigkey_python_test.py EXTRA_DIST = $(PYTESTS) @@ -20,7 +22,7 @@ EXTRA_DIST += testutil.py # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py index bccc5968a3..72807cc929 100644 --- a/src/lib/dns/python/tests/message_python_test.py +++ b/src/lib/dns/python/tests/message_python_test.py @@ -62,6 +62,12 @@ def create_message(): message_render.add_rrset(Message.SECTION_ANSWER, rrset) return message_render +def strip_mutable_tsig_data(data): + # Unfortunately we cannot easily compare TSIG RR because we can't tweak + # current time. As a work around this helper function strips off the time + # dependent part of TSIG RDATA, i.e., the MAC (assuming HMAC-MD5) and + # Time Signed. + return data[0:-32] + data[-26:-22] + data[-6:] class MessageTest(unittest.TestCase): @@ -81,6 +87,8 @@ class MessageTest(unittest.TestCase): self.bogus_section = Message.SECTION_ADDITIONAL + 1 self.bogus_below_section = Message.SECTION_QUESTION - 1 + self.tsig_key = TSIGKey("www.example.com:SFuWd/q99SzF8Yzd1QbB9g==") + self.tsig_ctx = TSIGContext(self.tsig_key) def test_init(self): self.assertRaises(TypeError, Message, -1) @@ -277,12 +285,39 @@ class MessageTest(unittest.TestCase): self.assertRaises(InvalidMessageOperation, self.r.to_wire, MessageRenderer()) + def __common_tsigquery_setup(self): + self.r.set_opcode(Opcode.QUERY()) + self.r.set_rcode(Rcode.NOERROR()) + self.r.set_header_flag(Message.HEADERFLAG_RD) + self.r.add_question(Question(Name("www.example.com"), + RRClass("IN"), RRType("A"))) + + def __common_tsig_checks(self, expected_file): + renderer = MessageRenderer() + self.r.to_wire(renderer, self.tsig_ctx) + actual_wire = strip_mutable_tsig_data(renderer.get_data()) + expected_wire = strip_mutable_tsig_data(read_wire_data(expected_file)) + self.assertEqual(expected_wire, actual_wire) + + def test_to_wire_with_tsig(self): + self.r.set_qid(0x2d65) + self.__common_tsigquery_setup() + self.__common_tsig_checks("message_toWire2.wire") + + def test_to_wire_with_edns_tsig(self): + self.r.set_qid(0x6cd) + self.__common_tsigquery_setup() + edns = EDNS() + edns.set_udp_size(4096) + self.r.set_edns(edns) + self.__common_tsig_checks("message_toWire3.wire") + def test_to_text(self): message_render = create_message() msg_str =\ """;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4149 -;; flags: qr aa rd ; QUESTION: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 +;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;test.example.com. IN A diff --git a/src/bin/stats/run_b10-stats.sh.in b/src/lib/dns/python/tests/tsig_python_test.py similarity index 65% rename from src/bin/stats/run_b10-stats.sh.in rename to src/lib/dns/python/tests/tsig_python_test.py index 65b9737fa5..bffa0cfcca 100644 --- a/src/bin/stats/run_b10-stats.sh.in +++ b/src/lib/dns/python/tests/tsig_python_test.py @@ -1,5 +1,3 @@ -#! /bin/sh - # Copyright (C) 2010 Internet Systems Consortium. # # Permission to use, copy, modify, and distribute this software for any @@ -15,19 +13,17 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@} -export PYTHON_EXEC +import unittest +from pydnspp import * -PYTHONPATH=@abs_top_builddir@/src/lib/python -export PYTHONPATH +class TSIGContextTest(unittest.TestCase): + tsig_key = TSIGKey('www.example.com:SFuWd/q99SzF8Yzd1QbB9g==') -BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket -export BIND10_MSGQ_SOCKET_FILE + def setUp(self): + # In the minimal implementation, we simply check constructing a + # TSIGContext doesn't cause any disruption. We can add more tests + # later. + self.tsig_ctx = TSIGContext(self.tsig_key) -B10_FROM_BUILD=@abs_top_builddir@ -export B10_FROM_BUILD - -STATS_PATH=@abs_top_builddir@/src/bin/stats - -cd ${STATS_PATH} -exec ${PYTHON_EXEC} -O b10-stats "$@" +if __name__ == '__main__': + unittest.main() diff --git a/src/lib/dns/python/tests/tsigerror_python_test.py b/src/lib/dns/python/tests/tsigerror_python_test.py new file mode 100644 index 0000000000..a968b6bcd5 --- /dev/null +++ b/src/lib/dns/python/tests/tsigerror_python_test.py @@ -0,0 +1,97 @@ +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import unittest +import sys +from pydnspp import * + +class TSIGErrorTest(unittest.TestCase): + def test_from_code(self): + self.assertEqual(0, TSIGError(0).get_code()) + self.assertEqual(18, TSIGError(18).get_code()) + self.assertEqual(65535, TSIGError(65535).get_code()) + self.assertRaises(ValueError, TSIGError, 65536) + self.assertRaises(ValueError, TSIGError, -1) + self.assertRaises(TypeError, TSIGError, "not yet supported") + + def test_from_rcode(self): + # We use RCODE for code values from 0-15. + self.assertEqual(0, TSIGError(Rcode.NOERROR()).get_code()) + self.assertEqual(15, TSIGError(Rcode(15)).get_code()) + + # From error code 16 TSIG errors define a separate space, so passing + # corresponding RCODE for such code values should be prohibited. + self.assertRaises(ValueError, TSIGError, Rcode(16)) + + def test_constants(self): + # We'll only test arbitrarily chosen subsets of the codes. + # This class is quite simple, so it should be suffice. + self.assertEqual(TSIGError.BAD_SIG_CODE, TSIGError(16).get_code()) + self.assertEqual(TSIGError.BAD_KEY_CODE, TSIGError(17).get_code()) + self.assertEqual(TSIGError.BAD_TIME_CODE, TSIGError(18).get_code()) + + self.assertEqual(0, TSIGError.NOERROR.get_code()) + self.assertEqual(9, TSIGError.NOTAUTH.get_code()) + self.assertEqual(14, TSIGError.RESERVED14.get_code()) + self.assertEqual(TSIGError.BAD_SIG_CODE, TSIGError.BAD_SIG.get_code()) + self.assertEqual(TSIGError.BAD_KEY_CODE, TSIGError.BAD_KEY.get_code()) + self.assertEqual(TSIGError.BAD_TIME_CODE, TSIGError.BAD_TIME.get_code()) + + def test_equal(self): + self.assertTrue(TSIGError.NOERROR == TSIGError(Rcode.NOERROR())) + self.assertTrue(TSIGError(Rcode.NOERROR()) == TSIGError.NOERROR) + + self.assertTrue(TSIGError.BAD_SIG == TSIGError(16)) + self.assertTrue(TSIGError(16) == TSIGError.BAD_SIG) + + def test_nequal(self): + self.assertTrue(TSIGError.BAD_KEY != TSIGError(Rcode.NOERROR())) + self.assertTrue(TSIGError(Rcode.NOERROR()) != TSIGError.BAD_KEY) + + def test_to_text(self): + # TSIGError derived from the standard Rcode + self.assertEqual("NOERROR", TSIGError(Rcode.NOERROR()).to_text()) + + # Well known TSIG errors + self.assertEqual("BADSIG", TSIGError.BAD_SIG.to_text()) + self.assertEqual("BADKEY", TSIGError.BAD_KEY.to_text()) + self.assertEqual("BADTIME", TSIGError.BAD_TIME.to_text()) + + # Unknown (or not yet supported) codes. Simply converted as numeric. + self.assertEqual("19", TSIGError(19).to_text()); + self.assertEqual("65535", TSIGError(65535).to_text()); + + # also check str() works same way + self.assertEqual("NOERROR", str(TSIGError(Rcode.NOERROR()))) + self.assertEqual("BADSIG", str(TSIGError.BAD_SIG)) + + def test_to_rcode(self): + # TSIGError derived from the standard Rcode + self.assertEqual(Rcode.NOERROR(), TSIGError(Rcode.NOERROR()).to_rcode()) + + # Well known TSIG errors + self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_SIG.to_rcode()) + self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_KEY.to_rcode()) + self.assertEqual(Rcode.NOTAUTH(), TSIGError.BAD_TIME.to_rcode()) + + # Unknown (or not yet supported) codes are treated as SERVFAIL. + self.assertEqual(Rcode.SERVFAIL(), TSIGError(19).to_rcode()) + self.assertEqual(Rcode.SERVFAIL(), TSIGError(65535).to_rcode()) + + # Check there's no redundant refcount (which would cause leak) + self.assertEqual(1, sys.getrefcount(TSIGError.BAD_SIG.to_rcode())) + +if __name__ == '__main__': + unittest.main() diff --git a/src/lib/dns/python/tests/tsigkey_python_test.py b/src/lib/dns/python/tests/tsigkey_python_test.py index 06e68687ee..305c8ddcaf 100644 --- a/src/lib/dns/python/tests/tsigkey_python_test.py +++ b/src/lib/dns/python/tests/tsigkey_python_test.py @@ -44,8 +44,33 @@ class TSIGKeyTest(unittest.TestCase): TSIGKey.HMACMD5_NAME, 'should be binary') # signature mismatch + def test_str(self): + k1 = TSIGKey('test.example:CwsLCwsLCwsLCwsLCwsLCw==:hmac-md5.sig-alg.reg.int') + self.assertEqual(Name('test.example.'), k1.get_key_name()) + self.assertEqual(Name('hmac-md5.sig-alg.reg.int.'), k1.get_algorithm_name()) + self.assertEqual(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b', + k1.get_secret()) + self.assertEqual('test.example.:CwsLCwsLCwsLCwsLCwsLCw==:hmac-md5.sig-alg.reg.int.', + k1.to_text()) + + self.assertRaises(InvalidParameter, TSIGKey, + 'test.example:CwsLCwsLCwsLCwsLCwsLCw==:unsupported') + self.assertRaises(InvalidParameter, TSIGKey, + '::') + self.assertRaises(InvalidParameter, TSIGKey, + 'test.example:') + self.assertRaises(InvalidParameter, TSIGKey, + 'test.example:%bad_base_64%') + self.assertRaises(InvalidParameter, TSIGKey, + 'test.example:CwsLCwsLCwsLCwsLCwsLCw==:') + self.assertRaises(InvalidParameter, TSIGKey, + 'test.:example:CwsLCwsLCwsLCwsLCwsLCw==') + class TSIGKeyRingTest(unittest.TestCase): key_name = Name('example.com') + md5_name = Name('hmac-md5.sig-alg.reg.int') + sha1_name = Name('hmac-sha1') + sha256_name = Name('hmac-sha256') secret = b'someRandomData' def setUp(self): @@ -130,18 +155,26 @@ class TSIGKeyRingTest(unittest.TestCase): def test_find(self): self.assertEqual((TSIGKeyRing.NOTFOUND, None), - self.keyring.find(self.key_name)) + self.keyring.find(self.key_name, self.md5_name)) self.assertEqual(TSIGKeyRing.SUCCESS, self.keyring.add(TSIGKey(self.key_name, - TSIGKey.HMACSHA256_NAME, + self.sha256_name, self.secret))) - (code, key) = self.keyring.find(self.key_name) + (code, key) = self.keyring.find(self.key_name, self.sha256_name) self.assertEqual(TSIGKeyRing.SUCCESS, code) self.assertEqual(self.key_name, key.get_key_name()) self.assertEqual(TSIGKey.HMACSHA256_NAME, key.get_algorithm_name()) self.assertEqual(self.secret, key.get_secret()) + (code, key) = self.keyring.find(Name('different-key.example'), + self.sha256_name) + self.assertEqual(TSIGKeyRing.NOTFOUND, code) + self.assertEqual(None, key) + (code, key) = self.keyring.find(self.key_name, self.md5_name) + self.assertEqual(TSIGKeyRing.NOTFOUND, code) + self.assertEqual(None, key) + self.assertRaises(TypeError, self.keyring.find, 1) self.assertRaises(TypeError, self.keyring.find, 'should be a name') self.assertRaises(TypeError, self.keyring.find, self.key_name, 0) @@ -149,24 +182,28 @@ class TSIGKeyRingTest(unittest.TestCase): def test_find_from_some(self): self.assertEqual(TSIGKeyRing.SUCCESS, self.keyring.add(TSIGKey(self.key_name, - TSIGKey.HMACSHA256_NAME, + self.sha256_name, self.secret))) self.assertEqual(TSIGKeyRing.SUCCESS, self.keyring.add(TSIGKey(Name('another.example'), - TSIGKey.HMACMD5_NAME, + self.md5_name, self.secret))) self.assertEqual(TSIGKeyRing.SUCCESS, self.keyring.add(TSIGKey(Name('more.example'), - TSIGKey.HMACSHA1_NAME, + self.sha1_name, self.secret))) - (code, key) = self.keyring.find(Name('another.example')) + (code, key) = self.keyring.find(Name('another.example'), self.md5_name) self.assertEqual(TSIGKeyRing.SUCCESS, code) self.assertEqual(Name('another.example'), key.get_key_name()) self.assertEqual(TSIGKey.HMACMD5_NAME, key.get_algorithm_name()) self.assertEqual((TSIGKeyRing.NOTFOUND, None), - self.keyring.find(Name('noexist.example'))) + self.keyring.find(Name('noexist.example'), + self.sha1_name)) + self.assertEqual((TSIGKeyRing.NOTFOUND, None), + self.keyring.find(Name('another.example'), + self.sha1_name)) if __name__ == '__main__': unittest.main() diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc new file mode 100644 index 0000000000..7b6ba0176e --- /dev/null +++ b/src/lib/dns/python/tsig_python.cc @@ -0,0 +1,156 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +using namespace isc::dns; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +namespace { +// The s_* Class simply covers one instantiation of the object +class s_TSIGContext : public PyObject { +public: + TSIGContext* tsig_ctx; +}; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// General creation and destruction +int TSIGContext_init(s_TSIGContext* self, PyObject* args); +void TSIGContext_destroy(s_TSIGContext* self); + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef TSIGContext_methods[] = { + { NULL, NULL, 0, NULL } +}; + +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_EDNS +// Most of the functions are not actually implemented and NULL here. +PyTypeObject tsig_context_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "libdns_python.TSIGContext", + sizeof(s_TSIGContext), // tp_basicsize + 0, // tp_itemsize + (destructor)TSIGContext_destroy, // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + NULL, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The TSIGContext class maintains a context of a signed session of " + "DNS transactions by TSIG.", + NULL, // tp_traverse + NULL, // tp_clear + NULL, // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + TSIGContext_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + (initproc)TSIGContext_init, // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +int +TSIGContext_init(s_TSIGContext* self, PyObject* args) { + const s_TSIGKey* tsigkey_obj; + + try { + if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) { + self->tsig_ctx = new TSIGContext(*tsigkey_obj->tsigkey); + return (0); + } + } catch (...) { + PyErr_SetString(po_IscException, "Unexpected exception"); + return (-1); + } + + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, + "Invalid arguments to TSIGContext constructor"); + + return (-1); +} + +void +TSIGContext_destroy(s_TSIGContext* const self) { + delete self->tsig_ctx; + self->tsig_ctx = NULL; + Py_TYPE(self)->tp_free(self); +} + +// Module Initialization, all statics are initialized here +bool +initModulePart_TSIGContext(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&tsig_context_type) < 0) { + return (false); + } + Py_INCREF(&tsig_context_type); + void* p = &tsig_context_type; + PyModule_AddObject(mod, "TSIGContext", static_cast(p)); + + addClassVariable(tsig_context_type, "DEFAULT_FUDGE", + Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE)); + + return (true); +} +} // end of anonymous namespace diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc new file mode 100644 index 0000000000..e973772bd6 --- /dev/null +++ b/src/lib/dns/python/tsigerror_python.cc @@ -0,0 +1,362 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include +#include + +#include + +#include + +#include "pydnspp_common.h" +#include "rcode_python.h" +#include "tsigerror_python.h" + +using namespace std; +using namespace isc::util::python; +using namespace isc::dns; +using namespace isc::dns::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// TSIGError +// + +// Trivial constructor. +s_TSIGError::s_TSIGError() : cppobj(NULL) { +} + +// Import pydoc text +#include "tsigerror_python_inc.cc" + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer TSIGErrorContainer; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// General creation and destruction +int TSIGError_init(s_TSIGError* self, PyObject* args); +void TSIGError_destroy(s_TSIGError* self); + +// These are the functions we export +PyObject* TSIGError_getCode(const s_TSIGError* const self); +PyObject* TSIGError_toText(const s_TSIGError* const self); +PyObject* TSIGError_toRcode(const s_TSIGError* const self); +PyObject* TSIGError_str(PyObject* self); +PyObject* TSIGError_richcmp(const s_TSIGError* const self, + const s_TSIGError* const other, int op); + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef TSIGError_methods[] = { + { "get_code", reinterpret_cast(TSIGError_getCode), + METH_NOARGS, + TSIGError_getCode_doc }, + { "to_text", reinterpret_cast(TSIGError_toText), METH_NOARGS, + TSIGError_toText_doc }, + { "to_rcode", reinterpret_cast(TSIGError_toRcode), + METH_NOARGS, + TSIGError_toRcode_doc }, + { NULL, NULL, 0, NULL } +}; + +int +TSIGError_init(s_TSIGError* self, PyObject* args) { + try { + // Constructor from the code value + long code = 0; + if (PyArg_ParseTuple(args, "l", &code)) { + if (code < 0 || code > 0xffff) { + PyErr_SetString(PyExc_ValueError, "TSIG error out of range"); + return (-1); + } + self->cppobj = new TSIGError(code); + return (0); + } + + // Constructor from Rcode + s_Rcode* py_rcode; + if (PyArg_ParseTuple(args, "O!", &rcode_type, &py_rcode)) { + self->cppobj = new TSIGError(*py_rcode->cppobj); + return (0); + } + } catch (const isc::OutOfRange& ex) { + const string ex_what = "Failed to construct TSIGError object: " + + string(ex.what()); + PyErr_SetString(PyExc_ValueError, ex_what.c_str()); + return (-1); + } catch (const exception& ex) { + const string ex_what = "Failed to construct TSIGError object: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (-1); + } catch (...) { + PyErr_SetString(po_IscException, + "Unexpected exception in constructing TSIGError"); + return (-1); + } + + PyErr_SetString(PyExc_TypeError, + "Invalid arguments to TSIGError constructor"); + + return (-1); +} + +void +TSIGError_destroy(s_TSIGError* const self) { + delete self->cppobj; + self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +PyObject* +TSIGError_getCode(const s_TSIGError* const self) { + return (Py_BuildValue("I", self->cppobj->getCode())); +} + +PyObject* +TSIGError_toText(const s_TSIGError* const self) { + try { + // toText() could throw, so we need to catch any exceptions below. + return (Py_BuildValue("s", self->cppobj->toText().c_str())); + } catch (const exception& ex) { + const string ex_what = + "Failed to convert TSIGError object to text: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, "Unexpected failure in " + "converting TSIGError object to text"); + } + return (NULL); +} + +PyObject* +TSIGError_str(PyObject* self) { + // Simply call the to_text method we already defined + return (PyObject_CallMethod(self, const_cast("to_text"), + const_cast(""))); +} + +PyObject* +TSIGError_toRcode(const s_TSIGError* const self) { + typedef CPPPyObjectContainer RcodePyObjectContainer; + + try { + RcodePyObjectContainer rcode_container(PyObject_New(s_Rcode, + &rcode_type)); + rcode_container.set(new Rcode(self->cppobj->toRcode())); + return (rcode_container.release()); + } catch (const exception& ex) { + const string ex_what = + "Failed to convert TSIGError to Rcode: " + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, "Unexpected failure in " + "converting TSIGError to Rcode"); + } + return (NULL); +} + +PyObject* +TSIGError_richcmp(const s_TSIGError* const self, + const s_TSIGError* const other, + const int op) +{ + bool c = false; + + // Check for null and if the types match. If different type, + // simply return False + if (other == NULL || (self->ob_type != other->ob_type)) { + Py_RETURN_FALSE; + } + + // Only equals and not equals here, unorderable type + switch (op) { + case Py_LT: + PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError"); + return (NULL); + case Py_LE: + PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError"); + return (NULL); + case Py_EQ: + c = (*self->cppobj == *other->cppobj); + break; + case Py_NE: + c = (*self->cppobj != *other->cppobj); + break; + case Py_GT: + PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError"); + return (NULL); + case Py_GE: + PyErr_SetString(PyExc_TypeError, "Unorderable type; TSIGError"); + return (NULL); + } + if (c) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} +} // end of unnamed namespace + +namespace isc { +namespace dns { +namespace python { +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_TSIGError +// Most of the functions are not actually implemented and NULL here. +PyTypeObject tsigerror_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "libdns_python.TSIGError", + sizeof(s_TSIGError), // tp_basicsize + 0, // tp_itemsize + reinterpret_cast(TSIGError_destroy), // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + // THIS MAY HAVE TO BE CHANGED TO NULL: + TSIGError_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + TSIGError_doc, + NULL, // tp_traverse + NULL, // tp_clear + // THIS MAY HAVE TO BE CHANGED TO NULL: + reinterpret_cast(TSIGError_richcmp), // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + TSIGError_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + reinterpret_cast(TSIGError_init), // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +namespace { +// Trivial shortcut to create and install TSIGError constants. +inline void +installTSIGErrorConstant(const char* name, const TSIGError& val) { + TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type)); + container.installAsClassVariable(tsigerror_type, name, new TSIGError(val)); +} +} + +// Module Initialization, all statics are initialized here +bool +initModulePart_TSIGError(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&tsigerror_type) < 0) { + return (false); + } + void* p = &tsigerror_type; + if (PyModule_AddObject(mod, "TSIGError", static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&tsigerror_type); + + try { + // Constant class variables + // Error codes (bare values) + installClassVariable(tsigerror_type, "BAD_SIG_CODE", + Py_BuildValue("H", TSIGError::BAD_SIG_CODE)); + installClassVariable(tsigerror_type, "BAD_KEY_CODE", + Py_BuildValue("H", TSIGError::BAD_KEY_CODE)); + installClassVariable(tsigerror_type, "BAD_TIME_CODE", + Py_BuildValue("H", TSIGError::BAD_TIME_CODE)); + + // Error codes (constant objects) + installTSIGErrorConstant("NOERROR", TSIGError::NOERROR()); + installTSIGErrorConstant("FORMERR", TSIGError::FORMERR()); + installTSIGErrorConstant("SERVFAIL", TSIGError::SERVFAIL()); + installTSIGErrorConstant("NXDOMAIN", TSIGError::NXDOMAIN()); + installTSIGErrorConstant("NOTIMP", TSIGError::NOTIMP()); + installTSIGErrorConstant("REFUSED", TSIGError::REFUSED()); + installTSIGErrorConstant("YXDOMAIN", TSIGError::YXDOMAIN()); + installTSIGErrorConstant("YXRRSET", TSIGError::YXRRSET()); + installTSIGErrorConstant("NXRRSET", TSIGError::NXRRSET()); + installTSIGErrorConstant("NOTAUTH", TSIGError::NOTAUTH()); + installTSIGErrorConstant("NOTZONE", TSIGError::NOTZONE()); + installTSIGErrorConstant("RESERVED11", TSIGError::RESERVED11()); + installTSIGErrorConstant("RESERVED12", TSIGError::RESERVED12()); + installTSIGErrorConstant("RESERVED13", TSIGError::RESERVED13()); + installTSIGErrorConstant("RESERVED14", TSIGError::RESERVED14()); + installTSIGErrorConstant("RESERVED15", TSIGError::RESERVED15()); + installTSIGErrorConstant("BAD_SIG", TSIGError::BAD_SIG()); + installTSIGErrorConstant("BAD_KEY", TSIGError::BAD_KEY()); + installTSIGErrorConstant("BAD_TIME", TSIGError::BAD_TIME()); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure in TSIGError initialization: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in TSIGError initialization"); + return (false); + } + + return (true); +} +} // namespace python +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h new file mode 100644 index 0000000000..f0c66cc9a8 --- /dev/null +++ b/src/lib/dns/python/tsigerror_python.h @@ -0,0 +1,44 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __PYTHON_TSIGERROR_H +#define __PYTHON_TSIGERROR_H 1 + +#include + +namespace isc { +namespace dns { +class TSIGError; + +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_TSIGError : public PyObject { +public: + s_TSIGError(); + const TSIGError* cppobj; +}; + +extern PyTypeObject tsigerror_type; + +bool initModulePart_TSIGError(PyObject* mod); + +} // namespace python +} // namespace dns +} // namespace isc +#endif // __PYTHON_TSIGERROR_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/python/tsigerror_python_inc.cc b/src/lib/dns/python/tsigerror_python_inc.cc new file mode 100644 index 0000000000..ed3b605a3e --- /dev/null +++ b/src/lib/dns/python/tsigerror_python_inc.cc @@ -0,0 +1,83 @@ +namespace { +const char* const TSIGError_doc = "\n\ +TSIG errors.\n\ +\n\ +\n\ +The TSIGError class objects represent standard errors related to TSIG\n\ +protocol operations as defined in related specifications, mainly in\n\ +RFC2845.\n\ +\n\ +TSIGError(error_code)\n\ +\n\ +Constructor from the code value.\n\ +\n\ +Exceptions:\n\ + None: \n\ +\n\ +Parameters:\n\ + error_code: The underlying 16-bit error code value of the TSIGError.\n\ +\n\ +TSIGError(rcode)\n\ +\n\ +Constructor from Rcode.\n\ +\n\ +As defined in RFC2845, error code values from 0 to 15 (inclusive) are\n\ +derived from the DNS RCODEs, which are represented via the Rcode class\n\ +in this library. This constructor works as a converter from these\n\ +RCODEs to corresponding TSIGError objects.\n\ +\n\ +Exceptions:\n\ + ValueError: Given rcode is not convertible to TSIGErrors.\n\ +\n\ +Parameters:\n\ + rcode: the Rcode from which the TSIGError should be derived.\n\ +\n\ +"; +const char* const TSIGError_getCode_doc = "get_code() -> integer\n\ +\n\ +Returns the TSIGCode error code value.\n\ +\n\ +Exceptions:\n\ + None: \n\ +\n\ +Return Value(s):\n\ + The underlying code value corresponding to the TSIGError.\n\ +"; +const char* const TSIGError_toText_doc = "to_text() -> string\n\ +\n\ +Convert the TSIGError to a string.\n\ +\n\ +For codes derived from RCODEs up to 15, this method returns the same\n\ +string as Rcode.to_text() for the corresponding code. For other pre-\n\ +defined code values (see TSIGError.CodeValue), this method returns a\n\ +string representation of the \"mnemonic' used for the enum and\n\ +constant objects as defined in RFC2845. For example, the string for\n\ +code value 16 is \"BADSIG\", etc. For other code values it returns a\n\ +string representation of the decimal number of the value, e.g. \"32\",\n\ +\"100\", etc.\n\ +\n\ +Exceptions:\n\ + None\n\ +\n\ +Return Value(s):\n\ + A string representation of the TSIGError.\n\ +"; +const char* const TSIGError_toRcode_doc = "to_rcode() -> Rcode\n\ +\n\ +Convert the TSIGError to a Rcode.\n\ +\n\ +This method returns an Rcode object that is corresponding to the TSIG\n\ +error. The returned Rcode is expected to be used by a verifying server\n\ +to specify the RCODE of a response when TSIG verification fails.\n\ +\n\ +Specifically, this method returns Rcode.NOTAUTH() for the TSIG\n\ +specific errors, BADSIG, BADKEY, BADTIME, as described in RFC2845. For\n\ +errors derived from the standard Rcode (code 0-15), it returns the\n\ +corresponding Rcode. For others, this method returns Rcode.SERVFAIL()\n\ +as a last resort.\n\ +\n\ +Exceptions:\n\ + None: \n\ +\n\ +"; +} diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc index aa87909214..4ca7bcd163 100644 --- a/src/lib/dns/python/tsigkey_python.cc +++ b/src/lib/dns/python/tsigkey_python.cc @@ -55,6 +55,7 @@ void TSIGKey_destroy(s_TSIGKey* self); PyObject* TSIGKey_getKeyName(const s_TSIGKey* self); PyObject* TSIGKey_getAlgorithmName(const s_TSIGKey* self); PyObject* TSIGKey_getSecret(const s_TSIGKey* self); +PyObject* TSIGKey_toText(const s_TSIGKey* self); // This list contains the actual set of functions we have in // python. Each entry has @@ -72,6 +73,8 @@ PyMethodDef TSIGKey_methods[] = { { "get_secret", reinterpret_cast(TSIGKey_getSecret), METH_NOARGS, "Return the value of the TSIG secret." }, + { "to_text", reinterpret_cast(TSIGKey_toText), METH_NOARGS, + "Returns the string representation (name:secret:algorithm)" }, { NULL, NULL, 0, NULL } }; @@ -148,27 +151,33 @@ createNameObject(const Name& source) { int TSIGKey_init(s_TSIGKey* self, PyObject* args) { + const char* str; + const s_Name* key_name; const s_Name* algorithm_name; PyObject* bytes_obj; const char* secret; Py_ssize_t secret_len; - if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name, + + try { + if (PyArg_ParseTuple(args, "s", &str)) { + self->tsigkey = new TSIGKey(str); + return (0); + } else if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name, &name_type, &algorithm_name, &bytes_obj) && - PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) != -1) { - try { - self->tsigkey = new TSIGKey(*key_name->name, - *algorithm_name->name, - secret, secret_len); - } catch (const isc::InvalidParameter& ex) { - PyErr_SetString(po_InvalidParameter, ex.what()); - return (-1); - } catch (...) { - PyErr_SetString(po_IscException, "Unexpected exception"); - return (-1); + PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) != -1) { + self->tsigkey = new TSIGKey(*key_name->name, + *algorithm_name->name, + secret, secret_len); + return (0); } - return (0); + } catch (const isc::InvalidParameter& ex) { + PyErr_SetString(po_InvalidParameter, ex.what()); + return (-1); + } catch (...) { + PyErr_SetString(po_IscException, "Unexpected exception"); + return (-1); } PyErr_Clear(); @@ -201,6 +210,11 @@ TSIGKey_getSecret(const s_TSIGKey* const self) { self->tsigkey->getSecretLength())); } +PyObject* +TSIGKey_toText(const s_TSIGKey* self) { + return (Py_BuildValue("s", self->tsigkey->toText().c_str())); +} + // Module Initialization, all statics are initialized here bool initModulePart_TSIGKey(PyObject* mod) { @@ -402,10 +416,12 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) { PyObject* TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) { s_Name* key_name; + s_Name* algorithm_name; - if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) { + if (PyArg_ParseTuple(args, "O!O!", &name_type, &key_name, + &name_type, &algorithm_name)) { const TSIGKeyRing::FindResult result = - self->keyring->find(*key_name->name); + self->keyring->find(*key_name->name, *algorithm_name->name); if (result.key != NULL) { s_TSIGKey* key = PyObject_New(s_TSIGKey, &tsigkey_type); if (key == NULL) { diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc index 723fc88ae0..96e2a9c895 100644 --- a/src/lib/dns/question.cc +++ b/src/lib/dns/question.cc @@ -56,7 +56,7 @@ Question::toWire(OutputBuffer& buffer) const { } unsigned int -Question::toWire(MessageRenderer& renderer) const { +Question::toWire(AbstractMessageRenderer& renderer) const { renderer.writeName(name_); rrtype_.toWire(renderer); rrclass_.toWire(renderer); diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h index 3c038b371d..b3f3d98356 100644 --- a/src/lib/dns/question.h +++ b/src/lib/dns/question.h @@ -32,7 +32,7 @@ class OutputBuffer; namespace dns { -class MessageRenderer; +class AbstractMessageRenderer; class Question; /// \brief A pointer-like type pointing to an \c Question object. @@ -218,13 +218,13 @@ public: /// \param renderer DNS message rendering context that encapsulates the /// output buffer and name compression information. /// \return 1 - unsigned int toWire(MessageRenderer& renderer) const; + unsigned int toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the Question in the wire format without name compression. /// /// This method behaves like the render version except it doesn't compress /// the owner name. - /// See \c toWire(MessageRenderer& renderer)const. + /// See \c toWire(AbstractMessageRenderer& renderer)const. /// /// \param buffer An output buffer to store the wire data. /// \return 1 diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc index f2bd7ad72a..8211e7fd2b 100644 --- a/src/lib/dns/rdata/any_255/tsig_250.cc +++ b/src/lib/dns/rdata/any_255/tsig_250.cc @@ -24,7 +24,7 @@ #include #include #include - +#include using namespace std; using namespace boost; @@ -313,15 +313,7 @@ TSIG::toText() const { result += encodeBase64(impl_->mac_) + " "; } result += lexical_cast(impl_->original_id_) + " "; - if (impl_->error_ == 16) { // XXX: we'll soon introduce generic converter. - result += "BADSIG "; - } else if (impl_->error_ == 17) { - result += "BADKEY "; - } else if (impl_->error_ == 18) { - result += "BADTIME "; - } else { - result += lexical_cast(impl_->error_) + " "; - } + result += TSIGError(impl_->error_).toText() + " "; result += lexical_cast(impl_->other_data_.size()); if (impl_->other_data_.size() > 0) { result += " " + encodeBase64(impl_->other_data_); diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h index ae3ff6fc26..80035d8d3d 100644 --- a/src/lib/dns/rrclass-placeholder.h +++ b/src/lib/dns/rrclass-placeholder.h @@ -31,7 +31,7 @@ class OutputBuffer; namespace dns { // forward declarations -class MessageRenderer; +class AbstractMessageRenderer; /// /// \brief A standard DNS module exception that is thrown if an RRClass object @@ -169,7 +169,7 @@ public: /// standard exception will be thrown. /// /// \param buffer An output buffer to store the wire data. - void toWire(MessageRenderer& renderer) const; + void toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the \c RRClass in the wire format. /// /// This method renders the class code in network byte order into the diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc index f1bb8d7e07..a28e5cff99 100644 --- a/src/lib/dns/rrclass.cc +++ b/src/lib/dns/rrclass.cc @@ -52,7 +52,7 @@ RRClass::toWire(OutputBuffer& buffer) const { } void -RRClass::toWire(MessageRenderer& renderer) const { +RRClass::toWire(AbstractMessageRenderer& renderer) const { renderer.writeUint16(classcode_); } diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc index e80afdbc42..776d49f650 100644 --- a/src/lib/dns/rrset.cc +++ b/src/lib/dns/rrset.cc @@ -104,8 +104,8 @@ AbstractRRset::toWire(OutputBuffer& buffer) const { } unsigned int -AbstractRRset::toWire(MessageRenderer& renderer) const { - const unsigned int rrs_written = rrsetToWire( +AbstractRRset::toWire(AbstractMessageRenderer& renderer) const { + const unsigned int rrs_written = rrsetToWire( *this, renderer, renderer.getLengthLimit()); if (getRdataCount() > rrs_written) { renderer.setTruncated(); @@ -202,7 +202,7 @@ BasicRRset::toWire(OutputBuffer& buffer) const { } unsigned int -BasicRRset::toWire(MessageRenderer& renderer) const { +BasicRRset::toWire(AbstractMessageRenderer& renderer) const { return (AbstractRRset::toWire(renderer)); } diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h index bf7adc0cf1..6c15b538fb 100644 --- a/src/lib/dns/rrset.h +++ b/src/lib/dns/rrset.h @@ -47,7 +47,7 @@ class Name; class RRType; class RRClass; class RRTTL; -class MessageRenderer; +class AbstractMessageRenderer; class AbstractRRset; class BasicRRset; class RdataIterator; @@ -311,7 +311,7 @@ public: /// \return The number of RRs rendered. If the truncation is necessary /// this value may be different from the number of RDATA objects contained /// in the RRset. - virtual unsigned int toWire(MessageRenderer& renderer) const = 0; + virtual unsigned int toWire(AbstractMessageRenderer& renderer) const = 0; /// \brief Render the RRset in the wire format without any compression. /// @@ -617,7 +617,7 @@ public: /// /// This method simply uses the default implementation. /// See \c AbstractRRset::toWire(MessageRenderer&)const. - virtual unsigned int toWire(MessageRenderer& renderer) const; + virtual unsigned int toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the RRset in the wire format without any compression. /// diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc index a4c4f83812..ecd8cc60c3 100644 --- a/src/lib/dns/rrttl.cc +++ b/src/lib/dns/rrttl.cc @@ -63,7 +63,7 @@ RRTTL::toWire(OutputBuffer& buffer) const { } void -RRTTL::toWire(MessageRenderer& renderer) const { +RRTTL::toWire(AbstractMessageRenderer& renderer) const { renderer.writeUint32(ttlval_); } diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h index 17ec8919af..bf23295f08 100644 --- a/src/lib/dns/rrttl.h +++ b/src/lib/dns/rrttl.h @@ -28,7 +28,7 @@ class OutputBuffer; namespace dns { // forward declarations -class MessageRenderer; +class AbstractMessageRenderer; /// /// \brief A standard DNS module exception that is thrown if an RRTTL object @@ -123,7 +123,7 @@ public: /// /// \param renderer DNS message rendering context that encapsulates the /// output buffer in which the RRTTL is to be stored. - void toWire(MessageRenderer& renderer) const; + void toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the \c RRTTL in the wire format. /// /// This method renders the TTL value in network byte order into the diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index fdd6e1eea0..9783beba7b 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -20,6 +20,7 @@ TESTS += run_unittests run_unittests_SOURCES = unittest_util.h unittest_util.cc run_unittests_SOURCES += edns_unittest.cc run_unittests_SOURCES += messagerenderer_unittest.cc +run_unittests_SOURCES += name_unittest.cc run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc run_unittests_SOURCES += rrttl_unittest.cc run_unittests_SOURCES += opcode_unittest.cc @@ -46,13 +47,18 @@ run_unittests_SOURCES += question_unittest.cc run_unittests_SOURCES += rrparamregistry_unittest.cc run_unittests_SOURCES += masterload_unittest.cc run_unittests_SOURCES += message_unittest.cc +run_unittests_SOURCES += tsig_unittest.cc +run_unittests_SOURCES += tsigerror_unittest.cc run_unittests_SOURCES += tsigkey_unittest.cc +run_unittests_SOURCES += tsigrecord_unittest.cc run_unittests_SOURCES += run_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(GTEST_LDADD) run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la +run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la endif diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 4031e4f40b..c79ea2c414 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -12,9 +12,18 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include + +#include + #include #include +#include + +#include +#include + #include #include #include @@ -26,6 +35,8 @@ #include #include #include +#include +#include #include @@ -53,6 +64,18 @@ using namespace isc::dns::rdata; const uint16_t Message::DEFAULT_MAX_UDPSIZE; const Name test_name("test.example.com"); +namespace isc { +namespace util { +namespace detail { +extern int64_t (*gettimeFunction)(); +} +} +} + +// XXX: this is defined as class static constants, but some compilers +// seemingly cannot find the symbol when used in the EXPECT_xxx macros. +const uint16_t TSIGContext::DEFAULT_FUDGE; + namespace { class MessageTest : public ::testing::Test { protected: @@ -60,7 +83,9 @@ protected: message_parse(Message::PARSE), message_render(Message::RENDER), bogus_section(static_cast( - Message::SECTION_ADDITIONAL + 1)) + Message::SECTION_ADDITIONAL + 1)), + tsig_ctx(TSIGKey("www.example.com:" + "SFuWd/q99SzF8Yzd1QbB9g==")) { rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(3600))); @@ -88,6 +113,9 @@ protected: RRsetPtr rrset_a; // A RRset with two RDATAs RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset + TSIGContext tsig_ctx; + vector expected_data; + static void factoryFromFile(Message& message, const char* datafile); }; @@ -166,6 +194,70 @@ TEST_F(MessageTest, setEDNS) { EXPECT_EQ(edns, message_render.getEDNS()); } +TEST_F(MessageTest, fromWireWithTSIG) { + // Initially there should be no TSIG + EXPECT_EQ(static_cast(NULL), message_parse.getTSIGRecord()); + + // getTSIGRecord() is only valid in the parse mode. + EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation); + + factoryFromFile(message_parse, "message_toWire2.wire"); + const char expected_mac[] = { + 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7, + 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3 + }; + const TSIGRecord* tsig_rr = message_parse.getTSIGRecord(); + ASSERT_NE(static_cast(NULL), tsig_rr); + EXPECT_EQ(Name("www.example.com"), tsig_rr->getName()); + EXPECT_EQ(85, tsig_rr->getLength()); // see TSIGRecordTest.getLength + EXPECT_EQ(TSIGKey::HMACMD5_NAME(), tsig_rr->getRdata().getAlgorithm()); + EXPECT_EQ(0x4da8877a, tsig_rr->getRdata().getTimeSigned()); + EXPECT_EQ(TSIGContext::DEFAULT_FUDGE, tsig_rr->getRdata().getFudge()); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + tsig_rr->getRdata().getMAC(), + tsig_rr->getRdata().getMACSize(), + expected_mac, sizeof(expected_mac)); + EXPECT_EQ(0, tsig_rr->getRdata().getError()); + EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen()); + EXPECT_EQ(static_cast(NULL), tsig_rr->getRdata().getOtherData()); + + // If we clear the message for reuse, the recorded TSIG will be cleared. + message_parse.clear(Message::PARSE); + EXPECT_EQ(static_cast(NULL), message_parse.getTSIGRecord()); +} + +TEST_F(MessageTest, fromWireWithTSIGCompressed) { + // Mostly same as fromWireWithTSIG, but the TSIG owner name is compressed. + factoryFromFile(message_parse, "message_fromWire12.wire"); + const TSIGRecord* tsig_rr = message_parse.getTSIGRecord(); + ASSERT_NE(static_cast(NULL), tsig_rr); + EXPECT_EQ(Name("www.example.com"), tsig_rr->getName()); + // len(www.example.com) = 17, but when fully compressed, the length is + // 2 bytes. So the length of the record should be 15 bytes shorter. + EXPECT_EQ(70, tsig_rr->getLength()); +} + +TEST_F(MessageTest, fromWireWithBadTSIG) { + // Multiple TSIG RRs + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire13.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // TSIG in the answer section (must be in additional) + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire14.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // TSIG is not the last record. + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire15.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // Unexpected RR Class (this will fail in constructing TSIGRecord) + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire16.wire"), + DNSMessageFORMERR); +} + TEST_F(MessageTest, getRRCount) { // by default all counters should be 0 EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION)); @@ -519,6 +611,65 @@ TEST_F(MessageTest, toWireInParseMode) { EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation); } +// See dnssectime_unittest.cc +template +int64_t +testGetTime() { + return (NOW); +} + +void +commonTSIGToWireCheck(Message& message, MessageRenderer& renderer, + TSIGContext& tsig_ctx, const char* const expected_file) +{ + message.setOpcode(Opcode::QUERY()); + message.setRcode(Rcode::NOERROR()); + message.setHeaderFlag(Message::HEADERFLAG_RD, true); + message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), + RRType::A())); + + message.toWire(renderer, tsig_ctx); + vector expected_data; + UnitTestUtil::readWireData(expected_file, expected_data); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(), + renderer.getLength(), + &expected_data[0], expected_data.size()); +} + +TEST_F(MessageTest, toWireWithTSIG) { + // Rendering a message with TSIG. Various special cases specific to + // TSIG are tested in the tsig tests. We only check the message contains + // a TSIG at the end and the ARCOUNT of the header is updated. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + message_render.setQid(0x2d65); + + { + SCOPED_TRACE("Message sign with TSIG"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"); + } +} + +TEST_F(MessageTest, toWireWithEDNSAndTSIG) { + // Similar to the previous test, but with an EDNS before TSIG. + // The wire data check will confirm the ordering. + isc::util::detail::gettimeFunction = testGetTime<0x4db60d1f>; + + message_render.setQid(0x6cd); + + EDNSPtr edns(new EDNS()); + edns->setUDPSize(4096); + message_render.setEDNS(edns); + + { + SCOPED_TRACE("Message sign with TSIG and EDNS"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire3.wire"); + } +} + TEST_F(MessageTest, toWireWithoutOpcode) { message_render.setRcode(Rcode::NOERROR()); EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); @@ -529,6 +680,45 @@ TEST_F(MessageTest, toWireWithoutRcode) { EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); } +TEST_F(MessageTest, toText) { + // Check toText() output for a typical DNS response with records in + // all sections + + factoryFromFile(message_parse, "message_toText1.wire"); + { + SCOPED_TRACE("Message toText test (basic case)"); + ifstream ifs; + unittests::openTestData("message_toText1.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } + + // Another example with EDNS. The expected data was slightly modified + // from the dig output (other than replacing tabs with a space): adding + // a newline after the "OPT PSEUDOSECTION". This is an intentional change + // in our version for better readability. + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_toText2.wire"); + { + SCOPED_TRACE("Message toText test with EDNS"); + ifstream ifs; + unittests::openTestData("message_toText2.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } + + // Another example with TSIG. The expected data was slightly modified + // from the dig output (other than replacing tabs with a space): removing + // a redundant white space at the end of TSIG RDATA. We'd rather consider + // it a dig's defect than a feature. + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_toText3.wire"); + { + SCOPED_TRACE("Message toText test with TSIG"); + ifstream ifs; + unittests::openTestData("message_toText3.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } +} + TEST_F(MessageTest, toTextWithoutOpcode) { message_render.setRcode(Rcode::NOERROR()); EXPECT_THROW(message_render.toText(), InvalidMessageOperation); diff --git a/src/lib/dns/tests/run_unittests.cc b/src/lib/dns/tests/run_unittests.cc index 3cdc61d32d..18eb0a53ab 100644 --- a/src/lib/dns/tests/run_unittests.cc +++ b/src/lib/dns/tests/run_unittests.cc @@ -14,13 +14,16 @@ #include +#include #include int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR); + isc::util::unittests::addTestDataPath(TEST_DATA_SRCDIR); isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR); + isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR); return (RUN_ALL_TESTS()); } diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index 31771bb6cb..cb1bb1cad6 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -3,6 +3,12 @@ CLEANFILES = *.wire BUILT_SOURCES = edns_toWire1.wire edns_toWire2.wire edns_toWire3.wire BUILT_SOURCES += edns_toWire4.wire BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire +BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire +BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire +BUILT_SOURCES += message_fromWire16.wire +BUILT_SOURCES += message_toWire2.wire message_toWire3.wire +BUILT_SOURCES += message_toText1.wire message_toText2.wire +BUILT_SOURCES += message_toText3.wire BUILT_SOURCES += name_toWire5.wire name_toWire6.wire BUILT_SOURCES += rdatafields1.wire rdatafields2.wire rdatafields3.wire BUILT_SOURCES += rdatafields4.wire rdatafields5.wire rdatafields6.wire @@ -33,6 +39,11 @@ BUILT_SOURCES += rdata_tsig_fromWire9.wire BUILT_SOURCES += rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire BUILT_SOURCES += rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire BUILT_SOURCES += rdata_tsig_toWire5.wire +BUILT_SOURCES += tsigrecord_toWire1.wire tsigrecord_toWire2.wire +BUILT_SOURCES += tsig_verify1.wire tsig_verify2.wire tsig_verify3.wire +BUILT_SOURCES += tsig_verify4.wire tsig_verify5.wire tsig_verify6.wire +BUILT_SOURCES += tsig_verify7.wire tsig_verify8.wire tsig_verify9.wire +BUILT_SOURCES += tsig_verify10.wire # NOTE: keep this in sync with real file listing # so is included in tarball @@ -45,8 +56,13 @@ EXTRA_DIST += message_fromWire3 message_fromWire4 EXTRA_DIST += message_fromWire5 message_fromWire6 EXTRA_DIST += message_fromWire7 message_fromWire8 EXTRA_DIST += message_fromWire9 message_fromWire10.spec -EXTRA_DIST += message_fromWire11.spec -EXTRA_DIST += message_toWire1 +EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec +EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec +EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec +EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec +EXTRA_DIST += message_toText1.txt message_toText1.spec +EXTRA_DIST += message_toText2.txt message_toText2.spec +EXTRA_DIST += message_toText3.txt message_toText3.spec EXTRA_DIST += name_fromWire1 name_fromWire2 name_fromWire3_1 name_fromWire3_2 EXTRA_DIST += name_fromWire4 name_fromWire6 name_fromWire7 name_fromWire8 EXTRA_DIST += name_fromWire9 name_fromWire10 name_fromWire11 name_fromWire12 @@ -66,7 +82,8 @@ EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec EXTRA_DIST += rdata_nsec_fromWire10.spec EXTRA_DIST += rdata_nsec3param_fromWire1 -EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire3 +EXTRA_DIST += rdata_nsec3_fromWire1 +EXTRA_DIST += rdata_nsec3_fromWire2.spec rdata_nsec3_fromWire3 EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec EXTRA_DIST += rdata_nsec3_fromWire6.spec rdata_nsec3_fromWire7.spec EXTRA_DIST += rdata_nsec3_fromWire8.spec rdata_nsec3_fromWire9.spec @@ -94,7 +111,11 @@ EXTRA_DIST += rdata_tsig_fromWire9.spec EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec EXTRA_DIST += rdata_tsig_toWire5.spec -EXTRA_DIST += rdata_nsec3_fromWire2.spec +EXTRA_DIST += tsigrecord_toWire1.spec tsigrecord_toWire2.spec +EXTRA_DIST += tsig_verify1.spec tsig_verify2.spec tsig_verify3.spec +EXTRA_DIST += tsig_verify4.spec tsig_verify5.spec tsig_verify6.spec +EXTRA_DIST += tsig_verify7.spec tsig_verify8.spec tsig_verify9.spec +EXTRA_DIST += tsig_verify10.spec .spec.wire: ./gen-wiredata.py -o $@ $< diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen-wiredata.py.in index 72cbdaaedd..fd98c6eb4b 100755 --- a/src/lib/dns/tests/testdata/gen-wiredata.py.in +++ b/src/lib/dns/tests/testdata/gen-wiredata.py.in @@ -15,7 +15,7 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -import configparser, re, time, sys +import configparser, re, time, socket, sys from datetime import datetime from optparse import OptionParser @@ -215,9 +215,76 @@ class EDNS: f.write('# RDLEN=%d\n' % self.rdlen) f.write('%04x\n' % self.rdlen) -class SOA: - # this currently doesn't support name compression within the RDATA. - rdlen = -1 # auto-calculate +class RR: + '''This is a base class for various types of RR test data. + For each RR type (A, AAAA, NS, etc), we define a derived class of RR + to dump type specific RDATA parameters. This class defines parameters + common to all types of RDATA, namely the owner name, RR class and TTL. + The dump() method of derived classes are expected to call dump_header(), + whose default implementation is provided in this class. This method + decides whether to dump the test data as an RR (with name, type, class) + or only as RDATA (with its length), and dumps the corresponding data + via the specified file object. + + By convention we assume derived classes are named after the common + standard mnemonic of the corresponding RR types. For example, the + derived class for the RR type SOA should be named "SOA". + + Configurable parameters are as follows: + - as_rr (bool): Whether or not the data is to be dumped as an RR. False + by default. + - rr_class (string): The RR class of the data. Only meaningful when the + data is dumped as an RR. Default is 'IN'. + - rr_ttl (integer): The TTL value of the RR. Only meaningful when the + data is dumped as an RR. Default is 86400 (1 day). + ''' + + def __init__(self): + self.as_rr = False + # only when as_rr is True, same for class/TTL: + self.rr_name = 'example.com' + self.rr_class = 'IN' + self.rr_ttl = 86400 + def dump_header(self, f, rdlen): + type_txt = self.__class__.__name__ + type_code = parse_value(type_txt, dict_rrtype) + if self.as_rr: + rrclass = parse_value(self.rr_class, dict_rrclass) + f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d RDLEN=%d)\n' % + (type_txt, self.rr_name, + code_totext(rrclass, rdict_rrclass), self.rr_ttl, rdlen)) + f.write('%s %04x %04x %08x %04x\n' % + (encode_name(self.rr_name), type_code, rrclass, + self.rr_ttl, rdlen)) + else: + f.write('\n# %s RDATA (RDLEN=%d)\n' % (type_txt, rdlen)) + f.write('%04x\n' % rdlen) + +class A(RR): + rdlen = 4 # fixed by default + address = '192.0.2.1' + + def dump(self, f): + self.dump_header(f, self.rdlen) + f.write('# Address=%s\n' % (self.address)) + bin_address = socket.inet_aton(self.address) + f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1], + bin_address[2], bin_address[3])) + +class NS(RR): + rdlen = None # auto calculate + nsname = 'ns.example.com' + + def dump(self, f): + nsname_wire = encode_name(self.nsname) + if self.rdlen is None: + self.rdlen = len(nsname_wire) / 2 + self.dump_header(f, self.rdlen) + f.write('# NS name=%s\n' % (self.nsname)) + f.write('%s\n' % nsname_wire) + +class SOA(RR): + rdlen = None # auto-calculate mname = 'ns.example.com' rname = 'root.example.com' serial = 2010012601 @@ -228,11 +295,9 @@ class SOA: def dump(self, f): mname_wire = encode_name(self.mname) rname_wire = encode_name(self.rname) - rdlen = self.rdlen - if rdlen < 0: - rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2) - f.write('\n# SOA RDATA (RDLEN=%d)\n' % rdlen) - f.write('%04x\n' % rdlen); + if self.rdlen is None: + self.rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2) + self.dump_header(f, self.rdlen) f.write('# NNAME=%s RNAME=%s\n' % (self.mname, self.rname)) f.write('%s %s\n' % (mname_wire, rname_wire)) f.write('# SERIAL(%d) REFRESH(%d) RETRY(%d) EXPIRE(%d) MINIMUM(%d)\n' % @@ -432,7 +497,7 @@ class RRSIG: f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer)) f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire)) -class TSIG: +class TSIG(RR): rdlen = None # auto-calculate algorithm = 'hmac-sha256' time_signed = 1286978795 # arbitrarily chosen default @@ -444,12 +509,18 @@ class TSIG: other_len = None # 6 if error is BADTIME; otherwise 0 other_data = None # use time_signed + fudge + 1 for BADTIME dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 } + + # TSIG has some special defaults + def __init__(self): + super().__init__() + self.rr_class = 'ANY' + self.rr_ttl = 0 + def dump(self, f): if str(self.algorithm) == 'hmac-md5': name_wire = encode_name('hmac-md5.sig-alg.reg.int') else: name_wire = encode_name(self.algorithm) - rdlen = self.rdlen mac_size = self.mac_size if mac_size is None: if self.algorithm in self.dict_macsize.keys(): @@ -468,11 +539,10 @@ class TSIG: if self.error == 18 else '' else: other_data = encode_string(self.other_data, other_len) - if rdlen is None: - rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \ - len(other_data) / 2) - f.write('\n# TSIG RDATA (RDLEN=%d)\n' % rdlen) - f.write('%04x\n' % rdlen); + if self.rdlen is None: + self.rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \ + len(other_data) / 2) + self.dump_header(f, self.rdlen) f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' % (self.algorithm, self.time_signed, self.fudge)) f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge)) @@ -488,7 +558,8 @@ def get_config_param(section): config_param = {'name' : (Name, {}), 'header' : (DNSHeader, header_xtables), 'question' : (DNSQuestion, question_xtables), - 'edns' : (EDNS, {}), 'soa' : (SOA, {}), 'txt' : (TXT, {}), + 'edns' : (EDNS, {}), 'a' : (A, {}), 'ns' : (NS, {}), + 'soa' : (SOA, {}), 'txt' : (TXT, {}), 'rp' : (RP, {}), 'rrsig' : (RRSIG, {}), 'nsec' : (NSEC, {}), 'nsec3' : (NSEC3, {}), 'tsig' : (TSIG, {}) } diff --git a/src/lib/dns/tests/testdata/message_fromWire12.spec b/src/lib/dns/tests/testdata/message_fromWire12.spec new file mode 100644 index 0000000000..4eadeed338 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire12.spec @@ -0,0 +1,21 @@ +# +# A simple DNS response message with TSIG signed, but the owner name of TSIG +# is compressed +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: ptr=12 +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_fromWire13.spec b/src/lib/dns/tests/testdata/message_fromWire13.spec new file mode 100644 index 0000000000..e81ec4cba3 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire13.spec @@ -0,0 +1,20 @@ +# +# Invalid TSIG: containing 2 TSIG RRs. +# + +[custom] +sections: header:question:tsig:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 2 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_fromWire14.spec b/src/lib/dns/tests/testdata/message_fromWire14.spec new file mode 100644 index 0000000000..bf68a93269 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire14.spec @@ -0,0 +1,21 @@ +# +# Invalid TSIG: not in the additional section. +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +# TSIG goes to the answer section +ancount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_fromWire15.spec b/src/lib/dns/tests/testdata/message_fromWire15.spec new file mode 100644 index 0000000000..25d810f569 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire15.spec @@ -0,0 +1,22 @@ +# +# Invalid TSIG: not at the end of the message +# + +[custom] +sections: header:question:tsig:edns +[header] +id: 0x2d65 +rd: 1 +arcount: 2 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 +[edns] +# (all default) diff --git a/src/lib/dns/tests/testdata/message_fromWire16.spec b/src/lib/dns/tests/testdata/message_fromWire16.spec new file mode 100644 index 0000000000..be0abc355a --- /dev/null +++ b/src/lib/dns/tests/testdata/message_fromWire16.spec @@ -0,0 +1,21 @@ +# +# Invalid TSIG: not in the additional section. +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_class: IN +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_toText1.spec b/src/lib/dns/tests/testdata/message_toText1.spec new file mode 100644 index 0000000000..b31310e773 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText1.spec @@ -0,0 +1,24 @@ +# +# A standard DNS message (taken from an invocation of dig) +# + +[custom] +sections: header:question:a/1:ns:a/2 +[header] +id: 29174 +qr: 1 +aa: 1 +ancount: 1 +nscount: 1 +arcount: 1 +[question] +name: www.example.com +[a/1] +as_rr: True +rr_name: www.example.com +address: 192.0.2.80 +[ns] +as_rr: True +[a/2] +as_rr: True +rr_name: ns.example.com diff --git a/src/lib/dns/tests/testdata/message_toText1.txt b/src/lib/dns/tests/testdata/message_toText1.txt new file mode 100644 index 0000000000..58c7239bde --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText1.txt @@ -0,0 +1,14 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29174 +;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1 + +;; QUESTION SECTION: +;www.example.com. IN A + +;; ANSWER SECTION: +www.example.com. 86400 IN A 192.0.2.80 + +;; AUTHORITY SECTION: +example.com. 86400 IN NS ns.example.com. + +;; ADDITIONAL SECTION: +ns.example.com. 86400 IN A 192.0.2.1 diff --git a/src/lib/dns/tests/testdata/message_toText2.spec b/src/lib/dns/tests/testdata/message_toText2.spec new file mode 100644 index 0000000000..978aab336a --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText2.spec @@ -0,0 +1,14 @@ +# +# A standard DNS message with EDNS (taken from an invocation of dig) +# + +[custom] +sections: header:question:edns +[header] +id: 45981 +qr: 1 +rcode: refused +arcount: 1 +[question] +[edns] +do: 1 diff --git a/src/lib/dns/tests/testdata/message_toText2.txt b/src/lib/dns/tests/testdata/message_toText2.txt new file mode 100644 index 0000000000..42cc2c1800 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText2.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 45981 +;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags: do; udp: 4096 + +;; QUESTION SECTION: +;example.com. IN A diff --git a/src/lib/dns/tests/testdata/message_toText3.spec b/src/lib/dns/tests/testdata/message_toText3.spec new file mode 100644 index 0000000000..a74ea1b747 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText3.spec @@ -0,0 +1,31 @@ +# +# A standard DNS message with TSIG (taken from an invocation of dig) +# + +[custom] +sections: header:question:a/1:ns:a/2:tsig +[header] +id: 10140 +qr: 1 +aa: 1 +ancount: 1 +nscount: 1 +arcount: 2 +[question] +name: www.example.com +[a/1] +as_rr: True +rr_name: www.example.com +address: 192.0.2.80 +[ns] +as_rr: True +[a/2] +as_rr: True +rr_name: ns.example.com +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 1304384318 +original_id: 10140 +mac: 0x5257c80396f2fa95b20c77ae9a652fb2 diff --git a/src/lib/dns/tests/testdata/message_toText3.txt b/src/lib/dns/tests/testdata/message_toText3.txt new file mode 100644 index 0000000000..359b9c5902 --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toText3.txt @@ -0,0 +1,17 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10140 +;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2 + +;; QUESTION SECTION: +;www.example.com. IN A + +;; ANSWER SECTION: +www.example.com. 86400 IN A 192.0.2.80 + +;; AUTHORITY SECTION: +example.com. 86400 IN NS ns.example.com. + +;; ADDITIONAL SECTION: +ns.example.com. 86400 IN A 192.0.2.1 + +;; TSIG PSEUDOSECTION: +www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. 1304384318 300 16 UlfIA5by+pWyDHeummUvsg== 10140 NOERROR 0 diff --git a/src/lib/dns/tests/testdata/message_toWire2.spec b/src/lib/dns/tests/testdata/message_toWire2.spec new file mode 100644 index 0000000000..d25605249f --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire2.spec @@ -0,0 +1,21 @@ +# +# A simple DNS query message with TSIG signed +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/message_toWire3.spec b/src/lib/dns/tests/testdata/message_toWire3.spec new file mode 100644 index 0000000000..c8e945313e --- /dev/null +++ b/src/lib/dns/tests/testdata/message_toWire3.spec @@ -0,0 +1,22 @@ +# +# A simple DNS query message with EDNS and TSIG +# + +[custom] +sections: header:question:edns:tsig +[header] +id: 0x06cd +rd: 1 +arcount: 2 +[question] +name: www.example.com +[edns] +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4db60d1f +mac_size: 16 +mac: 0x93444053881c83d7eb120e86f25b369e +original_id: 0x06cd diff --git a/src/lib/dns/tests/testdata/tsig_verify1.spec b/src/lib/dns/tests/testdata/tsig_verify1.spec new file mode 100644 index 0000000000..687013a9fd --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify1.spec @@ -0,0 +1,19 @@ +# +# An example of signed AXFR request +# + +[custom] +sections: header:question:tsig +[header] +id: 0x3410 +arcount: 1 +[question] +rrtype: AXFR +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8e951 +mac_size: 16 +mac: 0x35b2fd08268781634400c7c8a5533b13 +original_id: 0x3410 diff --git a/src/lib/dns/tests/testdata/tsig_verify10.spec b/src/lib/dns/tests/testdata/tsig_verify10.spec new file mode 100644 index 0000000000..33ce83ed2a --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify10.spec @@ -0,0 +1,22 @@ +# +# A simple DNS query message with TSIG signed whose MAC is too short +# (only 1 byte) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 1 +mac: 0x22 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify2.spec b/src/lib/dns/tests/testdata/tsig_verify2.spec new file mode 100644 index 0000000000..ff98ca3b61 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify2.spec @@ -0,0 +1,32 @@ +# +# An example of signed AXFR response +# + +[custom] +sections: header:question:soa:tsig +[header] +id: 0x3410 +aa: 1 +qr: 1 +ancount: 1 +arcount: 1 +[question] +rrtype: AXFR +[soa] +# note that names are compressed in this RR +as_rr: True +rr_name: ptr=12 +mname: ns.ptr=12 +rname: root.ptr=12 +serial: 2011041503 +refresh: 7200 +retry: 3600 +expire: 2592000 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8e951 +mac_size: 16 +mac: 0xbdd612cd2c7f9e0648bd6dc23713e83c +original_id: 0x3410 diff --git a/src/lib/dns/tests/testdata/tsig_verify3.spec b/src/lib/dns/tests/testdata/tsig_verify3.spec new file mode 100644 index 0000000000..7e2f797206 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify3.spec @@ -0,0 +1,26 @@ +# +# An example of signed AXFR response (continued) +# + +[custom] +sections: header:ns:tsig +[header] +id: 0x3410 +aa: 1 +qr: 1 +qdcount: 0 +ancount: 1 +arcount: 1 +[ns] +# note that names are compressed in this RR +as_rr: True +rr_name: example.com. +nsname: ns.ptr=12 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8e951 +mac_size: 16 +mac: 0x102458f7f62ddd7d638d746034130968 +original_id: 0x3410 diff --git a/src/lib/dns/tests/testdata/tsig_verify4.spec b/src/lib/dns/tests/testdata/tsig_verify4.spec new file mode 100644 index 0000000000..4ffbbcfb6a --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify4.spec @@ -0,0 +1,27 @@ +# +# An example of signed DNS response with bogus MAC +# + +[custom] +sections: header:question:a:tsig +[header] +id: 0x2d65 +aa: 1 +qr: 1 +rd: 1 +ancount: 1 +arcount: 1 +[question] +name: www.example.com +[a] +as_rr: True +rr_name: ptr=12 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +# bogus MAC +mac: 0xdeadbeefdeadbeefdeadbeefdeadbeef +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify5.spec b/src/lib/dns/tests/testdata/tsig_verify5.spec new file mode 100644 index 0000000000..a6cc64317c --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify5.spec @@ -0,0 +1,26 @@ +# +# An example of signed DNS response +# + +[custom] +sections: header:question:a:tsig +[header] +id: 0x2d65 +aa: 1 +qr: 1 +rd: 1 +ancount: 1 +arcount: 1 +[question] +name: www.example.com +[a] +as_rr: True +rr_name: ptr=12 +[tsig] +as_rr: True +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x8fcda66a7cd1a3b9948eb1869d384a9f +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify6.spec b/src/lib/dns/tests/testdata/tsig_verify6.spec new file mode 100644 index 0000000000..32e081847c --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify6.spec @@ -0,0 +1,21 @@ +# +# Forwarded DNS query message with TSIG signed (header ID != orig ID) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x1035 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify7.spec b/src/lib/dns/tests/testdata/tsig_verify7.spec new file mode 100644 index 0000000000..377578e957 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify7.spec @@ -0,0 +1,21 @@ +# +# DNS query message with TSIG that has empty MAC (invalidly) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 0 +mac: '' +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify8.spec b/src/lib/dns/tests/testdata/tsig_verify8.spec new file mode 100644 index 0000000000..5432d4a1f9 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify8.spec @@ -0,0 +1,23 @@ +# +# DNS query message with TSIG that has empty MAC + BADKEY error +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 0 +mac: '' +# 17: BADKEY +error: 17 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsig_verify9.spec b/src/lib/dns/tests/testdata/tsig_verify9.spec new file mode 100644 index 0000000000..58884558e1 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsig_verify9.spec @@ -0,0 +1,21 @@ +# +# A simple DNS query message with TSIG signed, but TSIG key and algorithm +# names have upper case characters (unusual) +# + +[custom] +sections: header:question:tsig +[header] +id: 0x2d65 +rd: 1 +arcount: 1 +[question] +name: www.example.com +[tsig] +as_rr: True +rr_name: WWW.EXAMPLE.COM +algorithm: HMAC-MD5.SIG-ALG.REG.INT +time_signed: 0x4da8877a +mac_size: 16 +mac: 0x227026ad297beee721ce6c6fff1e9ef3 +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec new file mode 100644 index 0000000000..a25dc46548 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec @@ -0,0 +1,16 @@ +# +# A simple TSIG RR (some of the parameters are taken from a live example +# and don't have a specific meaning) +# + +[custom] +sections: tsig +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0xdadadadadadadadadadadadadadadada +original_id: 0x2d65 diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec new file mode 100644 index 0000000000..f667e4ccc2 --- /dev/null +++ b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec @@ -0,0 +1,19 @@ +# +# TSIG RR after some names that could (unexpectedly) cause name compression +# + +[custom] +sections: name/1:name/2:tsig +[name/1] +name: hmac-md5.sig-alg.reg.int +[name/2] +name: foo.example.com +[tsig] +as_rr: True +# TSIG QNAME won't be compressed +rr_name: www.example.com +algorithm: hmac-md5 +time_signed: 0x4da8877a +mac_size: 16 +mac: 0xdadadadadadadadadadadadadadadada +original_id: 0x2d65 diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc new file mode 100644 index 0000000000..55c3ac296e --- /dev/null +++ b/src/lib/dns/tests/tsig_unittest.cc @@ -0,0 +1,907 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; + +// See dnssectime.cc +namespace isc { +namespace util { +namespace detail { +extern int64_t (*gettimeFunction)(); +} +} +} + +namespace { +// See dnssectime_unittest.cc +template +int64_t +testGetTime() { + return (NOW); +} + +class TSIGTest : public ::testing::Test { +protected: + TSIGTest() : + tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"), + badkey_name("badkey.example.com"), test_class(RRClass::IN()), + test_ttl(86400), message(Message::RENDER), buffer(0), renderer(buffer), + dummy_data(1024, 0xdd), // should be sufficiently large for all tests + dummy_record(badkey_name, any::TSIG(TSIGKey::HMACMD5_NAME(), + 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, + 0, NULL, qid, 0, 0, NULL)) + { + // Make sure we use the system time by default so that we won't be + // confused due to other tests that tweak the time. + isc::util::detail::gettimeFunction = NULL; + + decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret); + tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &secret[0], secret.size()))); + tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &secret[0], + secret.size()))); + } + ~TSIGTest() { + isc::util::detail::gettimeFunction = NULL; + } + + // Many of the tests below create some DNS message and sign it under + // some specific TSIG context. This helper method unifies the common + // logic with slightly different parameters. + ConstTSIGRecordPtr createMessageAndSign(uint16_t qid, const Name& qname, + TSIGContext* ctx, + unsigned int message_flags = + RD_FLAG, + RRType qtype = RRType::A(), + const char* answer_data = NULL, + const RRType* answer_type = NULL, + bool add_question = true, + Rcode rcode = Rcode::NOERROR()); + + void createMessageFromFile(const char* datafile); + + // bit-wise constant flags to configure DNS header flags for test + // messages. + static const unsigned int QR_FLAG = 0x1; + static const unsigned int AA_FLAG = 0x2; + static const unsigned int RD_FLAG = 0x4; + + boost::scoped_ptr tsig_ctx; + boost::scoped_ptr tsig_verify_ctx; + TSIGKeyRing keyring; + const uint16_t qid; + const Name test_name; + const Name badkey_name; + const RRClass test_class; + const RRTTL test_ttl; + Message message; + OutputBuffer buffer; + MessageRenderer renderer; + vector secret; + vector dummy_data; + const TSIGRecord dummy_record; + vector received_data; +}; + +ConstTSIGRecordPtr +TSIGTest::createMessageAndSign(uint16_t id, const Name& qname, + TSIGContext* ctx, unsigned int message_flags, + RRType qtype, const char* answer_data, + const RRType* answer_type, bool add_question, + Rcode rcode) +{ + message.clear(Message::RENDER); + message.setQid(id); + message.setOpcode(Opcode::QUERY()); + message.setRcode(rcode); + if ((message_flags & QR_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_QR); + } + if ((message_flags & AA_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_AA); + } + if ((message_flags & RD_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_RD); + } + if (add_question) { + message.addQuestion(Question(qname, test_class, qtype)); + } + if (answer_data != NULL) { + if (answer_type == NULL) { + answer_type = &qtype; + } + RRsetPtr answer_rrset(new RRset(qname, test_class, *answer_type, + test_ttl)); + answer_rrset->addRdata(createRdata(*answer_type, test_class, + answer_data)); + message.addRRset(Message::SECTION_ANSWER, answer_rrset); + } + renderer.clear(); + message.toWire(renderer); + + TSIGContext::State expected_new_state = + (ctx->getState() == TSIGContext::INIT) ? + TSIGContext::SENT_REQUEST : TSIGContext::SENT_RESPONSE; + ConstTSIGRecordPtr tsig = ctx->sign(id, renderer.getData(), + renderer.getLength()); + EXPECT_EQ(expected_new_state, ctx->getState()); + + return (tsig); +} + +void +TSIGTest::createMessageFromFile(const char* datafile) { + message.clear(Message::PARSE); + received_data.clear(); + UnitTestUtil::readWireData(datafile, received_data); + InputBuffer buffer(&received_data[0], received_data.size()); + message.fromWire(buffer); +} + +void +commonSignChecks(ConstTSIGRecordPtr tsig, uint16_t expected_qid, + uint64_t expected_timesigned, + const uint8_t* expected_mac, size_t expected_maclen, + uint16_t expected_error = 0, + uint16_t expected_otherlen = 0, + const uint8_t* expected_otherdata = NULL, + const Name& expected_algorithm = TSIGKey::HMACMD5_NAME()) +{ + ASSERT_TRUE(tsig != NULL); + const any::TSIG& tsig_rdata = tsig->getRdata(); + + EXPECT_EQ(expected_algorithm, tsig_rdata.getAlgorithm()); + EXPECT_EQ(expected_timesigned, tsig_rdata.getTimeSigned()); + EXPECT_EQ(300, tsig_rdata.getFudge()); + EXPECT_EQ(expected_maclen, tsig_rdata.getMACSize()); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + tsig_rdata.getMAC(), tsig_rdata.getMACSize(), + expected_mac, expected_maclen); + EXPECT_EQ(expected_qid, tsig_rdata.getOriginalID()); + EXPECT_EQ(expected_error, tsig_rdata.getError()); + EXPECT_EQ(expected_otherlen, tsig_rdata.getOtherLen()); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + tsig_rdata.getOtherData(), tsig_rdata.getOtherLen(), + expected_otherdata, expected_otherlen); +} + +void +commonVerifyChecks(TSIGContext& ctx, const TSIGRecord* record, + const void* data, size_t data_len, TSIGError expected_error, + TSIGContext::State expected_new_state = + TSIGContext::VERIFIED_RESPONSE) +{ + EXPECT_EQ(expected_error, ctx.verify(record, data, data_len)); + EXPECT_EQ(expected_error, ctx.getError()); + EXPECT_EQ(expected_new_state, ctx.getState()); +} + +TEST_F(TSIGTest, initialState) { + // Until signing or verifying, the state should be INIT + EXPECT_EQ(TSIGContext::INIT, tsig_ctx->getState()); + + // And there should be no error code. + EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError()); +} + +TEST_F(TSIGTest, constructFromKeyRing) { + // Construct a TSIG context with an empty key ring. Key shouldn't be + // found, and the BAD_KEY error should be recorded. + TSIGContext ctx1(test_name, TSIGKey::HMACMD5_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx1.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx1.getError()); + + // Add a matching key (we don't use the secret so leave it empty), and + // construct it again. This time it should be constructed with a valid + // key. + keyring.add(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), NULL, 0)); + TSIGContext ctx2(test_name, TSIGKey::HMACMD5_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx2.getState()); + EXPECT_EQ(TSIGError::NOERROR(), ctx2.getError()); + + // Similar to the first case except that the key ring isn't empty but + // it doesn't contain a matching key. + TSIGContext ctx3(test_name, TSIGKey::HMACSHA1_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx3.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx3.getError()); + + TSIGContext ctx4(Name("different-key.example"), TSIGKey::HMACMD5_NAME(), + keyring); + EXPECT_EQ(TSIGContext::INIT, ctx4.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx4.getError()); + + // "Unknown" algorithm name will result in BADKEY, too. + TSIGContext ctx5(test_name, Name("unknown.algorithm"), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx5.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx5.getError()); +} + +// Example output generated by +// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com +// QID: 0x2d65 +// Time Signed: 0x00004da8877a +// MAC: 227026ad297beee721ce6c6fff1e9ef3 +const uint8_t common_expected_mac[] = { + 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7, + 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3 +}; +TEST_F(TSIGTest, sign) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + { + SCOPED_TRACE("Sign test for query"); + commonSignChecks(createMessageAndSign(qid, test_name, tsig_ctx.get()), + qid, 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +// Same test as sign, but specifying the key name with upper-case (i.e. +// non canonical) characters. The digest must be the same. It should actually +// be ensured at the level of TSIGKey, but we confirm that at this level, too. +TEST_F(TSIGTest, signUsingUpperCasedKeyName) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + TSIGContext cap_ctx(TSIGKey(Name("WWW.EXAMPLE.COM"), + TSIGKey::HMACMD5_NAME(), + &secret[0], secret.size())); + + { + SCOPED_TRACE("Sign test for query using non canonical key name"); + commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid, + 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +// Same as the previous test, but for the algorithm name. +TEST_F(TSIGTest, signUsingUpperCasedAlgorithmName) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + TSIGContext cap_ctx(TSIGKey(test_name, + Name("HMAC-md5.SIG-alg.REG.int"), + &secret[0], secret.size())); + + { + SCOPED_TRACE("Sign test for query using non canonical algorithm name"); + commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid, + 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +TEST_F(TSIGTest, signAtActualTime) { + // Sign the message using the actual time, and check the accuracy of it. + // We cannot reasonably predict the expected MAC, so don't bother to + // check it. + const uint64_t now = static_cast(time(NULL)); + + { + SCOPED_TRACE("Sign test for query at actual time"); + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + const any::TSIG& tsig_rdata = tsig->getRdata(); + + // Check the resulted time signed is in the range of [now, now + 5] + // (5 is an arbitrary choice). Note that due to the order of the call + // to time() and sign(), time signed must not be smaller than the + // current time. + EXPECT_LE(now, tsig_rdata.getTimeSigned()); + EXPECT_GE(now + 5, tsig_rdata.getTimeSigned()); + } +} + +TEST_F(TSIGTest, signBadData) { + // some specific bad data should be rejected proactively. + const unsigned char dummy_data = 0; + EXPECT_THROW(tsig_ctx->sign(0, NULL, 10), InvalidParameter); + EXPECT_THROW(tsig_ctx->sign(0, &dummy_data, 0), InvalidParameter); +} + +TEST_F(TSIGTest, verifyBadData) { + // the data must at least hold the DNS message header and the specified + // TSIG. + EXPECT_THROW(tsig_ctx->verify(&dummy_record, &dummy_data[0], + 12 + dummy_record.getLength() - 1), + InvalidParameter); + + // And the data must not be NULL. + EXPECT_THROW(tsig_ctx->verify(&dummy_record, NULL, + 12 + dummy_record.getLength()), + InvalidParameter); +} + +#ifdef ENABLE_CUSTOM_OPERATOR_NEW +// We enable this test only when we enable custom new/delete at build time +// We could enable/disable the test runtime using the gtest filter, but +// we'd basically like to minimize the number of disabled tests (they +// should generally be considered tests that temporarily fail and should +// be fixed). +TEST_F(TSIGTest, signExceptionSafety) { + // Check sign() provides the strong exception guarantee for the simpler + // case (with a key error and empty MAC). The general case is more + // complicated and involves more memory allocation, so the test result + // won't be reliable. + + commonVerifyChecks(*tsig_verify_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::RECEIVED_REQUEST); + + try { + int dummydata; + isc::util::unittests::force_throw_on_new = true; + isc::util::unittests::throw_size_on_new = sizeof(TSIGRecord); + tsig_verify_ctx->sign(0, &dummydata, sizeof(dummydata)); + isc::util::unittests::force_throw_on_new = false; + ASSERT_FALSE(true) << "Expected throw on new, but it didn't happen"; + } catch (const std::bad_alloc&) { + isc::util::unittests::force_throw_on_new = false; + + // sign() threw, so the state should still be RECEIVED_REQUEST + EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState()); + } + isc::util::unittests::force_throw_on_new = false; +} +#endif // ENABLE_CUSTOM_OPERATOR_NEW + +// Same test as "sign" but use a different algorithm just to confirm we don't +// naively hardcode constants specific to a particular algorithm. +// Test data generated by +// "dig -y hmac-sha1:www.example.com:MA+QDhXbyqUak+qnMFyTyEirzng= www.example.com" +// QID: 0x0967, RDflag +// Current Time: 00004da8be86 +// Time Signed: 00004dae7d5f +// HMAC Size: 20 +// HMAC: 415340c7daf824ed684ee586f7b5a67a2febc0d3 +TEST_F(TSIGTest, signUsingHMACSHA1) { + isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>; + + secret.clear(); + decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret); + TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(), + &secret[0], secret.size())); + + const uint16_t sha1_qid = 0x0967; + const uint8_t expected_mac[] = { + 0x41, 0x53, 0x40, 0xc7, 0xda, 0xf8, 0x24, 0xed, 0x68, 0x4e, + 0xe5, 0x86, 0xf7, 0xb5, 0xa6, 0x7a, 0x2f, 0xeb, 0xc0, 0xd3 + }; + { + SCOPED_TRACE("Sign test using HMAC-SHA1"); + commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx), + sha1_qid, 0x4dae7d5f, expected_mac, + sizeof(expected_mac), 0, 0, NULL, + TSIGKey::HMACSHA1_NAME()); + } +} + +// The first part of this test checks verifying the signed query used for +// the "sign" test. +// The second part of this test generates a signed response to the signed +// query as follows: +// Answer: www.example.com. 86400 IN A 192.0.2.1 +// MAC: 8fcda66a7cd1a3b9948eb1869d384a9f +TEST_F(TSIGTest, verifyThenSignResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // This test data for the message test has the same wire format data + // as the message used in the "sign" test. + createMessageFromFile("message_toWire2.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } + + // Transform the original message to a response, then sign the response + // with the context of "verified state". + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_verify_ctx.get(), + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::A(), "192.0.2.1"); + const uint8_t expected_mac[] = { + 0x8f, 0xcd, 0xa6, 0x6a, 0x7c, 0xd1, 0xa3, 0xb9, + 0x94, 0x8e, 0xb1, 0x86, 0x9d, 0x38, 0x4a, 0x9f + }; + { + SCOPED_TRACE("Sign test for response"); + commonSignChecks(tsig, qid, 0x4da8877a, expected_mac, + sizeof(expected_mac)); + } +} + +TEST_F(TSIGTest, verifyUpperCaseNames) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // This test data for the message test has the same wire format data + // as the message used in the "sign" test. + createMessageFromFile("tsig_verify9.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, verifyForwardedMessage) { + // Similar to the first part of the previous test, but this test emulates + // the "forward" case, where the ID of the Header and the original ID in + // TSIG is different. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageFromFile("tsig_verify6.wire"); + { + SCOPED_TRACE("Verify test for forwarded request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } +} + +// Example of signing multiple messages in a single TCP stream, +// taken from data using BIND 9's "one-answer" transfer-format. +// Request: +// QID: 0x3410, flags (none) +// Question: example.com/IN/AXFR +// Time Signed: 0x4da8e951 +// MAC: 35b2fd08268781634400c7c8a5533b13 +// First message: +// QID: 0x3410, flags QR, AA +// Question: example.com/IN/AXFR +// Answer: example.com. 86400 IN SOA ns.example.com. root.example.com. ( +// 2011041503 7200 3600 2592000 1200) +// MAC: bdd612cd2c7f9e0648bd6dc23713e83c +// Second message: +// Answer: example.com. 86400 IN NS ns.example.com. +// MAC: 102458f7f62ddd7d638d746034130968 +TEST_F(TSIGTest, signContinuation) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8e951>; + + const uint16_t axfr_qid = 0x3410; + const Name zone_name("example.com"); + + // Create and sign the AXFR request + ConstTSIGRecordPtr tsig = createMessageAndSign(axfr_qid, zone_name, + tsig_ctx.get(), 0, + RRType::AXFR()); + // Then verify it (the wire format test data should contain the same + // message data, and verification should succeed). + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify1.wire", received_data); + { + SCOPED_TRACE("Verify AXFR query"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR(), + TSIGContext::RECEIVED_REQUEST); + } + + // Create and sign the first response message + tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(), + AA_FLAG|QR_FLAG, RRType::AXFR(), + "ns.example.com. root.example.com. " + "2011041503 7200 3600 2592000 1200", + &RRType::SOA()); + + // Then verify it at the requester side. + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify2.wire", received_data); + { + SCOPED_TRACE("Verify first AXFR response"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR()); + } + + // Create and sign the second response message + const uint8_t expected_mac[] = { + 0x10, 0x24, 0x58, 0xf7, 0xf6, 0x2d, 0xdd, 0x7d, + 0x63, 0x8d, 0x74, 0x60, 0x34, 0x13, 0x09, 0x68 + }; + { + SCOPED_TRACE("Sign test for continued response in TCP stream"); + tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(), + AA_FLAG|QR_FLAG, RRType::AXFR(), + "ns.example.com.", &RRType::NS(), false); + commonSignChecks(tsig, axfr_qid, 0x4da8e951, expected_mac, + sizeof(expected_mac)); + } + + // Then verify it at the requester side. + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify3.wire", received_data); + { + SCOPED_TRACE("Verify second AXFR response"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR()); + } +} + +// BADTIME example, taken from data using specially hacked BIND 9's nsupdate +// Query: +// QID: 0x1830, RD flag +// Current Time: 00004da8be86 +// Time Signed: 00004da8b9d6 +// Question: www.example.com/IN/SOA +//(mac) 8406 7d50 b8e7 d054 3d50 5bd9 de2a bb68 +// Response: +// QRbit, RCODE=9(NOTAUTH) +// Time Signed: 00004da8b9d6 (the one in the query) +// MAC: d4b043f6f44495ec8a01260e39159d76 +// Error: 0x12 (BADTIME), Other Len: 6 +// Other data: 00004da8be86 +TEST_F(TSIGTest, badtimeResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + const uint16_t test_qid = 0x7fc4; + ConstTSIGRecordPtr tsig = createMessageAndSign(test_qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // "advance the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8be86>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } + + // make and sign a response in the context of TSIG error. + tsig = createMessageAndSign(test_qid, test_name, tsig_verify_ctx.get(), + QR_FLAG, RRType::SOA(), NULL, NULL, + true, Rcode::NOTAUTH()); + const uint8_t expected_otherdata[] = { 0, 0, 0x4d, 0xa8, 0xbe, 0x86 }; + const uint8_t expected_mac[] = { + 0xd4, 0xb0, 0x43, 0xf6, 0xf4, 0x44, 0x95, 0xec, + 0x8a, 0x01, 0x26, 0x0e, 0x39, 0x15, 0x9d, 0x76 + }; + { + SCOPED_TRACE("Sign test for response with BADTIME"); + commonSignChecks(tsig, message.getQid(), 0x4da8b9d6, + expected_mac, sizeof(expected_mac), + 18, // error: BADTIME + sizeof(expected_otherdata), + expected_otherdata); + } +} + +TEST_F(TSIGTest, badtimeResponse2) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // "rewind the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 600>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to too future SIG"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, badtimeBoundaries) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + // Test various boundary conditions. We intentionally use the magic + // number of 300 instead of the constant variable for testing. + // In the okay cases, signature is not correct, but it's sufficient to + // check the error code isn't BADTIME for the purpose of this test. + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 301>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 300>; + EXPECT_NE(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 301>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 300>; + EXPECT_NE(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); +} + +TEST_F(TSIGTest, badtimeOverflow) { + isc::util::detail::gettimeFunction = testGetTime<200>; + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // This should be in the okay range, but since "200 - fudge" overflows + // and we compare them as 64-bit unsigned integers, it results in a false + // positive (we intentionally accept that). + isc::util::detail::gettimeFunction = testGetTime<100>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); +} + +TEST_F(TSIGTest, badsigResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // Try to sign a simple message with bogus secret. It should fail + // with BADSIG. + createMessageFromFile("message_toWire2.wire"); + TSIGContext bad_ctx(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), + &dummy_data[0], dummy_data.size())); + { + SCOPED_TRACE("Verify resulting in BADSIG"); + commonVerifyChecks(bad_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + + // Sign the same message (which doesn't matter for this test) with the + // context of "checked state". + { + SCOPED_TRACE("Sign test for response with BADSIG error"); + commonSignChecks(createMessageAndSign(qid, test_name, &bad_ctx), + message.getQid(), 0x4da8877a, NULL, 0, + 16); // 16: BADSIG + } +} + +TEST_F(TSIGTest, badkeyResponse) { + // A similar test as badsigResponse but for BADKEY + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(), + keyring)); + { + SCOPED_TRACE("Verify resulting in BADKEY"); + commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::RECEIVED_REQUEST); + } + + { + SCOPED_TRACE("Sign test for response with BADKEY error"); + ConstTSIGRecordPtr sig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + EXPECT_EQ(badkey_name, sig->getName()); + commonSignChecks(sig, qid, 0x4da8877a, NULL, 0, 17); // 17: BADKEY + } +} + +TEST_F(TSIGTest, badkeyForResponse) { + // "BADKEY" case for a response to a signed message + createMessageAndSign(qid, test_name, tsig_ctx.get()); + { + SCOPED_TRACE("Verify a response resulting in BADKEY"); + commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::SENT_REQUEST); + } + + // A similar case with a different algorithm + const TSIGRecord dummy_record2(test_name, + any::TSIG(TSIGKey::HMACSHA1_NAME(), + 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, + 0, NULL, qid, 0, 0, NULL)); + { + SCOPED_TRACE("Verify a response resulting in BADKEY due to bad alg"); + commonVerifyChecks(*tsig_ctx, &dummy_record2, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::SENT_REQUEST); + } +} + +TEST_F(TSIGTest, badsigThenValidate) { + // According to RFC2845 4.6, if TSIG verification fails the client + // should discard that message and wait for another signed response. + // This test emulates that situation. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageAndSign(qid, test_name, tsig_ctx.get()); + + createMessageFromFile("tsig_verify4.wire"); + { + SCOPED_TRACE("Verify a response that should fail due to BADSIG"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::SENT_REQUEST); + } + + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a BADSIG failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, nosigThenValidate) { + // Similar to the previous test, but the first response doesn't contain + // TSIG. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageAndSign(qid, test_name, tsig_ctx.get()); + + { + SCOPED_TRACE("Verify a response without TSIG that should exist"); + commonVerifyChecks(*tsig_ctx, NULL, &dummy_data[0], + dummy_data.size(), TSIGError::FORMERR(), + TSIGContext::SENT_REQUEST); + } + + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a FORMERR failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, badtimeThenValidate) { + // Similar to the previous test, but the first response results in BADTIME. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + + // "advance the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a + 600>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::SENT_REQUEST); + } + + // revert the clock again. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a BADTIME failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, emptyMAC) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // We don't allow empty MAC unless the TSIG error is BADSIG or BADKEY. + createMessageFromFile("tsig_verify7.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + + // If the empty MAC comes with a BADKEY error, the error is passed + // transparently. + createMessageFromFile("tsig_verify8.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_KEY(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, verifyAfterSendResponse) { + // Once the context is used for sending a signed response, it shouldn't + // be used for further verification. + + // The following are essentially the same as what verifyThenSignResponse + // does with simplification. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("message_toWire2.wire"); + tsig_verify_ctx->verify(message.getTSIGRecord(), &received_data[0], + received_data.size()); + EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState()); + createMessageAndSign(qid, test_name, tsig_verify_ctx.get(), + QR_FLAG|AA_FLAG|RD_FLAG, RRType::A(), "192.0.2.1"); + EXPECT_EQ(TSIGContext::SENT_RESPONSE, tsig_verify_ctx->getState()); + + // Now trying further verification. + createMessageFromFile("message_toWire2.wire"); + EXPECT_THROW(tsig_verify_ctx->verify(message.getTSIGRecord(), + &received_data[0], + received_data.size()), + TSIGContextError); +} + +TEST_F(TSIGTest, signAfterVerified) { + // Likewise, once the context verifies a response, it shouldn't for + // signing any more. + + // The following are borrowed from badsigThenValidate (without the + // intermediate failure) + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageAndSign(qid, test_name, tsig_ctx.get()); + createMessageFromFile("tsig_verify5.wire"); + tsig_ctx->verify(message.getTSIGRecord(), &received_data[0], + received_data.size()); + EXPECT_EQ(TSIGContext::VERIFIED_RESPONSE, tsig_ctx->getState()); + + // Now trying further signing. + EXPECT_THROW(createMessageAndSign(qid, test_name, tsig_ctx.get()), + TSIGContextError); +} + +TEST_F(TSIGTest, tooShortMAC) { + // Too short MAC should be rejected. + // Note: when we implement RFC4635-based checks, the error code will + // (probably) be FORMERR. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("tsig_verify10.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } +} + +} // end namespace diff --git a/src/lib/dns/tests/tsigerror_unittest.cc b/src/lib/dns/tests/tsigerror_unittest.cc new file mode 100644 index 0000000000..bb08aef9b1 --- /dev/null +++ b/src/lib/dns/tests/tsigerror_unittest.cc @@ -0,0 +1,116 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::dns; + +namespace { +TEST(TSIGErrorTest, constructFromErrorCode) { + // These are pretty trivial, and also test getCode(); + EXPECT_EQ(0, TSIGError(0).getCode()); + EXPECT_EQ(18, TSIGError(18).getCode()); + EXPECT_EQ(65535, TSIGError(65535).getCode()); +} + +TEST(TSIGErrorTest, constructFromRcode) { + // We use RCODE for code values from 0-15. + EXPECT_EQ(0, TSIGError(Rcode::NOERROR()).getCode()); + EXPECT_EQ(15, TSIGError(Rcode(15)).getCode()); + + // From error code 16 TSIG errors define a separate space, so passing + // corresponding RCODE for such code values should be prohibited. + EXPECT_THROW(TSIGError(Rcode(16)).getCode(), OutOfRange); +} + +TEST(TSIGErrorTest, constants) { + // We'll only test arbitrarily chosen subsets of the codes. + // This class is quite simple, so it should be suffice. + + EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError(16).getCode()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError(17).getCode()); + EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError(18).getCode()); + + EXPECT_EQ(0, TSIGError::NOERROR().getCode()); + EXPECT_EQ(9, TSIGError::NOTAUTH().getCode()); + EXPECT_EQ(14, TSIGError::RESERVED14().getCode()); + EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError::BAD_SIG().getCode()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError::BAD_KEY().getCode()); + EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError::BAD_TIME().getCode()); +} + +TEST(TSIGErrorTest, equal) { + EXPECT_TRUE(TSIGError::NOERROR() == TSIGError(Rcode::NOERROR())); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()) == TSIGError::NOERROR()); + EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR()))); + EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR()))); + + EXPECT_TRUE(TSIGError::BAD_SIG() == TSIGError(16)); + EXPECT_TRUE(TSIGError(16) == TSIGError::BAD_SIG()); + EXPECT_TRUE(TSIGError::BAD_SIG().equals(TSIGError(16))); + EXPECT_TRUE(TSIGError(16).equals(TSIGError::BAD_SIG())); +} + +TEST(TSIGErrorTest, nequal) { + EXPECT_TRUE(TSIGError::BAD_KEY() != TSIGError(Rcode::NOERROR())); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()) != TSIGError::BAD_KEY()); + EXPECT_TRUE(TSIGError::BAD_KEY().nequals(TSIGError(Rcode::NOERROR()))); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()).nequals(TSIGError::BAD_KEY())); +} + +TEST(TSIGErrorTest, toText) { + // TSIGError derived from the standard Rcode + EXPECT_EQ("NOERROR", TSIGError(Rcode::NOERROR()).toText()); + + // Well known TSIG errors + EXPECT_EQ("BADSIG", TSIGError::BAD_SIG().toText()); + EXPECT_EQ("BADKEY", TSIGError::BAD_KEY().toText()); + EXPECT_EQ("BADTIME", TSIGError::BAD_TIME().toText()); + + // Unknown (or not yet supported) codes. Simply converted as numeric. + EXPECT_EQ("19", TSIGError(19).toText()); + EXPECT_EQ("65535", TSIGError(65535).toText()); +} + +TEST(TSIGErrorTest, toRcode) { + // TSIGError derived from the standard Rcode + EXPECT_EQ(Rcode::NOERROR(), TSIGError(Rcode::NOERROR()).toRcode()); + + // Well known TSIG errors + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_SIG().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_KEY().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_TIME().toRcode()); + + // Unknown (or not yet supported) codes are treated as SERVFAIL. + EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(19).toRcode()); + EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(65535).toRcode()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST(TSIGErrorTest, LeftShiftOperator) { + ostringstream oss; + oss << TSIGError::BAD_KEY(); + EXPECT_EQ(TSIGError::BAD_KEY().toText(), oss.str()); +} +} // end namespace diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc index 6b2b8c5832..dac3c49710 100644 --- a/src/lib/dns/tests/tsigkey_unittest.cc +++ b/src/lib/dns/tests/tsigkey_unittest.cc @@ -18,6 +18,8 @@ #include +#include + #include #include @@ -31,13 +33,22 @@ class TSIGKeyTest : public ::testing::Test { protected: TSIGKeyTest() : secret("someRandomData"), key_name("example.com") {} string secret; - Name key_name; + const Name key_name; }; TEST_F(TSIGKeyTest, algorithmNames) { EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME()); EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME()); EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME()); + + // Also check conversion to cryptolink definitions + EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA1, TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name, + TSIGKey::HMACSHA256_NAME(), + NULL, 0).getAlgorithm()); } TEST_F(TSIGKeyTest, construct) { @@ -48,9 +59,13 @@ TEST_F(TSIGKeyTest, construct) { EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret.c_str(), secret.size(), key.getSecret(), key.getSecretLength()); + // "unknown" algorithm is only accepted with empty secret. EXPECT_THROW(TSIGKey(key_name, Name("unknown-alg"), secret.c_str(), secret.size()), isc::InvalidParameter); + TSIGKey key2(key_name, Name("unknown-alg"), NULL, 0); + EXPECT_EQ(key_name, key2.getKeyName()); + EXPECT_EQ(Name("unknown-alg"), key2.getAlgorithmName()); // The algorithm name should be converted to the canonical form. EXPECT_EQ("hmac-sha1.", @@ -58,6 +73,12 @@ TEST_F(TSIGKeyTest, construct) { secret.c_str(), secret.size()).getAlgorithmName().toText()); + // Same for key name + EXPECT_EQ("example.com.", + TSIGKey(Name("EXAMPLE.CoM."), TSIGKey::HMACSHA256_NAME(), + secret.c_str(), + secret.size()).getKeyName().toText()); + // Invalid combinations of secret and secret_len: EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), secret.c_str(), 0), isc::InvalidParameter); @@ -109,12 +130,18 @@ class TSIGKeyRingTest : public ::testing::Test { protected: TSIGKeyRingTest() : key_name("example.com"), + md5_name("hmac-md5.sig-alg.reg.int"), + sha1_name("hmac-sha1"), + sha256_name("hmac-sha256"), secretstring("anotherRandomData"), secret(secretstring.c_str()), secret_len(secretstring.size()) {} TSIGKeyRing keyring; - Name key_name; + const Name key_name; + const Name md5_name; + const Name sha1_name; + const Name sha256_name; private: const string secretstring; protected: @@ -134,8 +161,8 @@ TEST_F(TSIGKeyRingTest, add) { EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), secret, secret_len))); - // keys are identified their names, the same name of key with a different - // algorithm would be considered a duplicate. + // keys are identified by their names, the same name of key with a + // different algorithm would be considered a duplicate. EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( TSIGKey(Name("example.com"), TSIGKey::HMACSHA1_NAME(), secret, secret_len))); @@ -187,44 +214,90 @@ TEST_F(TSIGKeyRingTest, removeFromSome) { } TEST_F(TSIGKeyRingTest, find) { - EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.find(key_name).code); - EXPECT_EQ(static_cast(NULL), keyring.find(key_name).key); + // If the keyring is empty the search should fail. + EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.find(key_name, md5_name).code); + EXPECT_EQ(static_cast(NULL), + keyring.find(key_name, md5_name).key); - EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( - TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), - secret, secret_len))); - const TSIGKeyRing::FindResult result(keyring.find(key_name)); - EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code); - EXPECT_EQ(key_name, result.key->getKeyName()); - EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result.key->getAlgorithmName()); + // Add a key and try to find it. Should succeed. + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name, + secret, secret_len))); + const TSIGKeyRing::FindResult result1(keyring.find(key_name, sha256_name)); + EXPECT_EQ(TSIGKeyRing::SUCCESS, result1.code); + EXPECT_EQ(key_name, result1.key->getKeyName()); + EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result1.key->getAlgorithmName()); EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret, secret_len, - result.key->getSecret(), - result.key->getSecretLength()); + result1.key->getSecret(), + result1.key->getSecretLength()); + + // If either key name or algorithm doesn't match, search should fail. + const TSIGKeyRing::FindResult result2 = + keyring.find(Name("different-key.example"), sha256_name); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, result2.code); + EXPECT_EQ(static_cast(NULL), result2.key); + + const TSIGKeyRing::FindResult result3 = keyring.find(key_name, md5_name); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, result3.code); + EXPECT_EQ(static_cast(NULL), result3.key); } TEST_F(TSIGKeyRingTest, findFromSome) { // essentially the same test, but search a larger set - EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( - TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), - secret, secret_len))); - EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( - TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(), - secret, secret_len))); - EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( - TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(), - secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name, + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("another.example"), + md5_name, + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("more.example"), + sha1_name, + secret, secret_len))); const TSIGKeyRing::FindResult result( - keyring.find(Name("another.example"))); + keyring.find(Name("another.example"), md5_name)); EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code); EXPECT_EQ(Name("another.example"), result.key->getKeyName()); EXPECT_EQ(TSIGKey::HMACMD5_NAME(), result.key->getAlgorithmName()); EXPECT_EQ(TSIGKeyRing::NOTFOUND, - keyring.find(Name("noexist.example")).code); + keyring.find(Name("noexist.example"), sha1_name).code); EXPECT_EQ(static_cast(NULL), - keyring.find(Name("noexist.example")).key); + keyring.find(Name("noexist.example"), sha256_name).key); + + EXPECT_EQ(TSIGKeyRing::NOTFOUND, + keyring.find(Name("another.example"), sha1_name).code); + EXPECT_EQ(static_cast(NULL), + keyring.find(Name("another.example"), sha256_name).key); } +TEST(TSIGStringTest, TSIGKeyFromToString) { + TSIGKey k1 = TSIGKey("test.example:MSG6Ng==:hmac-md5.sig-alg.reg.int"); + TSIGKey k2 = TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int."); + TSIGKey k3 = TSIGKey("test.example:MSG6Ng=="); + TSIGKey k4 = TSIGKey(Name("test.example."), Name("hmac-sha1."), NULL, 0); + // "Unknown" key with empty secret is okay + TSIGKey k5 = TSIGKey("test.example.::unknown"); + + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k1.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k2.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k3.toText()); + EXPECT_EQ("test.example.::hmac-sha1.", k4.toText()); + EXPECT_EQ(Name("test.example."), k5.getKeyName()); + EXPECT_EQ(Name("unknown"), k5.getAlgorithmName()); + + EXPECT_THROW(TSIGKey(""), isc::InvalidParameter); + EXPECT_THROW(TSIGKey(":"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("::"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("..:aa:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example:xxxx:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.::"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:unknown"), isc::InvalidParameter); +} + + } // end namespace diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc new file mode 100644 index 0000000000..e1b3e9318c --- /dev/null +++ b/src/lib/dns/tests/tsigrecord_unittest.cc @@ -0,0 +1,159 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace isc::util; +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; + +namespace { +class TSIGRecordTest : public ::testing::Test { +protected: + TSIGRecordTest() : + test_name("www.example.com"), test_mac(16, 0xda), + test_rdata(any::TSIG(TSIGKey::HMACMD5_NAME(), 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, + test_mac.size(), &test_mac[0], + 0x2d65, 0, 0, NULL)), + test_record(test_name, test_rdata), + buffer(0), renderer(buffer) + {} + const Name test_name; + vector test_mac; + const any::TSIG test_rdata; + const TSIGRecord test_record; + OutputBuffer buffer; + MessageRenderer renderer; + vector data; +}; + +TEST_F(TSIGRecordTest, getName) { + EXPECT_EQ(test_name, test_record.getName()); +} + +TEST_F(TSIGRecordTest, getLength) { + // 85 = 17 + 26 + 16 + 26 + // len(www.example.com) = 17 + // len(hmac-md5.sig-alg.reg.int) = 26 + // len(MAC) = 16 + // the rest are fixed length fields (26 in total) + EXPECT_EQ(85, test_record.getLength()); +} + +TEST_F(TSIGRecordTest, fromParams) { + // Construct the same TSIG RR as test_record from parameters. + // See the getLength test for the magic number of 85 (although it + // actually doesn't matter) + const TSIGRecord record(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 85); + // Perform straight sanity checks + EXPECT_EQ(test_name, record.getName()); + EXPECT_EQ(85, record.getLength()); + EXPECT_EQ(0, test_rdata.compare(record.getRdata())); + + // The constructor doesn't check the length... + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 82)); + // ...even for impossibly small values... + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 1)); + // ...or too large values. + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 65536)); + + // RDATA must indeed be TSIG + EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), in::A("192.0.2.1"), 85), + DNSMessageFORMERR); + + // Unexpected class + EXPECT_THROW(TSIGRecord(test_name, RRClass::IN(), TSIGRecord::getTTL(), + test_rdata, 85), DNSMessageFORMERR); + + // Unexpected TTL + EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + RRTTL(3600), test_rdata, 85), DNSMessageFORMERR); +} + +TEST_F(TSIGRecordTest, recordToWire) { + UnitTestUtil::readWireData("tsigrecord_toWire1.wire", data); + EXPECT_EQ(1, test_record.toWire(renderer)); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + renderer.getData(), renderer.getLength(), + &data[0], data.size()); + + // Same test for a dumb buffer + buffer.clear(); + EXPECT_EQ(1, test_record.toWire(buffer)); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + buffer.getData(), buffer.getLength(), + &data[0], data.size()); +} + +TEST_F(TSIGRecordTest, recordToOLongToWire) { + // By setting the limit to "record length - 1", it will fail, and the + // renderer will be marked as "truncated". + renderer.setLengthLimit(test_record.getLength() - 1); + EXPECT_FALSE(renderer.isTruncated()); // not marked before render attempt + EXPECT_EQ(0, test_record.toWire(renderer)); + EXPECT_TRUE(renderer.isTruncated()); +} + +TEST_F(TSIGRecordTest, recordToWireAfterNames) { + // A similar test but the TSIG RR follows some domain names that could + // cause name compression inside TSIG. Our implementation shouldn't + // compress either owner (key) name or the algorithm name. This test + // confirms that. + + UnitTestUtil::readWireData("tsigrecord_toWire2.wire", data); + renderer.writeName(TSIGKey::HMACMD5_NAME()); + renderer.writeName(Name("foo.example.com")); + EXPECT_EQ(1, test_record.toWire(renderer)); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + renderer.getData(), renderer.getLength(), + &data[0], data.size()); +} + +TEST_F(TSIGRecordTest, toText) { + EXPECT_EQ("www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. " + "1302890362 300 16 2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n", + test_record.toText()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(TSIGRecordTest, LeftShiftOperator) { + ostringstream oss; + oss << test_record; + EXPECT_EQ(test_record.toText(), oss.str()); +} +} // end namespace diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc new file mode 100644 index 0000000000..714b2a596e --- /dev/null +++ b/src/lib/dns/tsig.cc @@ -0,0 +1,453 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include + +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace isc::util; +using namespace isc::cryptolink; +using namespace isc::dns::rdata; + +namespace isc { +namespace dns { +namespace { +typedef boost::shared_ptr HMACPtr; + +// TSIG uses 48-bit unsigned integer to represent time signed. +// Since gettimeWrapper() returns a 64-bit *signed* integer, we +// make sure it's stored in an unsigned 64-bit integer variable and +// represents a value in the expected range. (In reality, however, +// gettimeWrapper() will return a positive integer that will fit +// in 48 bits) +uint64_t +getTSIGTime() { + return (detail::gettimeWrapper() & 0x0000ffffffffffffULL); +} +} + +struct TSIGContext::TSIGContextImpl { + TSIGContextImpl(const TSIGKey& key) : + state_(INIT), key_(key), error_(Rcode::NOERROR()), + previous_timesigned_(0) + {} + + // This helper method is used from verify(). It's expected to be called + // just before verify() returns. It updates internal state based on + // the verification result and return the TSIGError to be returned to + // the caller of verify(), so that verify() can call this method within + // its 'return' statement. + TSIGError postVerifyUpdate(TSIGError error, const void* digest, + uint16_t digest_len) + { + if (state_ == INIT) { + state_ = RECEIVED_REQUEST; + } else if (state_ == SENT_REQUEST && error == TSIGError::NOERROR()) { + state_ = VERIFIED_RESPONSE; + } + if (digest != NULL) { + previous_digest_.assign(static_cast(digest), + static_cast(digest) + + digest_len); + } + error_ = error; + return (error); + } + + // The following three are helper methods to compute the digest for + // TSIG sign/verify in order to unify the common code logic for sign() + // and verify() and to keep these callers concise. + // These methods take an HMAC object, which will be updated with the + // calculated digest. + // Note: All methods construct a local OutputBuffer as a work space with a + // fixed initial buffer size to avoid intermediate buffer extension. + // This should be efficient enough, especially for fundamentally expensive + // operation like cryptographic sign/verify, but if the creation of the + // buffer in each helper method is still identified to be a severe + // performance bottleneck, we could have this class a buffer as a member + // variable and reuse it throughout the object's lifetime. Right now, + // we prefer keeping the scope for local things as small as possible. + void digestPreviousMAC(HMACPtr hmac) const; + void digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, + uint64_t time_signed, uint16_t fudge, + uint16_t error, uint16_t otherlen, + const void* otherdata, + bool time_variables_only) const; + void digestDNSMessage(HMACPtr hmac, uint16_t qid, const void* data, + size_t data_len) const; + State state_; + const TSIGKey key_; + vector previous_digest_; + TSIGError error_; + uint64_t previous_timesigned_; // only meaningful for response with BADTIME +}; + +void +TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) const { + // We should have ensured the digest size fits 16 bits within this class + // implementation. + assert(previous_digest_.size() <= 0xffff); + + OutputBuffer buffer(sizeof(uint16_t) + previous_digest_.size()); + const uint16_t previous_digest_len(previous_digest_.size()); + buffer.writeUint16(previous_digest_len); + if (previous_digest_len != 0) { + buffer.writeData(&previous_digest_[0], previous_digest_len); + } + hmac->update(buffer.getData(), buffer.getLength()); +} + +void +TSIGContext::TSIGContextImpl::digestTSIGVariables( + HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, uint64_t time_signed, + uint16_t fudge, uint16_t error, uint16_t otherlen, const void* otherdata, + bool time_variables_only) const +{ + // It's bit complicated, but we can still predict the necessary size of + // the data to be digested. So we precompute it to avoid possible + // reallocation inside OutputBuffer (not absolutely necessary, but this + // is a bit more efficient) + size_t data_size = 8; + if (!time_variables_only) { + data_size += 10 + key_.getKeyName().getLength() + + key_.getAlgorithmName().getLength(); + } + OutputBuffer buffer(data_size); + + if (!time_variables_only) { + key_.getKeyName().toWire(buffer); + buffer.writeUint16(rrclass); + buffer.writeUint32(rrttl); + key_.getAlgorithmName().toWire(buffer); + } + buffer.writeUint16(time_signed >> 32); + buffer.writeUint32(time_signed & 0xffffffff); + buffer.writeUint16(fudge); + + if (!time_variables_only) { + buffer.writeUint16(error); + buffer.writeUint16(otherlen); + } + + hmac->update(buffer.getData(), buffer.getLength()); + if (!time_variables_only && otherlen > 0) { + hmac->update(otherdata, otherlen); + } +} + +// In digestDNSMessage, we exploit some minimum knowledge of DNS message +// format: +// - the header section has a fixed length of 12 octets (MESSAGE_HEADER_LEN) +// - the offset in the header section to the ID field is 0 +// - the offset in the header section to the ARCOUNT field is 10 (and the field +// length is 2 octets) +// We could construct a separate Message object from the given data, adjust +// fields via the Message interfaces and then render it back to a separate +// buffer, but that would be overkilling. The DNS message header has a +// fixed length and necessary modifications are quite straightforward, so +// we do the job using lower level interfaces. +namespace { +const size_t MESSAGE_HEADER_LEN = 12; +} +void +TSIGContext::TSIGContextImpl::digestDNSMessage(HMACPtr hmac, + uint16_t qid, const void* data, + size_t data_len) const +{ + OutputBuffer buffer(MESSAGE_HEADER_LEN); + const uint8_t* msgptr = static_cast(data); + + // Install the original ID + buffer.writeUint16(qid); + msgptr += sizeof(uint16_t); + + // Copy the rest of the header except the ARCOUNT field. + buffer.writeData(msgptr, 8); + msgptr += 8; + + // Install the adjusted ARCOUNT (we don't care even if the value is bogus + // and it underflows; it would simply result in verification failure) + buffer.writeUint16(InputBuffer(msgptr, sizeof(uint16_t)).readUint16() - 1); + msgptr += 2; + + // Digest the header and the rest of the DNS message + hmac->update(buffer.getData(), buffer.getLength()); + hmac->update(msgptr, data_len - MESSAGE_HEADER_LEN); +} + +TSIGContext::TSIGContext(const TSIGKey& key) : impl_(new TSIGContextImpl(key)) +{ +} + +TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name, + const TSIGKeyRing& keyring) : impl_(NULL) +{ + const TSIGKeyRing::FindResult result(keyring.find(key_name, + algorithm_name)); + if (result.code == TSIGKeyRing::NOTFOUND) { + // If not key is found, create a dummy key with the specified key + // parameters and empty secret. In the common scenario this will + // be used in subsequent response with a TSIG indicating a BADKEY + // error. + impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name, + NULL, 0)); + impl_->error_ = TSIGError::BAD_KEY(); + } else { + impl_ = new TSIGContextImpl(*result.key); + } +} + +TSIGContext::~TSIGContext() { + delete impl_; +} + +TSIGContext::State +TSIGContext::getState() const { + return (impl_->state_); +} + +TSIGError +TSIGContext::getError() const { + return (impl_->error_); +} + +ConstTSIGRecordPtr +TSIGContext::sign(const uint16_t qid, const void* const data, + const size_t data_len) +{ + if (impl_->state_ == VERIFIED_RESPONSE) { + isc_throw(TSIGContextError, + "TSIG sign attempt after verifying a response"); + } + + if (data == NULL || data_len == 0) { + isc_throw(InvalidParameter, "TSIG sign error: empty data is given"); + } + + TSIGError error(TSIGError::NOERROR()); + const uint64_t now = getTSIGTime(); + + // For responses adjust the error code. + if (impl_->state_ == RECEIVED_REQUEST) { + error = impl_->error_; + } + + // For errors related to key or MAC, return an unsigned response as + // specified in Section 4.3 of RFC2845. + if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) { + ConstTSIGRecordPtr tsig(new TSIGRecord( + impl_->key_.getKeyName(), + any::TSIG(impl_->key_.getAlgorithmName(), + now, DEFAULT_FUDGE, 0, NULL, + qid, error.getCode(), 0, NULL))); + impl_->previous_digest_.clear(); + impl_->state_ = SENT_RESPONSE; + return (tsig); + } + + HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC( + impl_->key_.getSecret(), + impl_->key_.getSecretLength(), + impl_->key_.getAlgorithm()), + deleteHMAC); + + // If the context has previous MAC (either the Request MAC or its own + // previous MAC), digest it. + if (impl_->state_ != INIT) { + impl_->digestPreviousMAC(hmac); + } + + // Digest the message (without TSIG) + hmac->update(data, data_len); + + // Digest TSIG variables. + // First, prepare some non constant variables. + const uint64_t time_signed = (error == TSIGError::BAD_TIME()) ? + impl_->previous_timesigned_ : now; + // For BADTIME error, we include 6 bytes of other data. + // (6 bytes = size of time signed value) + const uint16_t otherlen = (error == TSIGError::BAD_TIME()) ? 6 : 0; + OutputBuffer otherdatabuf(otherlen); + if (error == TSIGError::BAD_TIME()) { + otherdatabuf.writeUint16(now >> 32); + otherdatabuf.writeUint32(now & 0xffffffff); + } + const void* const otherdata = + (otherlen == 0) ? NULL : otherdatabuf.getData(); + // Then calculate the digest. If state_ is SENT_RESPONSE we are sending + // a continued message in the same TCP stream so skip digesting + // variables except for time related variables (RFC2845 4.4). + impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(), + TSIGRecord::TSIG_TTL, time_signed, + DEFAULT_FUDGE, error.getCode(), + otherlen, otherdata, + impl_->state_ == SENT_RESPONSE); + + // Get the final digest, update internal state, then finish. + vector digest = hmac->sign(); + assert(digest.size() <= 0xffff); // cryptolink API should have ensured it. + ConstTSIGRecordPtr tsig(new TSIGRecord( + impl_->key_.getKeyName(), + any::TSIG(impl_->key_.getAlgorithmName(), + time_signed, DEFAULT_FUDGE, + digest.size(), &digest[0], + qid, error.getCode(), otherlen, + otherdata))); + // Exception free from now on. + impl_->previous_digest_.swap(digest); + impl_->state_ = (impl_->state_ == INIT) ? SENT_REQUEST : SENT_RESPONSE; + return (tsig); +} + +TSIGError +TSIGContext::verify(const TSIGRecord* const record, const void* const data, + const size_t data_len) +{ + if (impl_->state_ == SENT_RESPONSE) { + isc_throw(TSIGContextError, + "TSIG verify attempt after sending a response"); + } + + // This case happens when we sent a signed request and have received an + // unsigned response. According to RFC2845 Section 4.6 this case should be + // considered a "format error" (although the specific error code + // wouldn't matter much for the caller). + if (record == NULL) { + return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0)); + } + + const any::TSIG& tsig_rdata = record->getRdata(); + + // Reject some obviously invalid data + if (data_len < MESSAGE_HEADER_LEN + record->getLength()) { + isc_throw(InvalidParameter, + "TSIG verify: data length is invalid: " << data_len); + } + if (data == NULL) { + isc_throw(InvalidParameter, "TSIG verify: empty data is invalid"); + } + + // Check key: whether we first verify it with a known key or we verify + // it using the consistent key in the context. If the check fails we are + // done with BADKEY. + if (impl_->state_ == INIT && impl_->error_ == TSIGError::BAD_KEY()) { + return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0)); + } + if (impl_->key_.getKeyName() != record->getName() || + impl_->key_.getAlgorithmName() != tsig_rdata.getAlgorithm()) { + return (impl_->postVerifyUpdate(TSIGError::BAD_KEY(), NULL, 0)); + } + + // Check time: the current time must be in the range of + // [time signed - fudge, time signed + fudge]. Otherwise verification + // fails with BADTIME. (RFC2845 Section 4.6.2) + // Note: for simplicity we don't explicitly catch the case of too small + // current time causing underflow. With the fact that fudge is quite + // small and (for now) non configurable, it shouldn't be a real concern + // in practice. + const uint64_t now = getTSIGTime(); + if (tsig_rdata.getTimeSigned() + DEFAULT_FUDGE < now || + tsig_rdata.getTimeSigned() - DEFAULT_FUDGE > now) { + const void* digest = NULL; + size_t digest_len = 0; + if (impl_->state_ == INIT) { + digest = tsig_rdata.getMAC(); + digest_len = tsig_rdata.getMACSize(); + impl_->previous_timesigned_ = tsig_rdata.getTimeSigned(); + } + return (impl_->postVerifyUpdate(TSIGError::BAD_TIME(), digest, + digest_len)); + } + + // TODO: signature length check based on RFC4635 + // (Right now we enforce the standard signature length in libcryptolink) + + // Handling empty MAC. While RFC2845 doesn't explicitly prohibit other + // cases, it can only reasonably happen in a response with BADSIG or + // BADKEY. We reject other cases as if it were BADSIG to avoid unexpected + // acceptance of a bogus signature. This behavior follows the BIND 9 + // implementation. + if (tsig_rdata.getMACSize() == 0) { + TSIGError error = TSIGError(tsig_rdata.getError()); + if (error != TSIGError::BAD_SIG() && error != TSIGError::BAD_KEY()) { + error = TSIGError::BAD_SIG(); + } + return (impl_->postVerifyUpdate(error, NULL, 0)); + } + + HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC( + impl_->key_.getSecret(), + impl_->key_.getSecretLength(), + impl_->key_.getAlgorithm()), + deleteHMAC); + + // If the context has previous MAC (either the Request MAC or its own + // previous MAC), digest it. + if (impl_->state_ != INIT) { + impl_->digestPreviousMAC(hmac); + } + + // + // Digest DNS message (excluding the trailing TSIG RR and adjusting the + // QID and ARCOUNT header fields) + // + impl_->digestDNSMessage(hmac, tsig_rdata.getOriginalID(), + data, data_len - record->getLength()); + + // Digest TSIG variables. If state_ is VERIFIED_RESPONSE, it's a + // continuation of the same TCP stream and skip digesting them except + // for time related variables (RFC2845 4.4). + // Note: we use the constant values for RR class and TTL specified + // in RFC2845, not received values (we reject other values in constructing + // the TSIGRecord). + impl_->digestTSIGVariables(hmac, TSIGRecord::getClass().getCode(), + TSIGRecord::TSIG_TTL, + tsig_rdata.getTimeSigned(), + tsig_rdata.getFudge(), tsig_rdata.getError(), + tsig_rdata.getOtherLen(), + tsig_rdata.getOtherData(), + impl_->state_ == VERIFIED_RESPONSE); + + // Verify the digest with the received signature. + if (hmac->verify(tsig_rdata.getMAC(), tsig_rdata.getMACSize())) { + return (impl_->postVerifyUpdate(TSIGError::NOERROR(), + tsig_rdata.getMAC(), + tsig_rdata.getMACSize())); + } + + return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0)); +} + +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h new file mode 100644 index 0000000000..bceec25295 --- /dev/null +++ b/src/lib/dns/tsig.h @@ -0,0 +1,394 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __TSIG_H +#define __TSIG_H 1 + +#include + +#include + +#include +#include +#include + +namespace isc { +namespace dns { + +/// An exception that is thrown for logic errors identified in TSIG +/// sign/verify operations. +/// +/// Note that this exception is not thrown for TSIG protocol errors such as +/// verification failures. In general, this exception indicates an internal +/// program bug. +class TSIGContextError : public isc::Exception { +public: + TSIGContextError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// TSIG session context. +/// +/// The \c TSIGContext class maintains a context of a signed session of +/// DNS transactions by TSIG. In many cases a TSIG signed session consists +/// of a single set of request (e.g. normal query) and reply (e.g. normal +/// response), where the request is initially signed by the client, and the +/// reply is signed by the server using the initial signature. As mentioned +/// in RFC2845, a session can consist of multiple exchanges in a TCP +/// connection. As also mentioned in the RFC, an AXFR response often contains +/// multiple DNS messages, which can belong to the same TSIG session. +/// This class supports all these cases. +/// +/// A \c TSIGContext object is generally constructed with a TSIG key to be +/// used for the session, and keeps track of various kinds of session specific +/// information, such as the original digest while waiting for a response or +/// verification error information that is to be used for a subsequent +/// response. +/// +/// This class has two main methods, \c sign() and \c verify(). +/// The \c sign() method signs given data (which is supposed to be a complete +/// DNS message without the TSIG itself) using the TSIG key and other +/// related information associated with the \c TSIGContext object. +/// The \c verify() method verifies a given DNS message that contains a TSIG +/// RR using the key and other internal information. +/// +/// In general, a DNS client that wants to send a signed query will construct +/// a \c TSIGContext object with the TSIG key that the client is intending to +/// use, and sign the query with the context. The client will keeps the +/// context, and verify the response with it. +/// +/// On the other hand, a DNS server will construct a \c TSIGContext object +/// with the information of the TSIG RR included in a query with a set of +/// possible keys (in the form of a \c TSIGKeyRing object). The constructor +/// in this mode will identify the appropriate TSIG key (or internally record +/// an error if it doesn't find a key). The server will then verify the +/// query with the context, and generate a signed response using the same +/// same context. +/// +/// When multiple messages belong to the same TSIG session, either side +/// (signer or verifier) will keep using the same context. It records +/// the latest session state (such as the previous digest) so that repeated +/// calls to \c sign() or \c verify() work correctly in terms of the TSIG +/// protocol. +/// +/// \b Examples +/// +/// This is a typical client application that sends a TSIG signed query +/// and verifies the response. +/// +/// \code +/// // "renderer" is of MessageRenderer to render the message. +/// // (TSIGKey would be configured from config or command line in real app) +/// TSIGContext ctx(TSIGKey("key.example:MSG6Ng==")); +/// Message message(Message::RENDER); +/// message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), +/// RRType::A())); +/// message.toWire(renderer, ctx); +/// +/// // sendto, then recvfrom. received result in (data, data_len) +/// +/// message.clear(Message::PARSE); +/// InputBuffer buffer(data, data_len); +/// message.fromWire(buffer); +/// TSIGError tsig_error = ctx.verify(message.getTSIGRecord(), +/// data, data_len); +/// if (tsig_error == TSIGError::NOERROR()) { +/// // okay. ctx can be continuously used if it's receiving subsequent +/// // signed responses from a TCP stream. +/// } else if (message.getRcode() == Rcode::NOTAUTH()) { +/// // hard error. give up this transaction per RFC2845 4.6. +/// } else { +/// // Other error: discard response keep waiting with the same ctx +/// // for another (again, RFC2845 4.6). +/// } \endcode +/// +/// And this is a typical server application that authenticates a signed +/// query and returns a response according to the result. +/// +/// \code +/// // Assume "message" is of type Message for query handling and +/// // "renderer" is of MessageRenderer to render responses. +/// Message message(Message::RENDER); +/// +/// TSIGKeyRing keyring; // this must be configured with keys somewhere +/// +/// // Receive a query and store it in (data, data_len) +/// InputBuffer buffer(data, data_len); +/// message.clear(Message::PARSE); +/// message.fromWire(buffer); +/// +/// const TSIGRecord* tsig = message.getTSIGRecord(); +/// if (tsig != NULL) { +/// TSIGContext ctx(tsig->getName(), tsig->getRdata().getAlgorithm(), +/// keyring); +/// ctx.verify(tsig, data, data_len); +/// +/// // prepare response +/// message.makeResponse(); +/// //... +/// message.toWire(renderer, ctx); +/// +/// // send the response data back to the client. +/// // If this is a beginning of a signed session over a TCP and +/// // server has more data to send to the client, this ctx +/// // will be used to sign subsequent messages. +/// } \endcode +/// +/// TCP Consideration +/// +/// RFC2845 describes the case where a single TSIG session is used for +/// multiple DNS messages (Section 4.4). This class supports signing and +/// verifying the messages in this scenario, but does not care if the messages +/// were delivered over a TCP connection or not. If, for example, the +/// same \c TSIGContext object is used to sign two independent DNS queries +/// sent over UDP, they will be considered to belong to the same TSIG +/// session, and, as a result, verification will be likely to fail. +/// +/// \b Copyability +/// +/// This class is currently non copyable based on the observation of the +/// typical usage as described above. But there is no strong technical +/// reason why this class cannot be copyable. If we see the need for it +/// in future we may change the implementation on this point. +/// +/// Note to developers: +/// One basic design choice is to make the \c TSIGContext class is as +/// independent from the \c Message class. This is because the latter is +/// much more complicated, depending on many other classes, while TSIG is +/// a very specific part of the entire DNS protocol set. If the \c TSIGContext +/// class depends on \c \c Message, it will be more vulnerable to changes +/// to other classes, and will be more difficult to test due to the +/// direct or indirect dependencies. The interface of \c sign() that takes +/// opaque data (instead of, e.g., a \c Message or \c MessageRenderer object) +/// is therefore a deliberate design decision. +class TSIGContext : boost::noncopyable { +public: + /// Internal state of context + /// + /// The constants of this enum type define a specific state of + /// \c TSIGContext to adjust the behavior. The definition is public + /// and the state can be seen via the \c getState() method, but this is + /// mostly private information. It's publicly visible mainly for testing + /// purposes; there is no API for the application to change the state + /// directly. + enum State { + INIT, ///< Initial state + SENT_REQUEST, ///< Client sent a signed request, waiting response + RECEIVED_REQUEST, ///< Server received a signed request + SENT_RESPONSE, ///< Server sent a signed response + VERIFIED_RESPONSE ///< Client successfully verified a response + }; + + /// \name Constructors and destructor + /// + //@{ + /// Constructor from a TSIG key. + /// + /// \exception std::bad_alloc Resource allocation for internal data fails + /// + /// \param key The TSIG key to be used for TSIG sessions with this context. + explicit TSIGContext(const TSIGKey& key); + + /// Constructor from key parameters and key ring. + TSIGContext(const Name& key_name, const Name& algorithm_name, + const TSIGKeyRing& keyring); + + /// The destructor. + ~TSIGContext(); + //@} + + /// Sign a DNS message. + /// + /// This method computes the TSIG MAC for the given data, which is + /// generally expected to be a complete, wire-format DNS message + /// that doesn't contain a TSIG RR, based on the TSIG key and + /// other context information of \c TSIGContext, and returns a + /// result in the form of a (pointer object pointing to) + /// \c TSIGRecord object. + /// + /// The caller of this method will use the returned value to render a + /// complete TSIG RR into the message that has been signed so that it + /// will become a complete TSIG-signed message. + /// + /// In general, this method is called once by a client to send a + /// signed request or one more times by a server to sign + /// response(s) to a signed request. To avoid allowing accidental + /// misuse, if this method is called after a "client" validates a + /// response, an exception of class \c TSIGContextError will be + /// thrown. + /// + /// \note Normal applications are not expected to call this method + /// directly; they will usually use the \c Message::toWire() method + /// with a \c TSIGContext object being a parameter and have the + /// \c Message class create a complete signed message. + /// + /// This method treats the given data as opaque, even though it's generally + /// expected to represent a wire-format DNS message (see also the class + /// description), and doesn't inspect it in any way. For example, it + /// doesn't check whether the data length is sane for a valid DNS message. + /// This is also the reason why this method takes the \c qid parameter, + /// which will be used as the original ID of the resulting + /// \c TSIGRecordx object, even though this value should be stored in the + /// first two octets (in wire format) of the given data. + /// + /// \note This method still checks and rejects empty data (\c NULL pointer + /// data or the specified data length is 0) in order to avoid catastrophic + /// effect such as program crash. Empty data is not necessarily invalid + /// for HMAC computation, but obviously it doesn't make sense for a DNS + /// message. + /// + /// This method provides the strong exception guarantee; unless the method + /// returns (without an exception being thrown), the internal state of + /// the \c TSIGContext won't be modified. + /// + /// \exception TSIGContextError Context already verified a response. + /// \exception InvalidParameter \c data is NULL or \c data_len is 0 + /// \exception cryptolink::LibraryError Some unexpected error in the + /// underlying crypto operation + /// \exception std::bad_alloc Temporary resource allocation failure + /// + /// \param qid The QID to be as the value of the original ID field of + /// the resulting TSIG record + /// \param data Points to the wire-format data to be signed + /// \param data_len The length of \c data in bytes + /// + /// \return A TSIG record for the given data along with the context. + ConstTSIGRecordPtr sign(const uint16_t qid, const void* const data, + const size_t data_len); + + /// Verify a DNS message. + /// + /// This method verifies given data along with the context and a given + /// TSIG in the form of a \c TSIGRecord object. The data to be verified + /// is generally expected to be a complete, wire-format DNS message, + /// exactly as received by the host, and ending with a TSIG RR. + /// After verification process this method updates its internal state, + /// and returns the result in the form of a \c TSIGError object. + /// Possible return values are (see the \c TSIGError class description + /// for the mnemonics): + /// + /// - \c NOERROR: The data has been verified correctly. + /// - \c FORMERR: \c TSIGRecord is not given (see below). + /// - \c BAD_KEY: Appropriate key is not found or specified key doesn't + /// match for the data. + /// - \c BAD_TIME: The current time doesn't fall in the range specified + /// in the TSIG. + /// - \c BAD_SIG: The signature given in the TSIG doesn't match against + /// the locally computed digest or is the signature is + /// invalid in other way. + /// + /// If this method is called by a DNS client waiting for a signed + /// response and the result is not \c NOERROR, the context can be used + /// to try validating another signed message as described in RFC2845 + /// Section 4.6. + /// + /// If this method is called by a DNS server that tries to authenticate + /// a signed request, and if the result is not \c NOERROR, the + /// corresponding error condition is recorded in the context so that + /// the server can return a response indicating what was wrong by calling + /// \c sign() with the updated context. + /// + /// In general, this method is called once by a server for + /// authenticating a signed request or one more times by a client to + /// validate signed response(s) to a signed request. To avoid allowing + /// accidental misuse, if this method is called after a "server" signs + /// a response, an exception of class \c TSIGContextError will be thrown. + /// + /// The \c record parameter can be NULL; in that case this method simply + /// returns \c FORMERR as the case described in Section 4.6 of RFC2845, + /// i.e., receiving an unsigned response to a signed request. This way + /// a client can transparently pass the result of + /// \c Message::getTSIGRecord() without checking whether it's non NULL + /// and take an appropriate action based on the result of this method. + /// + /// This method handles the given data mostly as opaque. It digests + /// the data assuming it begins with a DNS header and ends with a TSIG + /// RR whose length is given by calling \c TSIGRecord::getLength() on + /// \c record, but otherwise it doesn't parse the data to confirm the + /// assumption. It's caller's responsibility to ensure the data is + /// valid and consistent with \c record. To avoid disruption, this + /// method performs minimal validation on the given \c data and \c record: + /// \c data must not be NULL; \c data_len must not be smaller than the + /// sum of the DNS header length (fixed, 12 octets) and the length of + /// the TSIG RR. If this check fails it throws an \c InvalidParameter + /// exception. + /// + /// One unexpected case that is not covered by this method is that a + /// client receives a signed response to an unsigned request. RFC2845 is + /// silent about such cases; BIND 9 explicitly identifies the case and + /// rejects it. With this implementation, the client can know that the + /// response contains a TSIG via the result of + /// \c Message::getTSIGRecord() and that it is an unexpected TSIG due to + /// the fact that it doesn't have a corresponding \c TSIGContext. + /// It's up to the client implementation whether to react to such a case + /// explicitly (for example, it could either ignore the TSIG and accept + /// the response or drop it). + /// + /// This method provides the strong exception guarantee; unless the method + /// returns (without an exception being thrown), the internal state of + /// the \c TSIGContext won't be modified. + /// + /// \todo Support intermediate TCP DNS messages without TSIG (RFC2845 4.4) + /// \todo Signature truncation support based on RFC4635 + /// + /// \exception TSIGContextError Context already signed a response. + /// \exception InvalidParameter \c data is NULL or \c data_len is too small. + /// + /// \param record The \c TSIGRecord to be verified with \c data + /// \param data Points to the wire-format data (exactly as received) to + /// be verified + /// \param data_len The length of \c data in bytes + /// \return The \c TSIGError that indicates verification result + TSIGError verify(const TSIGRecord* const record, const void* const data, + const size_t data_len); + + /// Return the current state of the context + /// + /// \note + /// The states are visible in public mainly for testing purposes. + /// Normal applications won't have to deal with them. + /// + /// \exception None + State getState() const; + + /// Return the TSIG error as a result of the latest verification + /// + /// This method can be called even before verifying anything, but the + /// returned value is meaningless in that case. + /// + /// \exception None + TSIGError getError() const; + + /// \name Protocol constants and defaults + /// + //@{ + /// The recommended fudge value (in seconds) by RFC2845. + /// + /// Right now fudge is not tunable, and all TSIGs generated by this API + /// will have this value of fudge. + static const uint16_t DEFAULT_FUDGE = 300; + //@} + +private: + struct TSIGContextImpl; + TSIGContextImpl* impl_; +}; +} +} + +#endif // __TSIG_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tsigerror.cc b/src/lib/dns/tsigerror.cc new file mode 100644 index 0000000000..36ef47da10 --- /dev/null +++ b/src/lib/dns/tsigerror.cc @@ -0,0 +1,68 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include + +#include + +#include +#include + +namespace isc { +namespace dns { +namespace { +const char* const tsigerror_text[] = { + "BADSIG", + "BADKEY", + "BADTIME" +}; +} + +TSIGError::TSIGError(Rcode rcode) : code_(rcode.getCode()) { + if (code_ > MAX_RCODE_FOR_TSIGERROR) { + isc_throw(OutOfRange, "Invalid RCODE for TSIG Error: " << rcode); + } +} + +std::string +TSIGError::toText() const { + if (code_ <= MAX_RCODE_FOR_TSIGERROR) { + return (Rcode(code_).toText()); + } else if (code_ <= BAD_TIME_CODE) { + return (tsigerror_text[code_ - (MAX_RCODE_FOR_TSIGERROR + 1)]); + } else { + return (boost::lexical_cast(code_)); + } +} + +Rcode +TSIGError::toRcode() const { + if (code_ <= MAX_RCODE_FOR_TSIGERROR) { + return (Rcode(code_)); + } + if (code_ > BAD_TIME_CODE) { + return (Rcode::SERVFAIL()); + } + return (Rcode::NOTAUTH()); +} + +std::ostream& +operator<<(std::ostream& os, const TSIGError& error) { + return (os << error.toText()); +} +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/tsigerror.h b/src/lib/dns/tsigerror.h new file mode 100644 index 0000000000..8efd3ae7d1 --- /dev/null +++ b/src/lib/dns/tsigerror.h @@ -0,0 +1,338 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __TSIGERROR_H +#define __TSIGERROR_H 1 + +#include +#include + +#include + +namespace isc { +namespace dns { +/// TSIG errors +/// +/// The \c TSIGError class objects represent standard errors related to +/// TSIG protocol operations as defined in related specifications, mainly +/// in RFC2845. +class TSIGError { +public: + /// Constants for pre-defined TSIG error values. + /// + /// Code values from 0 through 15 (inclusive) are derived from those of + /// RCODE and are not defined here. See the \c Rcode class. + /// + /// \note Unfortunately some systems define "BADSIG" as a macro in a public + /// header file. To avoid conflict with it we add an underscore to our + /// definitions. + enum CodeValue { + BAD_SIG_CODE = 16, ///< 16: TSIG verification failure + BAD_KEY_CODE = 17, ///< 17: TSIG key is not recognized + BAD_TIME_CODE = 18 ///< 18: Current time and time signed are too different + }; + + /// \name Constructors + /// + /// We use the default versions of destructor, copy constructor, + /// and assignment operator. + //@{ + /// Constructor from the code value. + /// + /// \exception None + /// + /// \param error_code The underlying 16-bit error code value of the \c TSIGError. + explicit TSIGError(uint16_t error_code) : code_(error_code) {} + + /// Constructor from \c Rcode. + /// + /// As defined in RFC2845, error code values from 0 to 15 (inclusive) are + /// derived from the DNS RCODEs, which are represented via the \c Rcode + /// class in this library. This constructor works as a converter from + /// these RCODEs to corresponding TSIGError objects. + /// + /// \exception isc::OutOfRange Given rcode is not convertible to + /// TSIGErrors. + /// + /// \param rcode the \c Rcode from which the TSIGError should be derived. + explicit TSIGError(Rcode rcode); + //@} + + /// \brief Returns the \c TSIGCode error code value. + /// + /// \exception None + /// + /// \return The underlying code value corresponding to the \c TSIGError. + uint16_t getCode() const { return (code_); } + + /// \brief Return true iff two \c TSIGError objects are equal. + /// + /// Two TSIGError objects are equal iff their error codes are equal. + /// + /// \exception None + /// + /// \param other the \c TSIGError object to compare against. + /// \return true if the two TSIGError are equal; otherwise false. + bool equals(const TSIGError& other) const + { return (code_ == other.code_); } + + /// \brief Same as \c equals(). + bool operator==(const TSIGError& other) const { return (equals(other)); } + + /// \brief Return true iff two \c TSIGError objects are not equal. + /// + /// \exception None + /// + /// \param other the \c TSIGError object to compare against. + /// \return true if the two TSIGError objects are not equal; + /// otherwise false. + bool nequals(const TSIGError& other) const + { return (code_ != other.code_); } + + /// \brief Same as \c nequals(). + bool operator!=(const TSIGError& other) const { return (nequals(other)); } + + /// \brief Convert the \c TSIGError to a string. + /// + /// For codes derived from RCODEs up to 15, this method returns the + /// same string as \c Rcode::toText() for the corresponding code. + /// For other pre-defined code values (see TSIGError::CodeValue), + /// this method returns a string representation of the "mnemonic' used + /// for the enum and constant objects as defined in RFC2845. + /// For example, the string for code value 16 is "BADSIG", etc. + /// For other code values it returns a string representation of the decimal + /// number of the value, e.g. "32", "100", etc. + /// + /// \exception std::bad_alloc Resource allocation for the string fails + /// + /// \return A string representation of the \c TSIGError. + std::string toText() const; + + /// \brief Convert the \c TSIGError to a \c Rcode + /// + /// This method returns an \c Rcode object that is corresponding to + /// the TSIG error. The returned \c Rcode is expected to be used + /// by a verifying server to specify the RCODE of a response when + /// TSIG verification fails. + /// + /// Specifically, this method returns \c Rcode::NOTAUTH() for the + /// TSIG specific errors, BADSIG, BADKEY, BADTIME, as described in + /// RFC2845. For errors derived from the standard Rcode (code 0-15), + /// it returns the corresponding \c Rcode. For others, this method + /// returns \c Rcode::SERVFAIL() as a last resort. + /// + /// \exception None + Rcode toRcode() const; + + /// A constant TSIG error object derived from \c Rcode::NOERROR() + static const TSIGError& NOERROR(); + + /// A constant TSIG error object derived from \c Rcode::FORMERR() + static const TSIGError& FORMERR(); + + /// A constant TSIG error object derived from \c Rcode::SERVFAIL() + static const TSIGError& SERVFAIL(); + + /// A constant TSIG error object derived from \c Rcode::NXDOMAIN() + static const TSIGError& NXDOMAIN(); + + /// A constant TSIG error object derived from \c Rcode::NOTIMP() + static const TSIGError& NOTIMP(); + + /// A constant TSIG error object derived from \c Rcode::REFUSED() + static const TSIGError& REFUSED(); + + /// A constant TSIG error object derived from \c Rcode::YXDOMAIN() + static const TSIGError& YXDOMAIN(); + + /// A constant TSIG error object derived from \c Rcode::YXRRSET() + static const TSIGError& YXRRSET(); + + /// A constant TSIG error object derived from \c Rcode::NXRRSET() + static const TSIGError& NXRRSET(); + + /// A constant TSIG error object derived from \c Rcode::NOTAUTH() + static const TSIGError& NOTAUTH(); + + /// A constant TSIG error object derived from \c Rcode::NOTZONE() + static const TSIGError& NOTZONE(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED11() + static const TSIGError& RESERVED11(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED12() + static const TSIGError& RESERVED12(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED13() + static const TSIGError& RESERVED13(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED14() + static const TSIGError& RESERVED14(); + + /// A constant TSIG error object derived from \c Rcode::RESERVED15() + static const TSIGError& RESERVED15(); + + /// A constant TSIG error object for the BADSIG code + /// (see \c TSIGError::BAD_SIG_CODE). + static const TSIGError& BAD_SIG(); + + /// A constant TSIG error object for the BADKEY code + /// (see \c TSIGError::BAD_KEY_CODE). + static const TSIGError& BAD_KEY(); + + /// A constant TSIG error object for the BADTIME code + /// (see \c TSIGError::BAD_TIME_CODE). + static const TSIGError& BAD_TIME(); + +private: + // This is internally used to specify the maximum possible RCODE value + // that can be convertible to TSIGErrors. + static const int MAX_RCODE_FOR_TSIGERROR = 15; + + uint16_t code_; +}; + +inline const TSIGError& +TSIGError::NOERROR() { + static TSIGError e(Rcode::NOERROR()); + return (e); +} + +inline const TSIGError& +TSIGError::FORMERR() { + static TSIGError e(Rcode::FORMERR()); + return (e); +} + +inline const TSIGError& +TSIGError::SERVFAIL() { + static TSIGError e(Rcode::SERVFAIL()); + return (e); +} + +inline const TSIGError& +TSIGError::NXDOMAIN() { + static TSIGError e(Rcode::NXDOMAIN()); + return (e); +} + +inline const TSIGError& +TSIGError::NOTIMP() { + static TSIGError e(Rcode::NOTIMP()); + return (e); +} + +inline const TSIGError& +TSIGError::REFUSED() { + static TSIGError e(Rcode::REFUSED()); + return (e); +} + +inline const TSIGError& +TSIGError::YXDOMAIN() { + static TSIGError e(Rcode::YXDOMAIN()); + return (e); +} + +inline const TSIGError& +TSIGError::YXRRSET() { + static TSIGError e(Rcode::YXRRSET()); + return (e); +} + +inline const TSIGError& +TSIGError::NXRRSET() { + static TSIGError e(Rcode::NXRRSET()); + return (e); +} + +inline const TSIGError& +TSIGError::NOTAUTH() { + static TSIGError e(Rcode::NOTAUTH()); + return (e); +} + +inline const TSIGError& +TSIGError::NOTZONE() { + static TSIGError e(Rcode::NOTZONE()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED11() { + static TSIGError e(Rcode::RESERVED11()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED12() { + static TSIGError e(Rcode::RESERVED12()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED13() { + static TSIGError e(Rcode::RESERVED13()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED14() { + static TSIGError e(Rcode::RESERVED14()); + return (e); +} + +inline const TSIGError& +TSIGError::RESERVED15() { + static TSIGError e(Rcode::RESERVED15()); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_SIG() { + static TSIGError e(BAD_SIG_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_KEY() { + static TSIGError e(BAD_KEY_CODE); + return (e); +} + +inline const TSIGError& +TSIGError::BAD_TIME() { + static TSIGError e(BAD_TIME_CODE); + return (e); +} + +/// Insert the \c TSIGError as a string into stream. +/// +/// This method convert \c tsig_error into a string and inserts it into the +/// output stream \c os. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param tsig_error An \c TSIGError object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const TSIGError& tsig_error); +} +} + +#endif // __TSIGERROR_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc index 057191da39..4082fbec04 100644 --- a/src/lib/dns/tsigkey.cc +++ b/src/lib/dns/tsigkey.cc @@ -15,50 +15,124 @@ #include #include #include +#include #include +#include + #include +#include #include using namespace std; +using namespace isc::cryptolink; namespace isc { namespace dns { +namespace { + HashAlgorithm + convertAlgorithmName(const isc::dns::Name& name) { + if (name == TSIGKey::HMACMD5_NAME()) { + return (isc::cryptolink::MD5); + } + if (name == TSIGKey::HMACSHA1_NAME()) { + return (isc::cryptolink::SHA1); + } + if (name == TSIGKey::HMACSHA256_NAME()) { + return (isc::cryptolink::SHA256); + } + return (isc::cryptolink::UNKNOWN_HASH); + } +} + struct TSIGKey::TSIGKeyImpl { TSIGKeyImpl(const Name& key_name, const Name& algorithm_name, + isc::cryptolink::HashAlgorithm algorithm, const void* secret, size_t secret_len) : key_name_(key_name), algorithm_name_(algorithm_name), + algorithm_(algorithm), secret_(static_cast(secret), static_cast(secret) + secret_len) { - // Convert the name to the canonical form. + // Convert the key and algorithm names to the canonical form. + key_name_.downcase(); algorithm_name_.downcase(); } - const Name key_name_; + Name key_name_; Name algorithm_name_; + const isc::cryptolink::HashAlgorithm algorithm_; const vector secret_; }; TSIGKey::TSIGKey(const Name& key_name, const Name& algorithm_name, const void* secret, size_t secret_len) : impl_(NULL) { - if (algorithm_name != HMACMD5_NAME() && - algorithm_name != HMACSHA1_NAME() && - algorithm_name != HMACSHA256_NAME()) { - isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " << - algorithm_name); - } + const HashAlgorithm algorithm = convertAlgorithmName(algorithm_name); if ((secret != NULL && secret_len == 0) || (secret == NULL && secret_len != 0)) { isc_throw(InvalidParameter, - "TSIGKey secret and its length are inconsistent"); + "TSIGKey secret and its length are inconsistent: " << + key_name << ":" << algorithm_name); } - - impl_ = new TSIGKeyImpl(key_name, algorithm_name, secret, secret_len); + if (algorithm == isc::cryptolink::UNKNOWN_HASH && secret_len != 0) { + isc_throw(InvalidParameter, + "TSIGKey with unknown algorithm has non empty secret: " << + key_name << ":" << algorithm_name); + } + impl_ = new TSIGKeyImpl(key_name, algorithm_name, algorithm, secret, + secret_len); } +TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) { + try { + istringstream iss(str); + + string keyname_str; + getline(iss, keyname_str, ':'); + if (iss.fail() || iss.bad() || iss.eof()) { + isc_throw(InvalidParameter, "Invalid TSIG key string: " << str); + } + + string secret_str; + getline(iss, secret_str, ':'); + if (iss.fail() || iss.bad()) { + isc_throw(InvalidParameter, "Invalid TSIG key string: " << str); + } + + string algo_str; + if (!iss.eof()) { + getline(iss, algo_str); + } + if (iss.fail() || iss.bad()) { + isc_throw(InvalidParameter, "Invalid TSIG key string: " << str); + } + + const Name algo_name(algo_str.empty() ? "hmac-md5.sig-alg.reg.int" : + algo_str); + const HashAlgorithm algorithm = convertAlgorithmName(algo_name); + + vector secret; + isc::util::encode::decodeBase64(secret_str, secret); + + if (algorithm == isc::cryptolink::UNKNOWN_HASH && !secret.empty()) { + isc_throw(InvalidParameter, + "TSIG key with unknown algorithm has non empty secret: " + << str); + } + + impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, algorithm, + secret.empty() ? NULL : &secret[0], + secret.size()); + } catch (const Exception& e) { + // 'reduce' the several types of exceptions name parsing and + // Base64 decoding can throw to just the InvalidParameter + isc_throw(InvalidParameter, e.what()); + } +} + + TSIGKey::TSIGKey(const TSIGKey& source) : impl_(new TSIGKeyImpl(*source.impl_)) {} @@ -89,6 +163,11 @@ TSIGKey::getAlgorithmName() const { return (impl_->algorithm_name_); } +isc::cryptolink::HashAlgorithm +TSIGKey::getAlgorithm() const { + return (impl_->algorithm_); +} + const void* TSIGKey::getSecret() const { return ((impl_->secret_.size() > 0) ? &impl_->secret_[0] : NULL); @@ -99,6 +178,17 @@ TSIGKey::getSecretLength() const { return (impl_->secret_.size()); } +std::string +TSIGKey::toText() const { + const vector secret_v(static_cast(getSecret()), + static_cast(getSecret()) + + getSecretLength()); + std::string secret_str = isc::util::encode::encodeBase64(secret_v); + + return (getKeyName().toText() + ":" + secret_str + ":" + + getAlgorithmName().toText()); +} + const Name& TSIGKey::HMACMD5_NAME() { static Name alg_name("hmac-md5.sig-alg.reg.int"); @@ -152,10 +242,11 @@ TSIGKeyRing::remove(const Name& key_name) { } TSIGKeyRing::FindResult -TSIGKeyRing::find(const Name& key_name) { +TSIGKeyRing::find(const Name& key_name, const Name& algorithm_name) const { TSIGKeyRingImpl::TSIGKeyMap::const_iterator found = impl_->keys.find(key_name); - if (found == impl_->keys.end()) { + if (found == impl_->keys.end() || + (*found).second.getAlgorithmName() != algorithm_name) { return (FindResult(NOTFOUND, NULL)); } return (FindResult(SUCCESS, &((*found).second))); diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h index e56fa88eb4..f0df709a28 100644 --- a/src/lib/dns/tsigkey.h +++ b/src/lib/dns/tsigkey.h @@ -15,6 +15,8 @@ #ifndef __TSIGKEY_H #define __TSIGKEY_H 1 +#include + namespace isc { namespace dns { @@ -66,12 +68,30 @@ public: //@{ /// \brief Constructor from key parameters /// - /// In the current implementation, \c algorithm_name must be a known - /// algorithm to this implementation, which are defined via the - /// static const member functions. For other names - /// an exception of class \c InvalidParameter will be thrown. - /// Note: This restriction may be too strict, and we may revisit it - /// later. + /// \c algorithm_name should generally be a known algorithm to this + /// implementation, which are defined via the + /// static const member functions. + /// + /// Other names are still accepted as long as the secret is empty + /// (\c secret is \c NULL and \c secret_len is 0), however; in some cases + /// we might want to treat just the pair of key name and algorithm name + /// opaquely, e.g., when generating a response TSIG with a BADKEY error + /// because the algorithm is unknown as specified in Section 3.2 of + /// RFC2845 (in which case the algorithm name would be copied from the + /// request to the response, and for that purpose it would be convenient + /// if a \c TSIGKey object can hold a name for an "unknown" algorithm). + /// + /// \note RFC2845 does not specify which algorithm name should be used + /// in such a BADKEY response. The behavior of using the same algorithm + /// is derived from the BIND 9 implementation. + /// + /// It is unlikely that a TSIG key with an unknown algorithm is of any + /// use with actual crypto operation, so care must be taken when dealing + /// with such keys. (The restriction for the secret will prevent + /// accidental creation of such a dangerous key, e.g., due to misspelling + /// in a configuration file). + /// If the given algorithm name is unknown and non empty secret is + /// specified, an exception of type \c InvalidParameter will be thrown. /// /// \c secret and \c secret_len must be consistent in that the latter /// is 0 if and only if the former is \c NULL; @@ -90,6 +110,28 @@ public: TSIGKey(const Name& key_name, const Name& algorithm_name, const void* secret, size_t secret_len); + /// \brief Constructor from an input string + /// + /// The string must be of the form: + /// :[:] + /// Where is a domain name for the key, is a + /// base64 representation of the key secret, and the optional + /// algorithm is an algorithm identifier as specified in RFC4635. + /// The default algorithm is hmac-md5.sig-alg.reg.int. + /// + /// The same restriction about the algorithm name (and secret) as that + /// for the other constructor applies. + /// + /// Since ':' is used as a separator here, it is not possible to + /// use this constructor to create keys with a ':' character in + /// their name. + /// + /// \exception InvalidParameter exception if the input string is + /// invalid. + /// + /// \param str The string to make a TSIGKey from + explicit TSIGKey(const std::string& str); + /// \brief The copy constructor. /// /// It internally allocates a resource, and if it fails a corresponding @@ -123,6 +165,9 @@ public: /// Return the algorithm name. const Name& getAlgorithmName() const; + /// Return the hash algorithm name in the form of cryptolink::HashAlgorithm + isc::cryptolink::HashAlgorithm getAlgorithm() const; + /// Return the length of the TSIG secret in bytes. size_t getSecretLength() const; @@ -139,6 +184,18 @@ public: const void* getSecret() const; //@} + /// \brief Converts the TSIGKey to a string value + /// + /// The resulting string will be of the form + /// name:secret:algorithm + /// Where is a domain name for the key, is a + /// base64 representation of the key secret, and algorithm is + /// an algorithm identifier as specified in RFC4635 + /// + /// \param key the TSIG key to convert + /// \return The string representation of the given TSIGKey. + std::string toText() const; + /// /// \name Well known algorithm names as defined in RFC2845 and RFC4635. /// @@ -268,7 +325,9 @@ public: /// Find a \c TSIGKey for the given name in the \c TSIGKeyRing. /// /// It searches the internal storage for a \c TSIGKey whose name is - /// \c key_name, and returns the result in the form of a \c FindResult + /// \c key_name and that uses the hash algorithm identified by + /// \c algorithm_name. + /// It returns the result in the form of a \c FindResult /// object as follows: /// - \c code: \c SUCCESS if a key is found; otherwise \c NOTFOUND. /// - \c key: A pointer to the found \c TSIGKey object if one is found; @@ -282,8 +341,9 @@ public: /// This method never throws an exception. /// /// \param key_name The name of the key to be found. + /// \param algorithm_name The name of the algorithm of the found key. /// \return A \c FindResult object enclosing the search result (see above). - FindResult find(const Name& key_name); + FindResult find(const Name& key_name, const Name& algorithm_name) const; private: struct TSIGKeyRingImpl; TSIGKeyRingImpl* impl_; diff --git a/src/lib/dns/tsigrecord.cc b/src/lib/dns/tsigrecord.cc new file mode 100644 index 0000000000..9dd3f78ca9 --- /dev/null +++ b/src/lib/dns/tsigrecord.cc @@ -0,0 +1,147 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +// Internally used constants: + +// Size in octets for the RR type, class TTL, RDLEN fields. +const size_t RR_COMMON_LEN = 10; + +// Size in octets for the fixed part of TSIG RDATAs. +// - Time Signed (6) +// - Fudge (2) +// - MAC Size (2) +// - Original ID (2) +// - Error (2) +// - Other Len (2) +const size_t RDATA_COMMON_LEN = 16; +} + +namespace isc { +namespace dns { +TSIGRecord::TSIGRecord(const Name& key_name, + const rdata::any::TSIG& tsig_rdata) : + key_name_(key_name), rdata_(tsig_rdata), + length_(RR_COMMON_LEN + RDATA_COMMON_LEN + key_name_.getLength() + + rdata_.getAlgorithm().getLength() + + rdata_.getMACSize() + rdata_.getOtherLen()) +{} + +namespace { +// This is a straightforward wrapper of dynamic_cast. +// We use this so that we can throw the DNSMessageFORMERR exception when +// unexpected type of RDATA is detected in the member initialization list +// of the constructor below. +const any::TSIG& +castToTSIGRdata(const rdata::Rdata& rdata) { + try { + return (dynamic_cast(rdata)); + } catch (std::bad_cast&) { + isc_throw(DNSMessageFORMERR, + "TSIG record is being constructed from " + "incompatible RDATA:" << rdata.toText()); + } +} +} + +TSIGRecord::TSIGRecord(const Name& name, const RRClass& rrclass, + const RRTTL& ttl, const rdata::Rdata& rdata, + size_t length) : + key_name_(name), rdata_(castToTSIGRdata(rdata)), length_(length) +{ + if (rrclass != getClass()) { + isc_throw(DNSMessageFORMERR, "Unexpected TSIG RR class: " << rrclass); + } + if (ttl != RRTTL(TSIG_TTL)) { + isc_throw(DNSMessageFORMERR, "Unexpected TSIG TTL: " << ttl); + } +} + +const RRClass& +TSIGRecord::getClass() { + return (RRClass::ANY()); +} + +const RRTTL& +TSIGRecord::getTTL() { + static RRTTL ttl(TSIG_TTL); + return (ttl); +} + +namespace { +template +void +toWireCommon(OUTPUT& output, const rdata::any::TSIG& rdata) { + // RR type, class, TTL are fixed constants. + RRType::TSIG().toWire(output); + TSIGRecord::getClass().toWire(output); + output.writeUint32(TSIGRecord::TSIG_TTL); + + // RDLEN + output.writeUint16(RDATA_COMMON_LEN + rdata.getAlgorithm().getLength() + + rdata.getMACSize() + rdata.getOtherLen()); + + // TSIG RDATA + rdata.toWire(output); +} +} + +int +TSIGRecord::toWire(AbstractMessageRenderer& renderer) const { + // If adding the TSIG would exceed the size limit, don't do it. + if (renderer.getLength() + length_ > renderer.getLengthLimit()) { + renderer.setTruncated(); + return (0); + } + + // key name = owner. note that we disable compression. + renderer.writeName(key_name_, false); + toWireCommon(renderer, rdata_); + return (1); +} + +int +TSIGRecord::toWire(OutputBuffer& buffer) const { + key_name_.toWire(buffer); + toWireCommon(buffer, rdata_); + return (1); +} + +std::string +TSIGRecord::toText() const { + return (key_name_.toText() + " " + RRTTL(TSIG_TTL).toText() + " " + + getClass().toText() + " " + RRType::TSIG().toText() + " " + + rdata_.toText() + "\n"); +} + +std::ostream& +operator<<(std::ostream& os, const TSIGRecord& record) { + return (os << record.toText()); +} +} // namespace dns +} // namespace isc diff --git a/src/lib/dns/tsigrecord.h b/src/lib/dns/tsigrecord.h new file mode 100644 index 0000000000..03de746829 --- /dev/null +++ b/src/lib/dns/tsigrecord.h @@ -0,0 +1,308 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __TSIGRECORD_H +#define __TSIGRECORD_H 1 + +#include +#include + +#include + +#include + +#include +#include + +namespace isc { +namespace util { +class OutputBuffer; +} +namespace dns { +class AbstractMessageRenderer; + +/// TSIG resource record. +/// +/// A \c TSIGRecord class object represents a TSIG resource record and is +/// responsible for conversion to and from wire format TSIG record based on +/// the protocol specification (RFC2845). +/// This class is provided so that other classes and applications can handle +/// TSIG without knowing protocol details of TSIG, such as that it uses a +/// fixed constant of TTL. +/// +/// \todo So the plan is to eventually provide the "from wire" constructor. +/// It's not yet provided in the current phase of development. +/// +/// \note +/// This class could be a derived class of \c AbstractRRset. That way +/// it would be able to be used in a polymorphic way; for example, +/// an application can construct a TSIG RR by itself and insert it to a +/// \c Message object as a generic RRset. On the other hand, it would mean +/// this class would have to implement an \c RdataIterator (even though it +/// can be done via straightforward forwarding) while the iterator is mostly +/// redundant since there should be one and only one RDATA for a valid TSIG +/// RR. Likewise, some methods such as \c setTTL() method wouldn't be well +/// defined due to such special rules for TSIG as using a fixed TTL. +/// Overall, TSIG is a very special RR type that simply uses the compatible +/// resource record format, and it will be unlikely that a user wants to +/// handle it through a generic interface in a polymorphic way. +/// We therefore chose to define it as a separate class. This is also +/// similar to why \c EDNS is a separate class. +class TSIGRecord { +public: + /// + /// \name Constructors + /// + /// We use the default copy constructor, default copy assignment operator, + /// (and default destructor) intentionally. + //@{ + /// Constructor from TSIG key name and RDATA + /// + /// \exception std::bad_alloc Resource allocation for copying the name or + /// RDATA fails + TSIGRecord(const Name& key_name, const rdata::any::TSIG& tsig_rdata); + + /// Constructor from resource record (RR) parameters. + /// + /// This constructor is intended to be used in the context of parsing + /// an incoming DNS message that contains a TSIG. The parser would + /// first extract the owner name, RR type (which is TSIG) class, TTL and + /// the TSIG RDATA from the message. This constructor is expected to + /// be given these RR parameters (except the RR type, because it must be + /// TSIG). + /// + /// According to RFC2845, a TSIG RR uses fixed RR class (ANY) and TTL (0). + /// If the RR class or TTL is different from the expected one, this + /// implementation considers it an invalid record and throws an exception + /// of class \c DNSMessageFORMERR. + /// + /// \note This behavior is not specified in the protocol specification, + /// but this implementation rejects unexpected values for the following + /// reasons (but in any case, this won't matter much in practice as + /// RFC2848 clearly states these fields always have the fixed values and + /// any sane implementation of TSIG signer will follow that): + /// According to the RFC editor (in a private communication), the intended + /// use of the TSIG TTL field is to signal protocol extensions (currently + /// no such extension is defined), so this field may actually be + /// validly non 0 in future. However, until the implementation supports + /// that extension it may not always be able to handle the extended + /// TSIG as intended; the extension may even affect digest computation. + /// There's a related issue on this point. Different implementations + /// interpret the RFC in different ways on the received TTL when + /// digesting the message: BIND 9 uses the received value (even if + /// it's not 0) as part of the TSIG variables; NLnet Labs' LDNS and NSD + /// always use a fixed constant of 0 regardless of the received TTL value. + /// This means if and when an extension with non 0 TTL is introduced + /// there will be interoperability problems in the form of verification + /// failure. By explicitly rejecting it (and subsequently returning + /// a response with a format error) we can indicate the source of the + /// problem more clearly than a "bad signature" TSIG error, which can + /// happen for various reasons. On the other hand, rejecting unexpected + /// RR classes is mostly for consistency; the RFC lists these two fields + /// in the same way, so it makes more sense to handle them equally. + /// (BIND 9 rejects unexpected RR classes for TSIG, but that is part of + /// general check on RR classes on received RRs; it generally requests + /// all classes are the same, and if the protocol specifies the use of + /// a particular class for a particular type of RR, it requests that + /// class be used). + /// + /// Likewise, if \c rdata is not of type \c any::TSIG, an exception of + /// class DNSMessageFORMERR will be thrown. When the caller is a + /// DNS message parser and builds \c rdata from incoming wire format + /// data as described above, this case happens when the RR class is + /// different from ANY (in the implementation, the type check takes place + /// before the explicit check against the RR class explained in the + /// previous paragraph). + /// + /// The \c length parameter is intended to be the length of the TSIG RR + /// (from the beginning of the owner name to the end of the RDATA) when + /// the caller is a DNS message parser. Note that it is the actual length + /// for the RR in the format; if the owner name or the algorithm name + /// (in the RDATA) is compressed (although the latter should not be + /// compressed according to RFC3597), the length must be the size of the + /// compressed data. The length is recorded inside the class and will + /// be returned via subsequent calls to \c getLength(). It's intended to + /// be used in the context TSIG verification; in the verify process + /// the MAC computation must be performed for the original data without + /// TSIG, so, to avoid parsing the entire data in the verify process + /// again, it's necessary to record information that can identify the + /// length to be digested for the MAC. This parameter serves for that + /// purpose. + /// + /// \note Since the constructor doesn't take the wire format data per se, + /// it doesn't (and cannot) check the validity of \c length, and simply + /// accepts any given value. It even accepts obviously invalid values + /// such as 0. It's caller's responsibility to provide a valid value of + /// length, and, the verifier's responsibility to use the length safely. + /// + /// DISCUSSION: this design is fragile in that it introduces + /// a tight coupling between message parsing and TSIG verification via + /// the \c TSIGRecord class. In terms of responsibility decoupling, + /// the ideal way to have \c TSIGRecord remember the entire wire data + /// along with the length of the TSIG. Then in the TSIG verification + /// we could refer to the necessary potion of data solely from a + /// \c TSIGRecord object. However, this approach would require expensive + /// heavy copy of the original data or introduce another kind of coupling + /// between the data holder and this class (if the original data is freed + /// while a \c TSIGRecord object referencing the data still exists, the + /// result will be catastrophic). As a "best current compromise", we + /// use the current design. We may reconsider it if it turns out to + /// cause a big problem or we come up with a better idea. + /// + /// \exception DNSMessageFORMERR Given RR parameters are invalid for TSIG. + /// \exception std::bad_alloc Internal resource allocation fails. + /// + /// \param name The owner name of the TSIG RR + /// \param rrclass The RR class of the RR. Must be \c RRClass::ANY() + /// (see above) + /// \param ttl The TTL of the RR. Must be 0 (see above) + /// \param rdata The RDATA of the RR. Must be of type \c any::TSIG. + /// \param length The size of the RR (see above) + TSIGRecord(const Name& name, const RRClass& rrclass, const RRTTL& ttl, + const rdata::Rdata& rdata, size_t length); + //@} + + /// Return the owner name of the TSIG RR, which is the TSIG key name + /// + /// \exception None + const Name& getName() const { return (key_name_); } + + /// Return the RDATA of the TSIG RR + /// + /// \exception None + const rdata::any::TSIG& getRdata() const { return (rdata_); } + + /// \name Protocol constants and defaults + /// + //@{ + /// Return the RR class of TSIG + /// + /// TSIG always uses the ANY RR class. This static method returns it, + /// when, though unlikely, an application wants to know which class TSIG + /// is supposed to use. + /// + /// \exception None + static const RRClass& getClass(); + + /// Return the TTL value of TSIG + /// + /// TSIG always uses 0 TTL. This static method returns it, + /// when, though unlikely, an application wants to know the TTL TSIG + /// is supposed to use. + /// + /// \exception None + static const RRTTL& getTTL(); + //@} + + /// Return the length of the TSIG record + /// + /// When constructed from the key name and RDATA, it is the length of + /// the record when it is rendered by the \c toWire() method. + /// + /// \note When constructed "from wire", that will mean the length of + /// the wire format data for the TSIG RR. The length will be necessary + /// to verify the message once parse is completed. + /// + /// \exception None + size_t getLength() const { return (length_); } + + /// \brief Render the \c TSIG RR in the wire format. + /// + /// This method renders the TSIG record as a form of a DNS TSIG RR + /// via \c renderer, which encapsulates output buffer and other rendering + /// contexts. + /// + /// Normally this version of \c toWire() method tries to compress the + /// owner name of the RR whenever possible, but this method intentionally + /// skips owner name compression. This is due to a report that some + /// Windows clients refuse a TSIG if its owner name is compressed + /// (See http://marc.info/?l=bind-workers&m=126637138430819&w=2). + /// Reportedly this seemed to be specific to GSS-TSIG, but this + /// implementation skip compression regardless of the algorithm. + /// + /// If by adding the TSIG RR the message size would exceed the limit + /// maintained in \c renderer, this method skips rendering the RR + /// and returns 0 and mark \c renderer as "truncated" (so that a + /// subsequent call to \c isTruncated() on \c renderer will result in + /// \c true); otherwise it returns 1, which is the number of RR + /// rendered. + /// + /// \note If the caller follows the specification of adding TSIG + /// as described in RFC2845, this should not happen; the caller is + /// generally expected to leave a sufficient room in the message for + /// the TSIG. But this method checks the unexpected case nevertheless. + /// + /// \exception std::bad_alloc Internal resource allocation fails (this + /// should be rare). + /// + /// \param renderer DNS message rendering context that encapsulates the + /// output buffer and name compression information. + /// \return 1 if the TSIG RR fits in the message size limit; otherwise 0. + int toWire(AbstractMessageRenderer& renderer) const; + + /// \brief Render the \c TSIG RR in the wire format. + /// + /// This method is same as \c toWire(AbstractMessageRenderer&)const + /// except it renders the RR in an \c OutputBuffer and therefore + /// does not care about message size limit. + /// As a consequence it always returns 1. + int toWire(isc::util::OutputBuffer& buffer) const; + + /// Convert the TSIG record to a string. + /// + /// The output format is the same as the result of \c toText() for + /// other normal types of RRsets (with always using the same RR class + /// and TTL). It also ends with a newline. + /// + /// \exception std::bad_alloc Internal resource allocation fails (this + /// should be rare). + /// + /// \return A string representation of \c TSIG record + std::string toText() const; + + /// The TTL value to be used in TSIG RRs. + static const uint32_t TSIG_TTL = 0; + //@} + +private: + const Name key_name_; + const rdata::any::TSIG rdata_; + const size_t length_; +}; + +/// A pointer-like type pointing to a \c TSIGRecord object. +typedef boost::shared_ptr TSIGRecordPtr; + +/// A pointer-like type pointing to an immutable \c TSIGRecord object. +typedef boost::shared_ptr ConstTSIGRecordPtr; + +/// Insert the \c TSIGRecord as a string into stream. +/// +/// This method convert \c record into a string and inserts it into the +/// output stream \c os. +/// +/// \param os A \c std::ostream object on which the insertion operation is +/// performed. +/// \param record A \c TSIGRecord object output by the operation. +/// \return A reference to the same \c std::ostream object referenced by +/// parameter \c os after the insertion operation. +std::ostream& operator<<(std::ostream& os, const TSIGRecord& record); +} +} + +#endif // __TSIGRECORD_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am index 57705648d3..c27b3e48f6 100644 --- a/src/lib/log/Makefile.am +++ b/src/lib/log/Makefile.am @@ -16,11 +16,13 @@ liblog_la_SOURCES += logger_impl.cc logger_impl.h liblog_la_SOURCES += logger_support.cc logger_support.h liblog_la_SOURCES += messagedef.cc messagedef.h liblog_la_SOURCES += message_dictionary.cc message_dictionary.h -liblog_la_SOURCES += message_exception.h message_exception.cc +liblog_la_SOURCES += message_exception.h liblog_la_SOURCES += message_initializer.cc message_initializer.h liblog_la_SOURCES += message_reader.cc message_reader.h liblog_la_SOURCES += message_types.h liblog_la_SOURCES += root_logger_name.cc root_logger_name.h +liblog_la_SOURCES += log_formatter.h log_formatter.cc +liblog_la_SOURCES += macros.h EXTRA_DIST = README EXTRA_DIST += messagedef.mes diff --git a/src/lib/log/README b/src/lib/log/README index ed11b5b8f7..529eefc209 100644 --- a/src/lib/log/README +++ b/src/lib/log/README @@ -44,7 +44,7 @@ stored definitions; this updated text is logged. However, to aid support, the message identifier so in the example above, the message finally logged would be something like: - OPENIN, unable to open a.txt for input + FAC_OPENIN, unable to open a.txt for input Using The System @@ -55,17 +55,15 @@ The steps in using the system are: mnemonic for the message, typically 6-12 characters long - and a message. The file is described in more detail below. - Ideally the file should have a file type of ".msg". + Ideally the file should have a file type of ".mes". -2. Run it through the message compiler to produce the .h and .cc files. It - is intended that this step be included in the build process. However, - for now run the compiler (found in the "compiler" subdirectory) manually. - The only argument is the name of the message file: it will produce as - output two files, having the same name as the input file but with file - types of ".h" and ".cc". - - The compiler is built in the "compiler" subdirectory of the "src/lib/log" - directory. +2. Run it through the message compiler to produce the .h and .cc files. This + step should be included in the build process. (For an example of how to + do this, see src/lib/nsas/Makefile.am.) During development though, the + message compiler (found in the "src/lib/log/compiler" directory) will need + to be run manually. The only argument is the name of the message file: it + will produce as output two files in the default directory, having the same + name as the input file but with file types of ".h" and ".cc". 3. Include the .h file in your source code to define message symbols, and make sure that the .cc file is compiled and linked into your program - @@ -96,11 +94,12 @@ An example file could be: $PREFIX TEST_ $NAMESPACE isc::log -TEST1 message %s is much too large -+ This message is a test for the general message code -UNKNOWN unknown message -+ Issued when the message is unknown. +% TEST1 message %1 is much too large +This message is a test for the general message code + +% UNKNOWN unknown message +Issued when the message is unknown. -- END -- @@ -117,23 +116,31 @@ Points to note: * Lines starting $ are directives. At present, two directives are recognised: - * $PREFIX, which has one argument: the string used to prefix symbols. If - absent, there is no prefix to the symbols. (Prefixes are explained below.) + * $PREFIX, which has one optional argument: the string used to prefix symbols. + If absent, there is no prefix to the symbols. (Prefixes are explained below.) + * $NAMESPACE, which has one argument: the namespace in which the symbols are - created. In the absence of a $NAMESPACE directive, symbols will be put - in the global namespace. + created. In the absence of a $NAMESPACE directive, symbols will be put in + the anonymous namespace. -* Lines starting + indicate an explanation for the preceding message. These +* Message lines. These start with a "%" and are followed by the message + identification and the message text, the latter including zero or more + replacement tokens, e.g. + + % TEST message %1 is larger than the permitted length of %2 + + * There may be zero or more spaces between the leading "%" and the message + identification (which, in the example above, is the word "TEST"). + + * The replacement tokens are the strings "%1", "%2" etc. When a message + is logged, these are replaced with the arguments passed to the logging + call: %1 refers to the first argument, %2 to the second etc. Within the + message text, the placeholders can appear in any order, and placeholders + can be repeated. + +* Remaining lines indicate an explanation for the preceding message. These are intended to be processed by a separate program and used to generate - an error messages manual. However they are treated like comments by the - message compiler. As with comments, these must be on a line by themselves; - if inline, the text (including the leading "+") will be interpreted as - part of the line. - -* Message lines. These comprise a symbol name and a message, which may - include zero or more printf-style tokens. Symbol names will be upper-cased - by the compiler. - + an error messages manual. They are ignored by the message compiler. Message Compiler ---------------- @@ -153,9 +160,8 @@ the form: : } -The symbols define the keys in the global message dictionary. - -The namespace enclosing the symbols is set by the $NAMESPACE directive. +The symbols define the keys in the global message dictionary, with the +namespace enclosing the symbols set by the $NAMESPACE directive. The "PREFIX_" part of the symbol name is the string defined in the $PREFIX the argument to the directive. So "$PREFIX MSG_" would prefix the identifier @@ -163,12 +169,20 @@ ABC with "MSG_" to give the symbol MSG_ABC. Similarly "$PREFIX E" would prefix it with "E" to give the symbol EABC. If no $PREFIX is given, no prefix appears (so the symbol in this example would be ABC). +The prefix is "syntactic sugar". Generally all symbols in a given message file +will be prefixed with the same set of letters. By extracting these into +a separate prefix, it becomes easier to disambiguate the different symbols. + +There may be multiple $PREFIX directives in a file. A $PREFIX directive applies +to all message definitions between it an the next $PREFIX directive. A $PREFIX +directive with no arguments clears the current prefix. + 2) A C++ source file (called .cc) that holds the definitions of the global symbols and code to insert the symbols and messages into the map. Symbols are defined to be equal to strings holding the identifier, e.g. - extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS"; + extern const isc::log::MessageID MSG_DUPLNS = "MSG_DUPLNS"; (The implementation allows symbols to be compared. However, use of strings should not be assumed - a future implementation may change this.) @@ -243,27 +257,49 @@ To use the current version of the logging: highest-level debug messages and 99 for the lowest-level (and typically more verbose) messages. - c) Name of an external message file. This is the same as a standard message - file, although it should not include any directives. (A single directive - of a particular type will be ignored; multiple directives will cause the - read of the file to fail with an error.) If a message is replaced, the - message should include the same printf-format directives in the same order - as the original message. + c) The external message file. If present, this is the same as a standard + message file, although it should not include any directives. (A single + directive of a particular type will be ignored; multiple directives will + cause the read of the file to fail with an error.) 4. Issue logging calls using methods on logger, e.g. - logger.error(DPS_NSTIMEOUT, "isc.org"); + logger.error(DPS_NSTIMEOUT).arg("isc.org"); (where, in the example above we might have defined the symbol in the message file with something along the lines of: $PREFIX DPS_ : - NSTIMEOUT queries to all nameservers for %s have timed out + NSTIMEOUT queries to all nameservers for %1 have timed out At present, the only logging is to the console. +Efficiency Considerations +------------------------- +A common pattern in logging is a debug call of the form: + + logger.debug(dbglevel, MSGID).arg(expensive_call()).arg(... + +... where "expensive_call()" is a function call to obtain logging information +that may be computationally intensive. Although the cost may be justified +when debugging is enabled, the cost is still incurred even if debugging is +disabled and the debug() method returns without outputting anything. (The +same may be true of other logging levels, although there are likely to be +fewer calls to logger.info(), logger.error() etc. throughout the code and +they are less likely to be disabled.) + +For this reason, a set of macros is provided and are called using the +construct: + + LOG_DEBUG(logger, dbglevel, MSGID).arg(expensive_call()).arg(... + LOG_INFO(logger, MSGID).arg(expensive_call()...) + +If these are used, the arguments passed to the arg() method are not evaluated +if the relevant logging level is disabled. + + Severity Guidelines =================== When using logging, the question arises, what severity should a message be @@ -361,9 +397,6 @@ is only one piece of code that does this functionality. Outstanding Issues ================== * Ability to configure system according to configuration database. -* Update the build procedure to create .cc and .h files from the .msg file - during the build process. (Requires that the message compiler is built - first.) log4cxx Issues diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc index ef4bafd573..457a62ec6e 100644 --- a/src/lib/log/compiler/message.cc +++ b/src/lib/log/compiler/message.cc @@ -50,17 +50,13 @@ static const char* VERSION = "1.0-0"; /// \li A .cc file containing code that adds the messages to the program's /// message dictionary at start-up time. /// -/// Alternatively, the program can produce a .py file that contains the -/// message definitions. -/// - /// \b Invocation
/// The program is invoked with the command: /// /// message [-v | -h | \] /// -/// It reads the message file and writes out two files of the same name but with -/// extensions of .h and .cc. +/// It reads the message file and writes out two files of the same name in the +/// default directory but with extensions of .h and .cc. /// /// \-v causes it to print the version number and exit. \-h prints a help /// message (and exits). @@ -251,17 +247,16 @@ writeClosingNamespace(ostream& output, const vector& ns) { /// /// \param file Name of the message file. The header file is written to a /// file of the same name but with a .h suffix. -/// \param prefix Prefix string to use in symbols /// \param ns Namespace in which the definitions are to be placed. An empty /// string indicates no namespace. /// \param dictionary Dictionary holding the message definitions. void -writeHeaderFile(const string& file, const string& prefix, - const vector& ns_components, MessageDictionary& dictionary) +writeHeaderFile(const string& file, const vector& ns_components, + MessageDictionary& dictionary) { Filename message_file(file); - Filename header_file(message_file.useAsDefault(".h")); + Filename header_file(Filename(message_file.name()).useAsDefault(".h")); // Text to use as the sentinels. string sentinel_text = sentinel(header_file); @@ -271,7 +266,7 @@ writeHeaderFile(const string& file, const string& prefix, try { if (hfile.fail()) { - throw MessageException(MSG_OPNMSGOUT, header_file.fullName(), + throw MessageException(MSG_OPENOUT, header_file.fullName(), strerror(errno)); } @@ -294,7 +289,7 @@ writeHeaderFile(const string& file, const string& prefix, vector idents = sortedIdentifiers(dictionary); for (vector::const_iterator j = idents.begin(); j != idents.end(); ++j) { - hfile << "extern const isc::log::MessageID " << prefix << *j << ";\n"; + hfile << "extern const isc::log::MessageID " << *j << ";\n"; } hfile << "\n"; @@ -305,7 +300,7 @@ writeHeaderFile(const string& file, const string& prefix, // Report errors (if any) and exit if (hfile.fail()) { - throw MessageException(MSG_MSGWRTERR, header_file.fullName(), + throw MessageException(MSG_WRITERR, header_file.fullName(), strerror(errno)); } @@ -354,17 +349,17 @@ replaceNonAlphaNum(char c) { /// to it. But until BIND-10 is ported to Windows, we won't know. void -writeProgramFile(const string& file, const string& prefix, - const vector& ns_components, MessageDictionary& dictionary) +writeProgramFile(const string& file, const vector& ns_components, + MessageDictionary& dictionary) { Filename message_file(file); - Filename program_file(message_file.useAsDefault(".cc")); + Filename program_file(Filename(message_file.name()).useAsDefault(".cc")); // Open the output file for writing ofstream ccfile(program_file.fullName().c_str()); try { if (ccfile.fail()) { - throw MessageException(MSG_OPNMSGOUT, program_file.fullName(), + throw MessageException(MSG_OPENOUT, program_file.fullName(), strerror(errno)); } @@ -387,7 +382,7 @@ writeProgramFile(const string& file, const string& prefix, vector idents = sortedIdentifiers(dictionary); for (vector::const_iterator j = idents.begin(); j != idents.end(); ++j) { - ccfile << "extern const isc::log::MessageID " << prefix << *j << + ccfile << "extern const isc::log::MessageID " << *j << " = \"" << *j << "\";\n"; } ccfile << "\n"; @@ -422,7 +417,7 @@ writeProgramFile(const string& file, const string& prefix, // Report errors (if any) and exit if (ccfile.fail()) { - throw MessageException(MSG_MSGWRTERR, program_file.fullName(), + throw MessageException(MSG_WRITERR, program_file.fullName(), strerror(errno)); } @@ -518,13 +513,10 @@ main(int argc, char* argv[]) { vector ns_components = splitNamespace(reader.getNamespace()); // Write the header file. - writeHeaderFile(message_file, reader.getPrefix(), ns_components, - dictionary); + writeHeaderFile(message_file, ns_components, dictionary); // Write the file that defines the message symbols and text - writeProgramFile(message_file, reader.getPrefix(), ns_components, - dictionary); - + writeProgramFile(message_file, ns_components, dictionary); // Finally, warn of any duplicates encountered. warnDuplicates(reader); @@ -535,9 +527,12 @@ main(int argc, char* argv[]) { string text = e.id(); text += ", "; text += global.getText(e.id()); - // Format with arguments - text = isc::util::str::format(text, e.arguments()); + vector args(e.arguments()); + for (size_t i(0); i < args.size(); ++ i) { + replacePlaceholder(&text, args[i], i + 1); + } + cerr << text << "\n"; return 1; diff --git a/src/lib/log/log_formatter.cc b/src/lib/log/log_formatter.cc new file mode 100644 index 0000000000..18c4741988 --- /dev/null +++ b/src/lib/log/log_formatter.cc @@ -0,0 +1,42 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +using namespace std; +using namespace boost; + +namespace isc { +namespace log { + +void +replacePlaceholder(string* message, const string& arg, + const unsigned placeholder) +{ + string mark("%" + lexical_cast(placeholder)); + size_t pos(message->find(mark)); + if (pos != string::npos) { + do { + message->replace(pos, mark.size(), arg); + pos = message->find(mark, pos + arg.size()); + } while (pos != string::npos); + } else { + // We're missing the placeholder, so add some complain + message->append(" @@Missing placeholder " + mark + " for '" + arg + + "'@@"); + } +} + +} +} diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h new file mode 100644 index 0000000000..cda1d961d5 --- /dev/null +++ b/src/lib/log/log_formatter.h @@ -0,0 +1,150 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __LOG_FORMATTER_H +#define __LOG_FORMMATER_H + +#include +#include + +namespace isc { +namespace log { + +/// \brief The internal replacement routine +/// +/// This is used internally by the Formatter. Replaces a placeholder +/// in the message by replacement. If the placeholder is not found, +/// it adds a complain at the end. +void +replacePlaceholder(std::string* message, const std::string& replacement, + const unsigned placeholder); + +/// +/// \brief The log message formatter +/// +/// This class allows us to format logging messages conveniently. We +/// call something like logger.warn(WARN_MSG).arg(15).arg(dnsMsg). This +/// outputs some text with placeholders replaced by the arguments, if +/// the logging verbosity is at WARN level or more. +/// +/// To make this work, we use the Formatter. The warn (or whatever logging +/// function) returns a Formatter object. That one holds the string to be +/// output with the placeholders. It also remembers if there should be any +/// output at all (eg. if the logging is enabled for this level). When there's +/// no .arg call on the object, it is destroyed right away and we use the +/// destructor to output the text (but only in case we should output anything). +/// +/// If there's an .arg call, we return reference to the same object, so another +/// .arg can be called on it. After the last .arg call is done, the object is +/// destroyed and, again, we can produce the output. +/// +/// Of course, if the logging is turned off, we don't bother with any replacing +/// and just return. +/// +/// User of logging code should not really care much about this class, only +/// call the .arg method to generate the correct output. +/// +/// The class is a template to allow easy testing. Also, we want everything +/// here in the header anyway and it doesn't depend on the details of what +/// Logger really is, so it doesn't hurt anything. +/// +/// Also, if you are interested in the internals, you might find the copy +/// constructor a bit strange. It deactivates the original formatter. We don't +/// really want to support copying of the Formatter by user, but C++ needs a +/// copy constructor when returning from the logging functions, so we need one. +/// And if we did not deactivate the original Formatter, that one would get +/// destroyed before any call to .arg, producing an output, and then the one +/// the .arg calls are called on would get destroyed as well, producing output +/// again. So, think of this behaviour as soul moving from one to another. +template class Formatter { +private: + /// \brief The logger we will use to output the final message. + /// + /// If NULL, we are not active and should not produce anything. + mutable Logger* logger_; + /// \brief Prefix (eg. "ERROR", "DEBUG" or like that) + const char* prefix_; + /// \brief The messages with %1, %2... placeholders + std::string* message_; + /// \brief Which will be the next placeholder to replace + unsigned nextPlaceholder_; + Formatter& operator =(const Formatter& other); +public: + /// \brief Constructor of "active" formatter + /// + /// This will create a formatter. If the arguments are set, it + /// will be active (will produce output). If you leave them all as NULL, + /// it will create an inactive Formatter -- one that'll produce no output. + /// + /// It is not expected to be called by user of logging system directly. + /// + /// \param prefix The severity prefix, like "ERROR" or "DEBUG" + /// \param message The message with placeholders. We take ownership of + /// it and we will modify the string. Must not be NULL unless + /// logger is also NULL, but it's not checked. + /// \param logger The logger where the final output will go, or NULL + /// if no output is wanted. + Formatter(const char* prefix = NULL, std::string* message = NULL, + Logger* logger = NULL) : + logger_(logger), prefix_(prefix), message_(message), + nextPlaceholder_(1) + { + } + + Formatter(const Formatter& other) : + logger_(other.logger_), prefix_(other.prefix_), + message_(other.message_), nextPlaceholder_(other.nextPlaceholder_) + { + other.logger_ = false; + } + /// \brief Destructor. + // + /// This is the place where output happens if the formatter is active. + ~ Formatter() { + if (logger_) { + logger_->output(prefix_, *message_); + delete message_; + } + } + /// \brief Replaces another placeholder + /// + /// Replaces another placeholder and returns a new formatter with it. + /// Deactivates the current formatter. In case the formatter is not active, + /// only produces another inactive formatter. + /// + /// \param arg The argument to place into the placeholder. + template Formatter& arg(const Arg& value) { + if (logger_) { + return (arg(boost::lexical_cast(value))); + } else { + return (*this); + } + } + /// \brief String version of arg. + Formatter& arg(const std::string& arg) { + if (logger_) { + // FIXME: This logic has a problem. If we had a message like + // "%1 %2" and called .arg("%2").arg(42), we would get "42 %2". + // But we consider this to be rare enough not to complicate + // matters. + replacePlaceholder(message_, arg, nextPlaceholder_ ++); + } + return (*this); + } +}; + +} +} + +#endif diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc index 99dc3a1f16..c340315698 100644 --- a/src/lib/log/logger.cc +++ b/src/lib/log/logger.cc @@ -112,52 +112,57 @@ Logger::isFatalEnabled() { // Output methods void -Logger::debug(int dbglevel, const isc::log::MessageID& ident, ...) { +Logger::output(const char* sevText, const string& message) { + getLoggerPtr()->outputRaw(sevText, message); +} + +Logger::Formatter +Logger::debug(int dbglevel, const isc::log::MessageID& ident) { if (isDebugEnabled(dbglevel)) { - va_list ap; - va_start(ap, ident); - getLoggerPtr()->debug(ident, ap); - va_end(ap); + return (Formatter("DEBUG", getLoggerPtr()->lookupMessage(ident), + this)); + } else { + return (Formatter()); } } -void -Logger::info(const isc::log::MessageID& ident, ...) { +Logger::Formatter +Logger::info(const isc::log::MessageID& ident) { if (isInfoEnabled()) { - va_list ap; - va_start(ap, ident); - getLoggerPtr()->info(ident, ap); - va_end(ap); + return (Formatter("INFO ", getLoggerPtr()->lookupMessage(ident), + this)); + } else { + return (Formatter()); } } -void -Logger::warn(const isc::log::MessageID& ident, ...) { +Logger::Formatter +Logger::warn(const isc::log::MessageID& ident) { if (isWarnEnabled()) { - va_list ap; - va_start(ap, ident); - getLoggerPtr()->warn(ident, ap); - va_end(ap); + return (Formatter("WARN ", getLoggerPtr()->lookupMessage(ident), + this)); + } else { + return (Formatter()); } } -void -Logger::error(const isc::log::MessageID& ident, ...) { +Logger::Formatter +Logger::error(const isc::log::MessageID& ident) { if (isErrorEnabled()) { - va_list ap; - va_start(ap, ident); - getLoggerPtr()->error(ident, ap); - va_end(ap); + return (Formatter("ERROR", getLoggerPtr()->lookupMessage(ident), + this)); + } else { + return (Formatter()); } } -void -Logger::fatal(const isc::log::MessageID& ident, ...) { +Logger::Formatter +Logger::fatal(const isc::log::MessageID& ident) { if (isFatalEnabled()) { - va_list ap; - va_start(ap, ident); - getLoggerPtr()->fatal(ident, ap); - va_end(ap); + return (Formatter("FATAL", getLoggerPtr()->lookupMessage(ident), + this)); + } else { + return (Formatter()); } } diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h index 88e88e2673..6bd8924e51 100644 --- a/src/lib/log/logger.h +++ b/src/lib/log/logger.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace isc { namespace log { @@ -83,10 +84,11 @@ public: loggerptr_(NULL), name_(name), infunc_(infunc) {} - /// \brief Destructor virtual ~Logger(); + /// \brief The formatter used to replace placeholders + typedef isc::log::Formatter Formatter; /// \brief Get Name of Logger /// @@ -157,36 +159,31 @@ public: /// \param dbglevel Debug level, ranging between 0 and 99. Higher numbers /// are used for more verbose output. /// \param ident Message identification. - /// \param ... Optional arguments for the message. - void debug(int dbglevel, const MessageID& ident, ...); + Formatter debug(int dbglevel, const MessageID& ident); /// \brief Output Informational Message /// /// \param ident Message identification. - /// \param ... Optional arguments for the message. - void info(const MessageID& ident, ...); + Formatter info(const MessageID& ident); /// \brief Output Warning Message /// /// \param ident Message identification. - /// \param ... Optional arguments for the message. - void warn(const MessageID& ident, ...); + Formatter warn(const MessageID& ident); /// \brief Output Error Message /// /// \param ident Message identification. - /// \param ... Optional arguments for the message. - void error(const MessageID& ident, ...); + Formatter error(const MessageID& ident); /// \brief Output Fatal Message /// /// \param ident Message identification. - /// \param ... Optional arguments for the message. - void fatal(const MessageID& ident, ...); + Formatter fatal(const MessageID& ident); /// \brief Equality /// @@ -205,6 +202,12 @@ protected: static void reset(); private: + friend class isc::log::Formatter; + /// \brief Raw output function + /// + /// This is used by the formatter to output formatted output. + void output(const char* sevText, const std::string& message); + /// \brief Copy Constructor /// /// Disabled (marked private) as it makes no sense to copy the logger - diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc index 41153e9aff..b30f83588e 100644 --- a/src/lib/log/logger_impl.cc +++ b/src/lib/log/logger_impl.cc @@ -13,6 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE #include +#include #include #include @@ -194,17 +195,14 @@ LoggerImpl::isDebugEnabled(int dbglevel) { } // Output a general message +string* +LoggerImpl::lookupMessage(const MessageID& ident) { + return (new string(string(ident) + ", " + + MessageDictionary::globalDictionary().getText(ident))); +} void -LoggerImpl::output(const char* sev_text, const MessageID& ident, - va_list ap) -{ - char message[512]; // Should be large enough for any message - - // Obtain text of the message and substitute arguments. - const string format = MessageDictionary::globalDictionary().getText(ident); - vsnprintf(message, sizeof(message), format.c_str(), ap); - +LoggerImpl::outputRaw(const char* sevText, const string& message) { // Get the time in a struct tm format, and convert to text time_t t_time; time(&t_time); @@ -214,8 +212,8 @@ LoggerImpl::output(const char* sev_text, const MessageID& ident, (void) strftime(chr_time, sizeof(chr_time), "%Y-%m-%d %H:%M:%S", tm_time); // Now output. - std::cout << chr_time << " " << sev_text << " [" << getName() << "] " << - ident << ", " << message << "\n"; + cout << chr_time << " " << sevText << " [" << getName() << "] " << + message << endl; } } // namespace log diff --git a/src/lib/log/logger_impl.h b/src/lib/log/logger_impl.h index 9fc9cf92e5..187e478a9d 100644 --- a/src/lib/log/logger_impl.h +++ b/src/lib/log/logger_impl.h @@ -167,64 +167,16 @@ public: } } - - /// \brief Output General Message + /// \brief Raw output /// - /// The message is formatted to include the date and time, the severity - /// and the logger generating the message. + /// Writes the message with time into the log. Used by the Formatter + /// to produce output. + void outputRaw(const char* sev_text, const std::string& message); + + /// \brief Look up message text in dictionary /// - /// \param sev_text Severity level as a text string - /// \param ident Message identification - /// \param ap Variable argument list holding message arguments - void output(const char* sev_text, const MessageID& ident, - va_list ap); - - - /// \brief Output Debug Message - /// - /// \param ident Message identification. - /// \param text Text to log - /// \param ap Variable argument list holding message arguments - void debug(const MessageID& ident, va_list ap) { - output("DEBUG", ident, ap); - } - - - /// \brief Output Informational Message - /// - /// \param ident Message identification. - /// \param text Text to log - /// \param ap Variable argument list holding message arguments - void info(const MessageID& ident, va_list ap) { - output("INFO ", ident, ap); - } - - /// \brief Output Warning Message - /// - /// \param ident Message identification. - /// \param text Text to log - /// \param ap Variable argument list holding message arguments - void warn(const MessageID& ident, va_list ap) { - output("WARN ", ident, ap); - } - - /// \brief Output Error Message - /// - /// \param ident Message identification. - /// \param text Text to log - /// \param ap Variable argument list holding message arguments - void error(const MessageID& ident, va_list ap) { - output("ERROR", ident, ap); - } - - /// \brief Output Fatal Message - /// - /// \param ident Message identification. - /// \param text Text to log - /// \param ap Variable argument list holding message arguments - void fatal(const MessageID& ident, va_list ap) { - output("FATAL", ident, ap); - } + /// This gets you the unformatted text of message for given ID. + std::string* lookupMessage(const MessageID& id); /// \brief Equality /// diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc index f8bf0756c8..e17c47d4a4 100644 --- a/src/lib/log/logger_support.cc +++ b/src/lib/log/logger_support.cc @@ -24,7 +24,9 @@ /// These functions will be replaced once the code has been written to obtain /// the logging parameters from the configuration database. +#include #include +#include #include #include #include @@ -62,7 +64,7 @@ readLocalMessageFile(const char* file) { MessageDictionary& dictionary = MessageDictionary::globalDictionary(); MessageReader reader(&dictionary); try { - logger.info(MSG_RDLOCMES, file); + logger.info(MSG_RDLOCMES).arg(file); reader.readFile(file, MessageReader::REPLACE); // File successfully read, list the duplicates @@ -70,7 +72,7 @@ readLocalMessageFile(const char* file) { for (MessageReader::MessageIDCollection::const_iterator i = unknown.begin(); i != unknown.end(); ++i) { string message_id = boost::lexical_cast(*i); - logger.warn(MSG_IDNOTFND, message_id.c_str()); + logger.warn(MSG_IDNOTFND).arg(message_id); } } catch (MessageException& e) { @@ -82,11 +84,15 @@ readLocalMessageFile(const char* file) { break; case 1: - logger.error(ident, args[0].c_str()); + logger.error(ident).arg(args[0]); break; - default: // 2 or more (2 should be the maximum) - logger.error(ident, args[0].c_str(), args[1].c_str()); + case 2: + logger.error(ident).arg(args[0]).arg(args[1]); + break; + + default: // 3 or more (3 should be the maximum) + logger.error(ident).arg(args[0]).arg(args[1]).arg(args[2]); } } } @@ -117,7 +123,7 @@ initLogger(const string& root, isc::log::Severity severity, int dbglevel, vector::iterator new_end = unique(duplicates.begin(), duplicates.end()); for (vector::iterator i = duplicates.begin(); i != new_end; ++i) { - logger.warn(MSG_DUPMSGID, i->c_str()); + logger.warn(MSG_DUPMSGID).arg(*i); } } @@ -128,5 +134,76 @@ initLogger(const string& root, isc::log::Severity severity, int dbglevel, } } +/// Logger Run-Time Initialization via Environment Variables +void initLogger() { + + // Root logger name is defined by the environment variable B10_LOGGER_ROOT. + // If not present, the name is "b10root". + const char* DEFAULT_ROOT = "b10root"; + const char* root = getenv("B10_LOGGER_ROOT"); + if (! root) { + root = DEFAULT_ROOT; + } + + // Set the logging severity. The environment variable is + // B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR" + // of "FATAL". Note that the string must be in upper case with no leading + // of trailing blanks. + isc::log::Severity severity = isc::log::DEFAULT; + const char* sev_char = getenv("B10_LOGGER_SEVERITY"); + if (sev_char) { + string sev_string(sev_char); + if (sev_string == "DEBUG") { + severity = isc::log::DEBUG; + } else if (sev_string == "INFO") { + severity = isc::log::INFO; + } else if (sev_string == "WARN") { + severity = isc::log::WARN; + } else if (sev_string == "ERROR") { + severity = isc::log::ERROR; + } else if (sev_string == "FATAL") { + severity = isc::log::FATAL; + } else { + std::cerr << "**ERROR** unrecognised logger severity of '" + << sev_string << "' - default severity will be used\n"; + } + } + + // If the severity is debug, get the debug level (environment variable + // B10_LOGGER_DBGLEVEL), which should be in the range 0 to 99. + int dbglevel = 0; + if (severity == isc::log::DEBUG) { + const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL"); + if (dbg_char) { + int level = 0; + try { + level = boost::lexical_cast(dbg_char); + if (level < MIN_DEBUG_LEVEL) { + std::cerr << "**ERROR** debug level of " << level + << " is invalid - a value of " << MIN_DEBUG_LEVEL + << " will be used\n"; + level = MIN_DEBUG_LEVEL; + } else if (level > MAX_DEBUG_LEVEL) { + std::cerr << "**ERROR** debug level of " << level + << " is invalid - a value of " << MAX_DEBUG_LEVEL + << " will be used\n"; + level = MAX_DEBUG_LEVEL; + } + } catch (...) { + // Error, but not fatal to the test + std::cerr << "**ERROR** Unable to translate " + "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n"; + } + dbglevel = level; + } + } + + /// Set the local message file + const char* localfile = getenv("B10_LOGGER_LOCALMSG"); + + // Initialize logging + initLogger(root, severity, dbglevel, localfile); +} + } // namespace log } // namespace isc diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h index 6b5fdecc99..f4861b258c 100644 --- a/src/lib/log/logger_support.h +++ b/src/lib/log/logger_support.h @@ -39,6 +39,37 @@ namespace log { void initLogger(const std::string& root, isc::log::Severity severity, int dbglevel, const char* file); + +/// \brief Run-Time Initialization from Environment +/// +/// Performs run-time initialization of the logger via the setting of +/// environment variables. These are: +/// +/// B10_LOGGER_ROOT +/// Name of the root logger. If not given, the string "b10root" will be used. +/// +/// B10_LOGGER_SEVERITY +/// Severity of messages that will be logged. This must be one of the strings +/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL". (Must be upper case and must +/// not contain leading or trailing spaces.) If not specified (or if +/// specified but incorrect), the default for the logging system will be used +/// (currently INFO). +/// +/// B10_LOGGER_DBGLEVEL +/// Ignored if the level is not DEBUG, this should be a number between 0 and +/// 99 indicating the logging severity. The default is 0. If outside these +/// limits or if not a number, a value of 0 is used. +/// +/// B10_LOGGER_LOCALMSG +/// If defined, the path specification of a file that contains message +/// definitions replacing ones in the default dictionary. +/// +/// Any errors in the settings cause messages to be output to stderr. +/// +/// This function is most likely to be called from unit test programs. + +void initLogger(); + } // namespace log } // namespace isc diff --git a/src/lib/log/macros.h b/src/lib/log/macros.h new file mode 100644 index 0000000000..31281314ed --- /dev/null +++ b/src/lib/log/macros.h @@ -0,0 +1,50 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __LOG_MACROS_H +#define __LOG_MACROS_H + +#include + +/// \brief Macro to conveniently test debug output and log it +#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE) \ + if (!(LOGGER).isDebugEnabled((LEVEL))) { \ + } else \ + (LOGGER).debug((LEVEL), (MESSAGE)) + +/// \brief Macro to conveniently test info output and log it +#define LOG_INFO(LOGGER, MESSAGE) \ + if (!(LOGGER).isInfoEnabled()) { \ + } else \ + (LOGGER).info((MESSAGE)) + +/// \brief Macro to conveniently test warn output and log it +#define LOG_WARN(LOGGER, MESSAGE) \ + if (!(LOGGER).isWarnEnabled()) { \ + } else \ + (LOGGER).warn((MESSAGE)) + +/// \brief Macro to conveniently test error output and log it +#define LOG_ERROR(LOGGER, MESSAGE) \ + if (!(LOGGER).isErrorEnabled()) { \ + } else \ + (LOGGER).error((MESSAGE)) + +/// \brief Macro to conveniently test fatal output and log it +#define LOG_FATAL(LOGGER, MESSAGE) \ + if (!(LOGGER).isFatalEnabled()) { \ + } else \ + (LOGGER).fatal((MESSAGE)) + +#endif diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h index 30c6618569..eebee89ffd 100644 --- a/src/lib/log/message_exception.h +++ b/src/lib/log/message_exception.h @@ -19,6 +19,7 @@ #include #include +#include #include namespace isc { @@ -35,33 +36,47 @@ public: /// \brief Constructor /// - /// \param id Message identification - MessageException(MessageID id) : id_(id) - {} + /// \param id Message identification. + /// \param lineno Line number on which error occurred (if > 0). + MessageException(MessageID id, int lineno = 0) : id_(id) + { + if (lineno > 0) { + args_.push_back(boost::lexical_cast(lineno)); + } + } /// \brief Constructor /// - /// \param id Message identification - /// \param arg1 First message argument - MessageException(MessageID id, const std::string& arg1) : id_(id) + /// \param id Message identification. + /// \param arg1 First message argument. + /// \param lineno Line number on which error occurred (if > 0). + MessageException(MessageID id, const std::string& arg1, int lineno = 0) + : id_(id) { + if (lineno > 0) { + args_.push_back(boost::lexical_cast(lineno)); + } args_.push_back(arg1); } /// \brief Constructor /// - /// \param id Message identification - /// \param arg1 First message argument - /// \param arg2 Second message argument + /// \param id Message identification. + /// \param arg1 First message argument. + /// \param arg2 Second message argument. + /// \param lineno Line number on which error occurred (if > 0). MessageException(MessageID id, const std::string& arg1, - const std::string& arg2) : id_(id) + const std::string& arg2, int lineno = 0) : id_(id) { + if (lineno > 0) { + args_.push_back(boost::lexical_cast(lineno)); + } args_.push_back(arg1); args_.push_back(arg2); } /// \brief Destructor - virtual ~MessageException() throw(); + ~MessageException() throw() {} /// \brief Return Message ID /// diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc index 7281346745..1a0b2420e8 100644 --- a/src/lib/log/message_reader.cc +++ b/src/lib/log/message_reader.cc @@ -12,8 +12,10 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. +#include #include #include +#include #include #include @@ -25,45 +27,50 @@ using namespace std; -namespace isc { -namespace log { - -// Virtual destructor. -MessageReader::~MessageReader() { +namespace { +const char DIRECTIVE_FLAG = '$'; // Starts each directive +const char MESSAGE_FLAG = '%'; // Starts each message } +namespace isc { +namespace log { + // Read the file. void MessageReader::readFile(const string& file, MessageReader::Mode mode) { - // Ensure the non-added collection is empty: this object might be - // being reused. + // Ensure the non-added collection is empty: we could be re-using this + // object. not_added_.clear(); - // Open the file + // Open the file. ifstream infile(file.c_str()); if (infile.fail()) { - throw MessageException(MSG_OPNMSGIN, file, strerror(errno)); + throw MessageException(MSG_OPENIN, file, strerror(errno)); } - // Loop round reading it. + // Loop round reading it. As we process the file one line at a time, + // keep a track of line number of aid diagnosis of problems. string line; getline(infile, line); + lineno_ = 0; + while (infile.good()) { + ++lineno_; processLine(line, mode); getline(infile, line); } // Why did the loop terminate? if (!infile.eof()) { - throw MessageException(MSG_MSGRDERR, file, strerror(errno)); + throw MessageException(MSG_READERR, file, strerror(errno)); } infile.close(); } -// Parse a line of the file +// Parse a line of the file. void MessageReader::processLine(const string& line, MessageReader::Mode mode) { @@ -74,15 +81,16 @@ MessageReader::processLine(const string& line, MessageReader::Mode mode) { if (text.empty()) { ; // Ignore blank lines - } else if ((text[0] == '#') || (text[0] == '+')) { - ; // Ignore comments or descriptions - - } else if (text[0] == '$') { + } else if (text[0] == DIRECTIVE_FLAG) { parseDirective(text); // Process directives - } else { - parseMessage(text, mode); // Process other lines + } else if (text[0] == MESSAGE_FLAG) { + parseMessage(text, mode); // Process message definition line + + } else { + ; // Other lines are extended message + // description so are ignored } } @@ -99,130 +107,162 @@ MessageReader::parseDirective(const std::string& text) { isc::util::str::uppercase(tokens[0]); if (tokens[0] == string("$PREFIX")) { parsePrefix(tokens); + } else if (tokens[0] == string("$NAMESPACE")) { parseNamespace(tokens); + } else { - throw MessageException(MSG_UNRECDIR, tokens[0]); + + // Unrecognised directive + throw MessageException(MSG_UNRECDIR, tokens[0], lineno_); } } // Process $PREFIX - void MessageReader::parsePrefix(const vector& tokens) { - // Check argument count + // Should not get here unless there is something in the tokens array. + assert(tokens.size() > 0); - static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; - if (tokens.size() < 2) { - throw MessageException(MSG_PRFNOARG); - } else if (tokens.size() > 2) { - throw MessageException(MSG_PRFEXTRARG); + // Process $PREFIX. With no arguments, the prefix is set to the empty + // string. One argument sets the prefix to the to its value and more than + // one argument is invalid. + if (tokens.size() == 1) { + prefix_ = ""; + } else if (tokens.size() == 2) { + prefix_ = tokens[1]; + + // Token is potentially valid providing it only contains alphabetic + // and numeric characters (and underscores) and does not start with a + // digit. + if (invalidSymbol(prefix_)) { + throw MessageException(MSG_PRFINVARG, prefix_, lineno_); + } + + } else { + + // Too many arguments + throw MessageException(MSG_PRFEXTRARG, lineno_); } +} - // As a style, we are going to have the symbols in uppercase - string prefix = tokens[1]; - isc::util::str::uppercase(prefix); - - // Token is potentially valid providing it only contains alphabetic - // and numeric characters (and underscores) and does not start with a - // digit. - if ((prefix.find_first_not_of(valid) != string::npos) || - (std::isdigit(prefix[0]))) { - - // Invalid character in string or it starts with a digit. - throw MessageException(MSG_PRFINVARG, tokens[1]); - } - - // All OK - unless the prefix has already been set. - - if (prefix_.size() != 0) { - throw MessageException(MSG_DUPLPRFX); - } - - // Prefix has not been set, so set it and return success. - - prefix_ = prefix; +// Check if string is an invalid C++ symbol. It is valid if comprises only +// alphanumeric characters and underscores, and does not start with a digit. +// (Owing to the logic of the rest of the code, we check for its invalidity, +// not its validity.) +bool +MessageReader::invalidSymbol(const string& symbol) { + static const string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789_"; + return ( symbol.empty() || + (symbol.find_first_not_of(valid_chars) != string::npos) || + (std::isdigit(symbol[0]))); } // Process $NAMESPACE. A lot of the processing is similar to that of $PREFIX, // except that only limited checks will be done on the namespace (to avoid a -// lot of parsing and separating out of the namespace components.) +// lot of parsing and separating out of the namespace components.) Also, unlike +// $PREFIX, there can only be one $NAMESPACE in a file. void MessageReader::parseNamespace(const vector& tokens) { // Check argument count - - static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:" - "abcdefghijklmnopqrstuvwxyz"; - if (tokens.size() < 2) { - throw MessageException(MSG_NSNOARG); + throw MessageException(MSG_NSNOARG, lineno_); } else if (tokens.size() > 2) { - throw MessageException(MSG_NSEXTRARG); + throw MessageException(MSG_NSEXTRARG, lineno_); } // Token is potentially valid providing it only contains alphabetic - // and numeric characters (and underscores and colons). - if (tokens[1].find_first_not_of(valid) != string::npos) { - - // Invalid character in string or it starts with a digit. - throw MessageException(MSG_NSINVARG, tokens[1]); + // and numeric characters (and underscores and colons). As noted above, + // we won't be exhaustive - after all, and code containing the resultant + // namespace will have to be compiled, and the compiler will catch errors. + static const string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789_:"; + if (tokens[1].find_first_not_of(valid_chars) != string::npos) { + throw MessageException(MSG_NSINVARG, tokens[1], lineno_); } // All OK - unless the namespace has already been set. if (ns_.size() != 0) { - throw MessageException(MSG_DUPLNS); + throw MessageException(MSG_DUPLNS, lineno_); } // Prefix has not been set, so set it and return success. - ns_ = tokens[1]; } // Process message. By the time this method is called, the line has been -// stripped of leading and trailing spaces, and we believe that it is a line -// defining a message. The first token on the line is converted to uppercase -// and becomes the message ID; the rest of the line is the message text. +// stripped of leading and trailing spaces. The first character of the string +// is the message introducer, so we can get rid of that. The remainder is +// a line defining a message. +// +// The first token on the line, when concatenated to the prefix and converted to +// upper-case, is the message ID. The first of the line from the next token +// on is the message text. void MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) { static string delimiters("\t\n "); // Delimiters + // The line passed should be at least one character long and start with the + // message introducer (else we should not have got here). + assert((text.size() >= 1) && (text[0] == MESSAGE_FLAG)); + + // A line comprising just the message introducer is not valid. + if (text.size() == 1) { + throw MessageException(MSG_NOMSGID, text, lineno_); + } + + // Strip off the introducer and any leading space after that. + string message_line = isc::util::str::trim(text.substr(1)); + // Look for the first delimiter. - size_t first_delim = text.find_first_of(delimiters); + size_t first_delim = message_line.find_first_of(delimiters); if (first_delim == string::npos) { // Just a single token in the line - this is not valid - throw MessageException(MSG_NOMSGTXT, text); + throw MessageException(MSG_NOMSGTXT, message_line, lineno_); } - // Extract the first token into the message ID - string ident = text.substr(0, first_delim); + // Extract the first token into the message ID, preceding it with the + // current prefix, then convert to upper-case. If the prefix is not set, + // perform the valid character check now - the string will become a C++ + // symbol so we may as well identify problems early. + string ident = prefix_ + message_line.substr(0, first_delim); + if (prefix_.empty()) { + if (invalidSymbol(ident)) { + throw MessageException(MSG_INVMSGID, ident, lineno_); + } + } + isc::util::str::uppercase(ident); // Locate the start of the message text - size_t first_text = text.find_first_not_of(delimiters, first_delim); + size_t first_text = message_line.find_first_not_of(delimiters, first_delim); if (first_text == string::npos) { // ?? This happens if there are trailing delimiters, which should not // occur as we have stripped trailing spaces off the line. Just treat // this as a single-token error for simplicity's sake. - throw MessageException(MSG_NOMSGTXT, text); + throw MessageException(MSG_NOMSGTXT, message_line, lineno_); } // Add the result to the dictionary and to the non-added list if the add to // the dictionary fails. bool added; if (mode == ADD) { - added = dictionary_->add(ident, text.substr(first_text)); + added = dictionary_->add(ident, message_line.substr(first_text)); } else { - added = dictionary_->replace(ident, text.substr(first_text)); + added = dictionary_->replace(ident, message_line.substr(first_text)); } if (!added) { not_added_.push_back(ident); diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h index d07c7f26fe..eded9c6f3a 100644 --- a/src/lib/log/message_reader.h +++ b/src/lib/log/message_reader.h @@ -64,10 +64,9 @@ public: dictionary_(dictionary) {} - /// \brief Virtual Destructor - virtual ~MessageReader(); - + virtual ~MessageReader() + {} /// \brief Get Dictionary /// @@ -188,10 +187,24 @@ private: /// \param tokens $NAMESPACE line split into tokens void parseNamespace(const std::vector& tokens); + /// \brief Check for invalid C++ symbol name + /// + /// The message ID (or concatenation of prefix and message ID) will be used + /// as the name of a symbol in C++ code. This function checks if the name + /// is invalid (contains anything other than alphanumeric characters or + /// underscores, or starts with a digit). + /// + /// \param symbol name to check to see if it is an invalid C++ symbol. + /// + /// \return true if the name is invalid, false if it is valid. + bool invalidSymbol(const std::string& symbol); + + /// Attributes MessageDictionary* dictionary_; ///< Dictionary to add messages to MessageIDCollection not_added_; ///< List of IDs not added + int lineno_; ///< Number of last line read std::string prefix_; ///< Argument of $PREFIX statement std::string ns_; ///< Argument of $NAMESPACE statement }; diff --git a/src/lib/log/messagedef.cc b/src/lib/log/messagedef.cc index f680a743c1..5cc89b3019 100644 --- a/src/lib/log/messagedef.cc +++ b/src/lib/log/messagedef.cc @@ -1,4 +1,4 @@ -// File created from messagedef.mes on Mon Feb 14 11:07:45 2011 +// File created from messagedef.mes on Mon May 9 13:52:54 2011 #include #include @@ -7,23 +7,23 @@ namespace isc { namespace log { -extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS"; -extern const isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX"; -extern const isc::log::MessageID MSG_DUPMSGID = "DUPMSGID"; -extern const isc::log::MessageID MSG_IDNOTFND = "IDNOTFND"; -extern const isc::log::MessageID MSG_MSGRDERR = "MSGRDERR"; -extern const isc::log::MessageID MSG_MSGWRTERR = "MSGWRTERR"; -extern const isc::log::MessageID MSG_NOMSGTXT = "NOMSGTXT"; -extern const isc::log::MessageID MSG_NSEXTRARG = "NSEXTRARG"; -extern const isc::log::MessageID MSG_NSINVARG = "NSINVARG"; -extern const isc::log::MessageID MSG_NSNOARG = "NSNOARG"; -extern const isc::log::MessageID MSG_OPNMSGIN = "OPNMSGIN"; -extern const isc::log::MessageID MSG_OPNMSGOUT = "OPNMSGOUT"; -extern const isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG"; -extern const isc::log::MessageID MSG_PRFINVARG = "PRFINVARG"; -extern const isc::log::MessageID MSG_PRFNOARG = "PRFNOARG"; -extern const isc::log::MessageID MSG_RDLOCMES = "RDLOCMES"; -extern const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR"; +extern const isc::log::MessageID MSG_DUPLNS = "MSG_DUPLNS"; +extern const isc::log::MessageID MSG_DUPMSGID = "MSG_DUPMSGID"; +extern const isc::log::MessageID MSG_IDNOTFND = "MSG_IDNOTFND"; +extern const isc::log::MessageID MSG_INVMSGID = "MSG_INVMSGID"; +extern const isc::log::MessageID MSG_NOMSGID = "MSG_NOMSGID"; +extern const isc::log::MessageID MSG_NOMSGTXT = "MSG_NOMSGTXT"; +extern const isc::log::MessageID MSG_NSEXTRARG = "MSG_NSEXTRARG"; +extern const isc::log::MessageID MSG_NSINVARG = "MSG_NSINVARG"; +extern const isc::log::MessageID MSG_NSNOARG = "MSG_NSNOARG"; +extern const isc::log::MessageID MSG_OPENIN = "MSG_OPENIN"; +extern const isc::log::MessageID MSG_OPENOUT = "MSG_OPENOUT"; +extern const isc::log::MessageID MSG_PRFEXTRARG = "MSG_PRFEXTRARG"; +extern const isc::log::MessageID MSG_PRFINVARG = "MSG_PRFINVARG"; +extern const isc::log::MessageID MSG_RDLOCMES = "MSG_RDLOCMES"; +extern const isc::log::MessageID MSG_READERR = "MSG_READERR"; +extern const isc::log::MessageID MSG_UNRECDIR = "MSG_UNRECDIR"; +extern const isc::log::MessageID MSG_WRITERR = "MSG_WRITERR"; } // namespace log } // namespace isc @@ -31,23 +31,23 @@ extern const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR"; namespace { const char* values[] = { - "DUPLNS", "duplicate $NAMESPACE directive found", - "DUPLPRFX", "duplicate $PREFIX directive found", - "DUPMSGID", "duplicate message ID (%s) in compiled code", - "IDNOTFND", "could not replace message for '%s': no such message identification", - "MSGRDERR", "error reading from message file %s: %s", - "MSGWRTERR", "error writing to %s: %s", - "NOMSGTXT", "a line containing a message ID ('%s') and nothing else was found", - "NSEXTRARG", "$NAMESPACE directive has too many arguments", - "NSINVARG", "$NAMESPACE directive has an invalid argument ('%s')", - "NSNOARG", "no arguments were given to the $NAMESPACE directive", - "OPNMSGIN", "unable to open message file %s for input: %s", - "OPNMSGOUT", "unable to open %s for output: %s", - "PRFEXTRARG", "$PREFIX directive has too many arguments", - "PRFINVARG", "$PREFIX directive has an invalid argument ('%s')", - "PRFNOARG", "no arguments were given to the $PREFIX directive", - "RDLOCMES", "reading local message file %s", - "UNRECDIR", "unrecognised directive '%s'", + "MSG_DUPLNS", "line %1: duplicate $NAMESPACE directive found", + "MSG_DUPMSGID", "duplicate message ID (%1) in compiled code", + "MSG_IDNOTFND", "could not replace message text for '%1': no such message", + "MSG_INVMSGID", "line %1: invalid message identification '%2'", + "MSG_NOMSGID", "line %1: message definition line found without a message ID", + "MSG_NOMSGTXT", "line %1: line found containing a message ID ('%2') and no text", + "MSG_NSEXTRARG", "line %1: $NAMESPACE directive has too many arguments", + "MSG_NSINVARG", "line %1: $NAMESPACE directive has an invalid argument ('%2')", + "MSG_NSNOARG", "line %1: no arguments were given to the $NAMESPACE directive", + "MSG_OPENIN", "unable to open message file %1 for input: %2", + "MSG_OPENOUT", "unable to open %1 for output: %2", + "MSG_PRFEXTRARG", "line %1: $PREFIX directive has too many arguments", + "MSG_PRFINVARG", "line %1: $PREFIX directive has an invalid argument ('%2')", + "MSG_RDLOCMES", "reading local message file %1", + "MSG_READERR", "error reading from message file %1: %2", + "MSG_UNRECDIR", "line %1: unrecognised directive '%2'", + "MSG_WRITERR", "error writing to %1: %2", NULL }; diff --git a/src/lib/log/messagedef.h b/src/lib/log/messagedef.h index eb8f4eabc2..79c8bab9e5 100644 --- a/src/lib/log/messagedef.h +++ b/src/lib/log/messagedef.h @@ -1,4 +1,4 @@ -// File created from messagedef.mes on Mon Feb 14 11:07:45 2011 +// File created from messagedef.mes on Mon May 9 13:52:54 2011 #ifndef __MESSAGEDEF_H #define __MESSAGEDEF_H @@ -9,22 +9,22 @@ namespace isc { namespace log { extern const isc::log::MessageID MSG_DUPLNS; -extern const isc::log::MessageID MSG_DUPLPRFX; extern const isc::log::MessageID MSG_DUPMSGID; extern const isc::log::MessageID MSG_IDNOTFND; -extern const isc::log::MessageID MSG_MSGRDERR; -extern const isc::log::MessageID MSG_MSGWRTERR; +extern const isc::log::MessageID MSG_INVMSGID; +extern const isc::log::MessageID MSG_NOMSGID; extern const isc::log::MessageID MSG_NOMSGTXT; extern const isc::log::MessageID MSG_NSEXTRARG; extern const isc::log::MessageID MSG_NSINVARG; extern const isc::log::MessageID MSG_NSNOARG; -extern const isc::log::MessageID MSG_OPNMSGIN; -extern const isc::log::MessageID MSG_OPNMSGOUT; +extern const isc::log::MessageID MSG_OPENIN; +extern const isc::log::MessageID MSG_OPENOUT; extern const isc::log::MessageID MSG_PRFEXTRARG; extern const isc::log::MessageID MSG_PRFINVARG; -extern const isc::log::MessageID MSG_PRFNOARG; extern const isc::log::MessageID MSG_RDLOCMES; +extern const isc::log::MessageID MSG_READERR; extern const isc::log::MessageID MSG_UNRECDIR; +extern const isc::log::MessageID MSG_WRITERR; } // namespace log } // namespace isc diff --git a/src/lib/log/messagedef.mes b/src/lib/log/messagedef.mes index 35993888ab..51c04fa6fb 100644 --- a/src/lib/log/messagedef.mes +++ b/src/lib/log/messagedef.mes @@ -12,108 +12,108 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. +# \brief Message Utility Message File +# +# This is the source of the set of messages generated by the message and +# logging components. The associated .h and .cc files are created by hand from +# this file though and are not built during the build process; this is to avoid +# the chicken-and-egg situation where we need the files to build the message +# compiler, yet we need the compiler to build the files. + $PREFIX MSG_ $NAMESPACE isc::log -# \brief Message Utility Message File -# -# This is the source of the set of messages generated by the message and logging -# components. The associated .h and .cc files are created by hand from this -# file though and are not built during the build process; this is to avoid the -# chicken-and-egg situation where we need the files to build the message -# compiler, yet we need the compiler to build the files. +% DUPMSGID duplicate message ID (%1) in compiled code +Indicative of a programming error, when it started up, BIND10 detected that +the given message ID had been registered by one or more modules. (All message +IDs should be unique throughout BIND10.) This has no impact on the operation +of the server other that erroneous messages may be logged. (When BIND10 loads +the message IDs (and their associated text), if a duplicate ID is found it is +discarded. However, when the module that supplied the duplicate ID logs that +particular message, the text supplied by the module that added the original +ID will be output - something that may bear no relation to the condition being +logged. -DUPMSGID duplicate message ID (%s) in compiled code -+ Indicative of a programming error, when it started up, BIND10 detected that -+ the given message ID had been registered by one or more modules. (All message -+ IDs should be unique throughout BIND10.) This has no impact on the operation -+ of the server other that erroneous messages may be logged. (When BIND10 loads -+ the message IDs (and their associated text), if a duplicate ID is found it is -+ discarded. However, when the module that supplied the duplicate ID logs that -+ particular message, the text supplied by the module that added the original -+ ID will be output - something that may bear no relation to the condition being -+ logged. +% DUPLNS line %1: duplicate $NAMESPACE directive found +When reading a message file, more than one $NAMESPACE directive was found. In +this version of the code, such a condition is regarded as an error and the +read will be abandoned. -DUPLNS duplicate $NAMESPACE directive found -+ When reading a message file, more than one $NAMESPACE directive was found. In -+ this version of the code, such a condition is regarded as an error and the -+ read will be abandoned. +% IDNOTFND could not replace message text for '%1': no such message +During start-up a local message file was read. A line with the listed +message identification was found in the file, but the identification is not +one contained in the compiled-in message dictionary. Either the message +identification has been mis-spelled in the file, or the local file was used +for an earlier version of the software and the message with that +identification has been removed. -DUPLPRFX duplicate $PREFIX directive found -+ When reading a message file, more than one $PREFIX directive was found. In -+ this version of the code, such a condition is regarded as an error and the -+ read will be abandoned. +This message may appear a number of times in the file, once for every such +unknown message identification. -IDNOTFND could not replace message for '%s': no such message identification -+ During start-up a local message file was read. A line with the listed -+ message identification was found in the file, but the identification is not -+ one contained in the compiled-in message dictionary. Either the message -+ identification has been mis-spelled in the file, or the local file was used -+ for an earlier version of the software and the message with that -+ identification has been removed. -+ -+ This message may appear a number of times in the file, once for every such -+ unknown message identification. +% INVMSGID line %1: invalid message identification '%2' +The concatenation of the prefix and the message identification is used as +a symbol in the C++ module; as such it may only contain -MSGRDERR error reading from message file %s: %s -+ The specified error was encountered reading from the named message file. +% NOMSGID line %1: message definition line found without a message ID +Message definition lines are lines starting with a "%". The rest of the line +should comprise the message ID and text describing the message. This error +indicates the message compiler found a line in the message file comprising +just the "%" and nothing else. -MSGWRTERR error writing to %s: %s -+ The specified error was encountered by the message compiler when writing to -+ the named output file. +% NOMSGTXT line %1: line found containing a message ID ('%2') and no text +Message definition lines are lines starting with a "%". The rest of the line +should comprise the message ID and text describing the message. This error +is generated when a line is found in the message file that contains the +leading "%" and the message identification but no text. -NSEXTRARG $NAMESPACE directive has too many arguments -+ The $NAMESPACE directive takes a single argument, a namespace in which all the -+ generated symbol names are placed. This error is generated when the -+ compiler finds a $NAMESPACE directive with more than one argument. +% NSEXTRARG line %1: $NAMESPACE directive has too many arguments +The $NAMESPACE directive takes a single argument, a namespace in which all the +generated symbol names are placed. This error is generated when the +compiler finds a $NAMESPACE directive with more than one argument. -NSINVARG $NAMESPACE directive has an invalid argument ('%s') -+ The $NAMESPACE argument should be a valid C++ namespace. The reader does a -+ cursory check on its validity, checking that the characters in the namespace -+ are correct. The error is generated when the reader finds an invalid -+ character. (Valid are alphanumeric characters, underscores and colons.) +% NSINVARG line %1: $NAMESPACE directive has an invalid argument ('%2') +The $NAMESPACE argument should be a valid C++ namespace. The reader does a +cursory check on its validity, checking that the characters in the namespace +are correct. The error is generated when the reader finds an invalid +character. (Valid are alphanumeric characters, underscores and colons.) -NOMSGTXT a line containing a message ID ('%s') and nothing else was found -+ Message definitions comprise lines starting with a message identification (a -+ symbolic name for the message) and followed by the text of the message. This -+ error is generated when a line is found in the message file that contains just -+ the message identification and no text. +% NSNOARG line %1: no arguments were given to the $NAMESPACE directive +The $NAMESPACE directive takes a single argument, a namespace in which all the +generated symbol names are placed. This error is generated when the +compiler finds a $NAMESPACE directive with no arguments. -NSNOARG no arguments were given to the $NAMESPACE directive -+ The $NAMESPACE directive takes a single argument, a namespace in which all the -+ generated symbol names are placed. This error is generated when the -+ compiler finds a $NAMESPACE directive with no arguments. +% OPENIN unable to open message file %1 for input: %2 +The program was not able to open the specified input message file for the +reason given. -OPNMSGIN unable to open message file %s for input: %s -+ The program was not able to open the specified input message file for the -+ reason given. +% OPENOUT unable to open %1 for output: %2 +The program was not able to open the specified output file for the reason +given. -OPNMSGOUT unable to open %s for output: %s -+ The program was not able to open the specified output file for the reason -+ given. +% PRFEXTRARG line %1: $PREFIX directive has too many arguments +The $PREFIX directive takes a single argument, a prefix to be added to the +symbol names when a C++ .h file is created. This error is generated when the +compiler finds a $PREFIX directive with more than one argument. -PRFEXTRARG $PREFIX directive has too many arguments -+ The $PREFIX directive takes a single argument, a prefix to be added to the -+ symbol names when a C++ .h file is created. This error is generated when the -+ compiler finds a $PREFIX directive with more than one argument. +% PRFINVARG line %1: $PREFIX directive has an invalid argument ('%2') +The $PREFIX argument is used in a symbol name in a C++ header file. As such, +it must adhere to restrictions on C++ symbol names (e.g. may only contain +alphanumeric characters or underscores, and may nor start with a digit). +A $PREFIX directive was found with an argument (given in the message) that +violates those restictions. -PRFINVARG $PREFIX directive has an invalid argument ('%s') -+ The $PREFIX argument is used in a symbol name in a C++ header file. As such, -+ it must adhere to restrictions on C++ symbol names (e.g. may only contain -+ alphanumeric characters or underscores, and may nor start with a digit). A -+ $PREFIX directive was found with an argument (given in the message) that -+ violates those restictions. +% RDLOCMES reading local message file %1 +This is an informational message output by BIND10 when it starts to read a +local message file. (A local message file may replace the text of one of more +messages; the ID of the message will not be changed though.) -PRFNOARG no arguments were given to the $PREFIX directive -+ The $PREFIX directive takes a single argument, a prefix to be added to the -+ symbol names when a C++ .h file is created. This error is generated when the -+ compiler finds a $PREFIX directive with no arguments. +% READERR error reading from message file %1: %2 +The specified error was encountered reading from the named message file. -RDLOCMES reading local message file %s -+ This is an informational message output by BIND10 when it starts to read a -+ local message file. (A local message file may replace the text of one of more -+ messages; the ID of the message will not be changed though.) +% WRITERR error writing to %1: %2 +The specified error was encountered by the message compiler when writing to +the named output file. -UNRECDIR unrecognised directive '%s' -+ A line starting with a dollar symbol was found, but the first word on the line -+ (shown in the message) was not a recognised message compiler directive. +% UNRECDIR line %1: unrecognised directive '%2' +A line starting with a dollar symbol was found, but the first word on the line +(shown in the message) was not a recognised message compiler directive. diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am index b9fe150f92..46065e85c1 100644 --- a/src/lib/log/tests/Makefile.am +++ b/src/lib/log/tests/Makefile.am @@ -22,6 +22,7 @@ run_unittests_SOURCES += message_reader_unittest.cc run_unittests_SOURCES += message_initializer_unittest.cc run_unittests_SOURCES += message_initializer_unittest_2.cc run_unittests_SOURCES += run_unittests.cc +run_unittests_SOURCES += log_formatter_unittest.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc new file mode 100644 index 0000000000..b67831a1b7 --- /dev/null +++ b/src/lib/log/tests/log_formatter_unittest.cc @@ -0,0 +1,125 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include +#include + +using namespace std; + +namespace { + +class FormatterTest : public ::testing::Test { +protected: + typedef pair Output; + typedef isc::log::Formatter Formatter; + vector outputs; +public: + void output(const char* prefix, const string& message) { + outputs.push_back(Output(prefix, message)); + } + // Just shortcut for new string + string* s(const char* text) { + return (new string(text)); + } +}; + +// Create an inactive formatter and check it doesn't produce any output +TEST_F(FormatterTest, inactive) { + Formatter(); + EXPECT_EQ(0, outputs.size()); +} + +// Create an active formatter and check it produces output. Does no arg +// substitution yet +TEST_F(FormatterTest, active) { + Formatter("TEST", s("Text of message"), this); + ASSERT_EQ(1, outputs.size()); + EXPECT_STREQ("TEST", outputs[0].first); + EXPECT_EQ("Text of message", outputs[0].second); +} + +// No output even when we have an arg on the inactive formatter +TEST_F(FormatterTest, inactiveArg) { + Formatter().arg("Hello"); + EXPECT_EQ(0, outputs.size()); +} + +// Create an active formatter and replace a placeholder with string +TEST_F(FormatterTest, stringArg) { + { + SCOPED_TRACE("C++ string"); + Formatter("TEST", s("Hello %1"), this).arg(string("World")); + ASSERT_EQ(1, outputs.size()); + EXPECT_STREQ("TEST", outputs[0].first); + EXPECT_EQ("Hello World", outputs[0].second); + } + { + SCOPED_TRACE("C++ string"); + Formatter("TEST", s("Hello %1"), this).arg(string("Internet")); + ASSERT_EQ(2, outputs.size()); + EXPECT_STREQ("TEST", outputs[1].first); + EXPECT_EQ("Hello Internet", outputs[1].second); + } +} + +// Can convert to string +TEST_F(FormatterTest, intArg) { + Formatter("TEST", s("The answer is %1"), this).arg(42); + ASSERT_EQ(1, outputs.size()); + EXPECT_STREQ("TEST", outputs[0].first); + EXPECT_EQ("The answer is 42", outputs[0].second); +} + +// Can use multiple arguments at different places +TEST_F(FormatterTest, multiArg) { + Formatter("TEST", s("The %2 are %1"), this).arg("switched"). + arg("arguments"); + ASSERT_EQ(1, outputs.size()); + EXPECT_STREQ("TEST", outputs[0].first); + EXPECT_EQ("The arguments are switched", outputs[0].second); +} + +// Can survive and complains if placeholder is missing +TEST_F(FormatterTest, missingPlace) { + EXPECT_NO_THROW(Formatter("TEST", s("Missing the first %2"), this). + arg("missing").arg("argument")); + ASSERT_EQ(1, outputs.size()); + EXPECT_STREQ("TEST", outputs[0].first); + EXPECT_EQ("Missing the first argument " + "@@Missing placeholder %1 for 'missing'@@", outputs[0].second); +} + +// Can replace multiple placeholders +TEST_F(FormatterTest, multiPlaceholder) { + Formatter("TEST", s("The %1 is the %1"), this). + arg("first rule of tautology club"); + ASSERT_EQ(1, outputs.size()); + EXPECT_STREQ("TEST", outputs[0].first); + EXPECT_EQ("The first rule of tautology club is " + "the first rule of tautology club", outputs[0].second); +} + +// Test we can cope with replacement containing the placeholder +TEST_F(FormatterTest, noRecurse) { + // If we recurse, this will probably eat all the memory and crash + Formatter("TEST", s("%1"), this).arg("%1 %1"); + ASSERT_EQ(1, outputs.size()); + EXPECT_STREQ("TEST", outputs[0].first); + EXPECT_EQ("%1 %1", outputs[0].second); +} + +} diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc index 4d8863e084..0a2338b72e 100644 --- a/src/lib/log/tests/logger_support_test.cc +++ b/src/lib/log/tests/logger_support_test.cc @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -92,13 +93,14 @@ int main(int argc, char** argv) { initLogger("alpha", severity, dbglevel, localfile); // Log a few messages - logger_ex.fatal(MSG_MSGWRTERR, "test1", "42"); - logger_ex.error(MSG_UNRECDIR, "false"); - logger_dlm.warn(MSG_MSGRDERR, "a.txt", "dummy test"); - logger_dlm.info(MSG_OPNMSGIN, "example.msg", "dummy test"); - logger_ex.debug(0, MSG_UNRECDIR, "[abc]"); - logger_ex.debug(24, MSG_UNRECDIR, "[24]"); - logger_ex.debug(25, MSG_UNRECDIR, "[25]"); - logger_ex.debug(26, MSG_UNRECDIR, "[26]"); + LOG_FATAL(logger_ex, MSG_WRITERR).arg("test1").arg("42"); + LOG_ERROR(logger_ex, MSG_RDLOCMES).arg("dummy/file"); + LOG_WARN(logger_dlm, MSG_READERR).arg("a.txt").arg("dummy reason"); + LOG_INFO(logger_dlm, MSG_OPENIN).arg("example.msg").arg("dummy reason"); + LOG_DEBUG(logger_ex, 0, MSG_RDLOCMES).arg("dummy/0"); + LOG_DEBUG(logger_ex, 24, MSG_RDLOCMES).arg("dummy/24"); + LOG_DEBUG(logger_ex, 25, MSG_RDLOCMES).arg("dummy/25"); + LOG_DEBUG(logger_ex, 26, MSG_RDLOCMES).arg("dummy/26"); + return (0); } diff --git a/src/lib/log/tests/message_dictionary_unittest.cc b/src/lib/log/tests/message_dictionary_unittest.cc index a92585c4dd..ba33820bc0 100644 --- a/src/lib/log/tests/message_dictionary_unittest.cc +++ b/src/lib/log/tests/message_dictionary_unittest.cc @@ -29,7 +29,7 @@ using namespace std; // and the latter should be present. static const char* values[] = { - "DUPLNS", "duplicate $NAMESPACE directive found", + "MSG_DUPLNS", "duplicate $NAMESPACE directive found", "NEWSYM", "new symbol added", NULL }; @@ -190,7 +190,7 @@ TEST_F(MessageDictionaryTest, GlobalTest) { TEST_F(MessageDictionaryTest, GlobalLoadTest) { vector& duplicates = MessageInitializer::getDuplicates(); ASSERT_EQ(1, duplicates.size()); - EXPECT_EQ(string("DUPLNS"), duplicates[0]); + EXPECT_EQ(string("MSG_DUPLNS"), duplicates[0]); string text = MessageDictionary::globalDictionary().getText("NEWSYM"); EXPECT_EQ(string("new symbol added"), text); diff --git a/src/lib/log/tests/message_reader_unittest.cc b/src/lib/log/tests/message_reader_unittest.cc index 36288f24ee..7b3ba5f8cf 100644 --- a/src/lib/log/tests/message_reader_unittest.cc +++ b/src/lib/log/tests/message_reader_unittest.cc @@ -68,8 +68,8 @@ TEST_F(MessageReaderTest, BlanksAndComments) { EXPECT_NO_THROW(reader_.processLine(" \n ")); EXPECT_NO_THROW(reader_.processLine("# This is a comment")); EXPECT_NO_THROW(reader_.processLine("\t\t # Another comment")); - EXPECT_NO_THROW(reader_.processLine(" + A description line")); - EXPECT_NO_THROW(reader_.processLine("#+ A comment")); + EXPECT_NO_THROW(reader_.processLine(" A description line")); + EXPECT_NO_THROW(reader_.processLine("# A comment")); EXPECT_NO_THROW(reader_.processLine(" +# A description line")); // ... and (b) nothing gets added to either the map or the not-added section. @@ -97,6 +97,15 @@ processLineException(MessageReader& reader, const char* what, } } +// Check that it recognises invalid directives + +TEST_F(MessageReaderTest, InvalidDirectives) { + + // Check that a "$" with nothing else generates an error + processLineException(reader_, "$", MSG_UNRECDIR); + processLineException(reader_, "$xyz", MSG_UNRECDIR); +} + // Check that it can parse a prefix TEST_F(MessageReaderTest, Prefix) { @@ -104,8 +113,8 @@ TEST_F(MessageReaderTest, Prefix) { // Check that no $PREFIX is present EXPECT_EQ(string(""), reader_.getPrefix()); - // Check that a $PREFIX directive with no argument generates an error. - processLineException(reader_, "$PREFIX", MSG_PRFNOARG); + // Check that a $PREFIX directive with no argument is OK + EXPECT_NO_THROW(reader_.processLine("$PREFIX")); // Check a $PREFIX with multiple arguments is invalid processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG); @@ -118,17 +127,19 @@ TEST_F(MessageReaderTest, Prefix) { // A valid prefix should be accepted EXPECT_NO_THROW(reader_.processLine("$PREFIX dlm__")); - EXPECT_EQ(string("DLM__"), reader_.getPrefix()); + EXPECT_EQ(string("dlm__"), reader_.getPrefix()); // And check that the parser fails on invalid prefixes... processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG); - // ... and rejects another valid one - processLineException(reader_, "$PREFIX ABC", MSG_DUPLPRFX); - // Check that we can clear the prefix as well reader_.clearPrefix(); EXPECT_EQ(string(""), reader_.getPrefix()); + + EXPECT_NO_THROW(reader_.processLine("$PREFIX dlm__")); + EXPECT_EQ(string("dlm__"), reader_.getPrefix()); + EXPECT_NO_THROW(reader_.processLine("$PREFIX")); + EXPECT_EQ(string(""), reader_.getPrefix()); } // Check that it can parse a namespace @@ -173,8 +184,8 @@ TEST_F(MessageReaderTest, Namespace) { TEST_F(MessageReaderTest, ValidMessageAddDefault) { // Add a couple of valid messages - reader_.processLine("GLOBAL1\t\tthis is message global one\n"); - reader_.processLine("GLOBAL2 this is message global two"); + reader_.processLine("% GLOBAL1\t\tthis is message global one\n"); + reader_.processLine("%GLOBAL2 this is message global two"); // ... and check them EXPECT_EQ(string("this is message global one"), @@ -191,9 +202,9 @@ TEST_F(MessageReaderTest, ValidMessageAddDefault) { TEST_F(MessageReaderTest, ValidMessageAdd) { // Add a couple of valid messages - reader_.processLine("GLOBAL1\t\tthis is message global one\n", + reader_.processLine("%GLOBAL1\t\tthis is message global one\n", MessageReader::ADD); - reader_.processLine("GLOBAL2 this is message global two", + reader_.processLine("% GLOBAL2 this is message global two", MessageReader::ADD); // ... and check them @@ -214,9 +225,9 @@ TEST_F(MessageReaderTest, ValidMessageReplace) { dictionary_->add("GLOBAL2", "original global2 message"); // Replace a couple of valid messages - reader_.processLine("GLOBAL1\t\tthis is message global one\n", + reader_.processLine("% GLOBAL1\t\tthis is message global one\n", MessageReader::REPLACE); - reader_.processLine("GLOBAL2 this is message global two", + reader_.processLine("% GLOBAL2 this is message global two", MessageReader::REPLACE); // ... and check them @@ -237,14 +248,14 @@ TEST_F(MessageReaderTest, ValidMessageReplace) { TEST_F(MessageReaderTest, Overflows) { // Add a couple of valid messages - reader_.processLine("GLOBAL1\t\tthis is message global one\n"); - reader_.processLine("GLOBAL2 this is message global two"); + reader_.processLine("% GLOBAL1\t\tthis is message global one\n"); + reader_.processLine("% GLOBAL2 this is message global two"); // Add a duplicate in ADD mode. - reader_.processLine("GLOBAL1\t\tthis is a replacement for global one"); + reader_.processLine("% GLOBAL1\t\tthis is a replacement for global one"); // Replace a non-existent one in REPLACE mode - reader_.processLine("LOCAL\t\tthis is a new message", + reader_.processLine("% LOCAL\t\tthis is a new message", MessageReader::REPLACE); // Check what is in the dictionary. diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in index e2bdf6f1bb..e48a781cd7 100755 --- a/src/lib/log/tests/run_time_init_test.sh.in +++ b/src/lib/log/tests/run_time_init_test.sh.in @@ -29,48 +29,49 @@ passfail() { # Create the local message file for testing cat > $localmes << . -NOTHERE this message is not in the global dictionary -MSGRDERR replacement read error, parameters: '%s' and '%s' -UNRECDIR replacement unrecognised directive message, parameter is '%s' +\$PREFIX MSG_ +% NOTHERE this message is not in the global dictionary +% READERR replacement read error, parameters: '%1' and '%2' +% RDLOCMES replacement read local message file, parameter is '%1' . echo -n "1. runInitTest default parameters: " cat > $tempfile << . -FATAL [alpha.example] MSGWRTERR, error writing to test1: 42 -ERROR [alpha.example] UNRECDIR, unrecognised directive 'false' -WARN [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test -INFO [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test +FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42 +ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file +WARN [alpha.dlm] MSG_READERR, error reading from message file a.txt: dummy reason +INFO [alpha.dlm] MSG_OPENIN, unable to open message file example.msg for input: dummy reason . ./logger_support_test | cut -d' ' -f3- | diff $tempfile - passfail $? echo -n "2. Severity filter: " cat > $tempfile << . -FATAL [alpha.example] MSGWRTERR, error writing to test1: 42 -ERROR [alpha.example] UNRECDIR, unrecognised directive 'false' +FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42 +ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file . ./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile - passfail $? echo -n "3. Debug level: " cat > $tempfile << . -FATAL [alpha.example] MSGWRTERR, error writing to test1: 42 -ERROR [alpha.example] UNRECDIR, unrecognised directive 'false' -WARN [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test -INFO [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test -DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]' -DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]' -DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]' +FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42 +ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file +WARN [alpha.dlm] MSG_READERR, error reading from message file a.txt: dummy reason +INFO [alpha.dlm] MSG_OPENIN, unable to open message file example.msg for input: dummy reason +DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/0 +DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/24 +DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/25 . ./logger_support_test -s debug -d 25 | cut -d' ' -f3- | diff $tempfile - passfail $? echo -n "4. Local message replacement: " cat > $tempfile << . -WARN [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification -FATAL [alpha.example] MSGWRTERR, error writing to test1: 42 -ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false' -WARN [alpha.dlm] MSGRDERR, replacement read error, parameters: 'a.txt' and 'dummy test' +WARN [alpha.log] MSG_IDNOTFND, could not replace message text for 'MSG_NOTHERE': no such message +FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42 +ERROR [alpha.example] MSG_RDLOCMES, replacement read local message file, parameter is 'dummy/file' +WARN [alpha.dlm] MSG_READERR, replacement read error, parameters: 'a.txt' and 'dummy reason' . ./logger_support_test -s warn $localmes | cut -d' ' -f3- | diff $tempfile - passfail $? diff --git a/src/lib/nsas/Makefile.am b/src/lib/nsas/Makefile.am index 5f05f1b22d..3ecbca7bac 100644 --- a/src/lib/nsas/Makefile.am +++ b/src/lib/nsas/Makefile.am @@ -4,23 +4,39 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG) AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util +AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas AM_CPPFLAGS += $(SQLITE_CFLAGS) AM_CXXFLAGS = $(B10_CXXFLAGS) -# Some versions of GCC warn about some versions of Boost regarding -# missing initializer for members in its posix_time. +# Some versions of GCC warn about some versions of Boost regarding missing +# initializer for members in its posix_time. # https://svn.boost.org/trac/boost/ticket/3477 # But older GCC compilers don't have the flag. AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG) -if USE_CLANGPP # clang++ complains about unused function parameters in some boost header # files. +if USE_CLANGPP AM_CXXFLAGS += -Wno-unused-parameter endif +# Define rule to build logging source files from message file +nsasdef.h nsasdef.cc: nsasdef.mes + $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/nsas/nsasdef.mes + +# What is being built. lib_LTLIBRARIES = libnsas.la + +# Tell Automake that the nsasdef.{cc,h} source files are created in the build +# process, so it must create these before doing anything else. Although they +# are a dependency of the library (so will be created from the message file +# anyway), there is no guarantee as to exactly _when_ in the build they will be +# created. As the .h file is included in other sources file (so must be +# present when they are compiled), the safest option is to create it first. +BUILT_SOURCES = nsasdef.h nsasdef.cc + +# Library sources. The generated files will not be in the distribution. libnsas_la_SOURCES = address_entry.h address_entry.cc libnsas_la_SOURCES += asiolink.h libnsas_la_SOURCES += hash.cc hash.h @@ -32,9 +48,16 @@ libnsas_la_SOURCES += nameserver_address.h nameserver_address.cc libnsas_la_SOURCES += nameserver_entry.cc nameserver_entry.h libnsas_la_SOURCES += nsas_entry_compare.h libnsas_la_SOURCES += nsas_entry.h nsas_types.h +libnsas_la_SOURCES += nsas_log.cc nsas_log.h libnsas_la_SOURCES += zone_entry.cc zone_entry.h libnsas_la_SOURCES += fetchable.h libnsas_la_SOURCES += address_request_callback.h libnsas_la_SOURCES += glue_hints.h glue_hints.cc -CLEANFILES = *.gcno *.gcda +nodist_libnsas_la_SOURCES = nsasdef.h nsasdef.cc + +# The message file should be in the distribution. +EXTRA_DIST = nsasdef.mes + +# Make sure that the generated files are got rid of in a clean operation +CLEANFILES = *.gcno *.gcda nsasdef.h nsasdef.cc diff --git a/src/lib/nsas/nameserver_address_store.cc b/src/lib/nsas/nameserver_address_store.cc index 3cef38db5a..ac55409816 100644 --- a/src/lib/nsas/nameserver_address_store.cc +++ b/src/lib/nsas/nameserver_address_store.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include "hash_table.h" #include "hash_deleter.h" @@ -31,6 +32,8 @@ #include "zone_entry.h" #include "glue_hints.h" #include "address_request_callback.h" +#include "nsasdef.h" +#include "nsas_log.h" using namespace isc::dns; using namespace std; @@ -84,6 +87,8 @@ NameserverAddressStore::lookup(const string& zone, const RRClass& class_code, boost::shared_ptr callback, AddressFamily family, const GlueHints& glue_hints) { + LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_LOOKUPZONE).arg(zone); + pair > zone_obj( zone_hash_->getOrAdd(HashKey(zone, class_code), boost::bind(newZone, resolver_, &zone, &class_code, @@ -103,6 +108,8 @@ NameserverAddressStore::cancel(const string& zone, const boost::shared_ptr& callback, AddressFamily family) { + LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_LOOKUPCANCEL).arg(zone); + boost::shared_ptr entry(zone_hash_->get(HashKey(zone, class_code))); if (entry) { diff --git a/src/lib/nsas/nameserver_address_store.h b/src/lib/nsas/nameserver_address_store.h index 148052ddc8..87845c932d 100644 --- a/src/lib/nsas/nameserver_address_store.h +++ b/src/lib/nsas/nameserver_address_store.h @@ -38,7 +38,6 @@ template class LruList; namespace nsas { -class ResolverInterface; template class HashTable; class ZoneEntry; class NameserverEntry; diff --git a/src/lib/nsas/nameserver_entry.cc b/src/lib/nsas/nameserver_entry.cc index 4035f79186..65b2ec291d 100644 --- a/src/lib/nsas/nameserver_entry.cc +++ b/src/lib/nsas/nameserver_entry.cc @@ -40,6 +40,7 @@ #include "address_entry.h" #include "nameserver_address.h" #include "nameserver_entry.h" +#include "nsas_log.h" using namespace isc::asiolink; using namespace isc::nsas; @@ -178,6 +179,9 @@ NameserverEntry::updateAddressRTTAtIndex(uint32_t rtt, size_t index, new_rtt = 1; } addresses_[family][index].setRTT(new_rtt); + LOG_DEBUG(nsas_logger, NSAS_DBG_RTT, NSAS_SETRTT) + .arg(addresses_[family][index].getAddress().toText()) + .arg(old_rtt).arg(new_rtt); } void @@ -203,7 +207,7 @@ NameserverEntry::setAddressUnreachable(const IOAddress& address) { * \short A callback into the resolver. * * Whenever we ask the resolver something, this is created and the answer is - * fed back trough this. It holds a shared pointer to the entry so it is not + * fed back through this. It holds a shared pointer to the entry so it is not * destroyed too soon. */ class NameserverEntry::ResolverCallback : @@ -230,6 +234,7 @@ class NameserverEntry::ResolverCallback : if (!response_message || response_message->getRcode() != isc::dns::Rcode::NOERROR() || response_message->getRRCount(isc::dns::Message::SECTION_ANSWER) == 0) { + LOG_ERROR(nsas_logger, NSAS_INVRESPSTR).arg(entry_->getName()); failureInternal(lock); return; } @@ -243,7 +248,12 @@ class NameserverEntry::ResolverCallback : if (response->getType() != type_ || response->getClass() != RRClass(entry_->getClass())) { - // TODO Log we got answer of different type + // Invalid response type or class + LOG_ERROR(nsas_logger, NSAS_INVRESPTC) + .arg(entry_->getName()).arg(type_) + .arg(entry_->getClass()).arg(response->getType()) + .arg(response->getClass()); + failureInternal(lock); return; } @@ -264,8 +274,10 @@ class NameserverEntry::ResolverCallback : } } // If we found it, use it. If not, create a new one. - entries.push_back(found ? *found : AddressEntry(IOAddress( - i->getCurrent().toText()), 1)); + entries.push_back(found ? *found : AddressEntry( + IOAddress(address), 1)); + LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_NSLKUPSUCC) + .arg(address).arg(entry_->getName()); } // We no longer need the previous set of addresses, we have @@ -310,6 +322,8 @@ class NameserverEntry::ResolverCallback : * So mark the current address family as unreachable. */ virtual void failure() { + LOG_DEBUG(nsas_logger, NSAS_DBG_RESULTS, NSAS_NSLKUPFAIL) + .arg(type_).arg(entry_->getName()); Lock lock(entry_->mutex_); failureInternal(lock); } @@ -422,6 +436,8 @@ NameserverEntry::askIP(isc::resolve::ResolverInterface* resolver, // Ask for both types of addresses // We are unlocked here, as the callback from that might want to lock lock.unlock(); + + LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_NSADDR).arg(getName()); askIP(resolver, RRType::A(), V4_ONLY); askIP(resolver, RRType::AAAA(), V6_ONLY); // Make sure we end the routine when we are not locked diff --git a/src/lib/log/message_exception.cc b/src/lib/nsas/nsas_log.cc similarity index 83% rename from src/lib/log/message_exception.cc rename to src/lib/nsas/nsas_log.cc index 1a69ca551b..931b131877 100644 --- a/src/lib/log/message_exception.cc +++ b/src/lib/nsas/nsas_log.cc @@ -12,15 +12,15 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -/// \brief Body of Virtual Destructor +/// Defines the logger used by the NSAS -#include +#include "nsas/nsas_log.h" namespace isc { -namespace log { +namespace nsas { -MessageException::~MessageException() throw() { -} +isc::log::Logger nsas_logger("nsas"); -} // namespace log +} // namespace nsas } // namespace isc + diff --git a/src/lib/nsas/nsas_log.h b/src/lib/nsas/nsas_log.h new file mode 100644 index 0000000000..9631988ac7 --- /dev/null +++ b/src/lib/nsas/nsas_log.h @@ -0,0 +1,53 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __NSAS_LOG__H +#define __NSAS_LOG__H + +#include +#include "nsasdef.h" + +namespace isc { +namespace nsas { + +/// \brief NSAS Logging +/// +/// Defines the levels used to output debug messages in the NSAS. Note that +/// higher numbers equate to more verbose (and detailed) output. + +// The first level traces normal operations - asking the NSAS for an address, +// and cancelling a lookup. It also records when the NSAS calls back to the +// resolver to resolve something. +const int NSAS_DBG_TRACE = 10; + +// The next level extends the normal operations and records the results of the +// lookups. +const int NSAS_DBG_RESULTS = 20; + +// Additional information on the usage of the names - the RTT values obtained +// when queries were done. +const int NSAS_DBG_RTT = 30; + + +/// \brief NSAS Logger +/// +/// Define the logger used to log messages. We could define it in multiple +/// modules, but defining in a single module and linking to it saves time and +/// space. +extern isc::log::Logger nsas_logger; // isc::nsas::logger is the NSAS logger + +} // namespace nsas +} // namespace isc + +#endif // __NSAS_LOG__H diff --git a/src/lib/nsas/nsasdef.mes b/src/lib/nsas/nsasdef.mes new file mode 100644 index 0000000000..0f32d09919 --- /dev/null +++ b/src/lib/nsas/nsasdef.mes @@ -0,0 +1,61 @@ +# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +$PREFIX NSAS_ +$NAMESPACE isc::nsas + +% INVRESPSTR queried for %1 but got invalid response +This message indicates an internal error in the nameserver address store +component (NSAS) of the resolver. The NSAS made a query for a RR for the +specified nameserver but received an invalid response. Either the success +function was called without a DNS message or the message was invalid on some +way. (In the latter case, the error should have been picked up elsewhere in +the processing logic, hence the raising of the error here.) + +% INVRESPTC queried for %1 RR of type/class %2/%3, received response %4/%5 +This message indicates an internal error in the nameserver address store +component (NSAS) of the resolver. The NSAS made a query for the given RR +type and class, but instead received an answer with the given type and class. + +% LOOKUPCANCEL lookup for zone %1 has been cancelled +A debug message, this is output when a NSAS (nameserver address store - +part of the resolver) lookup for a zone has been cancelled. + +% LOOKUPZONE searching NSAS for nameservers for zone %1 +A debug message, this is output when a call is made to the nameserver address +store (part of the resolver) to obtain the nameservers for the specified zone. + +% NSADDR asking resolver to obtain A and AAAA records for %1 +A debug message, the NSAS (nameserver address store - part of the resolver) is +making a callback into the resolver to retrieve the address records for the +specified nameserver. + +% NSLKUPFAIL failed to lookup any %1 for %2 +A debug message, the NSAS (nameserver address store - part of the resolver) +has been unable to retrieve the specified resource record for the specified +nameserver. This is not necessarily a problem - the nameserver may be +unreachable, in which case the NSAS will try other nameservers in the zone. + +% NSLKUPSUCC found address %1 for %2 +A debug message, the NSAS (nameserver address store - part of the resolver) +has retrieved the given address for the specified nameserver through an +external query. + +% SETRTT reporting RTT for %1 as %2; new value is now %3 +A NSAS (nameserver address store - part of the resolver) debug message +reporting the round-trip time (RTT) for a query made to the specified +nameserver. The RTT has been updated using the value given and the new RTT is +displayed. (The RTT is subject to a calculation that damps out sudden +changes. As a result, the new RTT is not necessarily equal to the RTT +reported.) diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am index 56f36d152a..e9235ba169 100644 --- a/src/lib/nsas/tests/Makefile.am +++ b/src/lib/nsas/tests/Makefile.am @@ -53,6 +53,7 @@ endif run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la +run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la diff --git a/src/lib/nsas/tests/nameserver_address_unittest.cc b/src/lib/nsas/tests/nameserver_address_unittest.cc index e3bc5de8e2..1b211e9e3e 100644 --- a/src/lib/nsas/tests/nameserver_address_unittest.cc +++ b/src/lib/nsas/tests/nameserver_address_unittest.cc @@ -51,7 +51,8 @@ public: ns_.reset(new NameserverEntry(name_.toText(), RRClass::IN())); ns_->askIP(resolver_.get(), boost::shared_ptr(new Callback), ANY_OK); resolver_->asksIPs(name_, 0, 1); - resolver_->requests[0].second->success(createResponseMessage(rrv4_)); + resolver_->requests[0].second->success( + isc::util::unittests::createResponseMessage(rrv4_)); } // Return the sample NameserverEntry diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc index 7e9f6750bf..3435d26b77 100644 --- a/src/lib/nsas/tests/nameserver_entry_unittest.cc +++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc @@ -72,7 +72,8 @@ private: RRsetPtr set) { if (set) { - resolver->requests[index].second->success(createResponseMessage(set)); + resolver->requests[index].second->success( + isc::util::unittests::createResponseMessage(set)); } else { resolver->requests[index].second->failure(); } diff --git a/src/lib/nsas/tests/nsas_test.h b/src/lib/nsas/tests/nsas_test.h index 033df8101b..2dd95effd5 100644 --- a/src/lib/nsas/tests/nsas_test.h +++ b/src/lib/nsas/tests/nsas_test.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -35,24 +36,12 @@ #include #include #include -#include #include "../nsas_entry.h" using namespace isc::dns::rdata; using namespace isc::dns; using namespace isc::util; - -namespace { - MessagePtr - createResponseMessage(RRsetPtr answer_rrset) - { - MessagePtr response(new Message(Message::RENDER)); - response->setOpcode(Opcode::QUERY()); - response->setRcode(Rcode::NOERROR()); - response->addRRset(Message::SECTION_ANSWER, answer_rrset); - return response; - } -} +using isc::util::unittests::TestResolver; namespace isc { namespace dns { @@ -223,139 +212,6 @@ private: static const uint32_t HASHTABLE_DEFAULT_SIZE = 1009; ///< First prime above 1000 -using namespace std; - -/* - * This pretends to be a resolver. It stores the queries and - * they can be answered. - */ -class TestResolver : public isc::resolve::ResolverInterface { - private: - bool checkIndex(size_t index) { - return (requests.size() > index); - } - - typedef std::map - PresetAnswers; - PresetAnswers answers_; - public: - typedef pair Request; - vector requests; - - /// \brief Destructor - /// - /// This is important. All callbacks in the requests vector must be - /// called to remove them from internal loops. Without this, destroying - /// the NSAS object will leave memory assigned. - ~TestResolver() { - for (size_t i = 0; i < requests.size(); ++i) { - requests[i].second->failure(); - } - } - - virtual void resolve(const QuestionPtr& q, const CallbackPtr& c) { - PresetAnswers::iterator it(answers_.find(*q)); - if (it == answers_.end()) { - requests.push_back(Request(q, c)); - } else { - if (it->second) { - c->success(createResponseMessage(it->second)); - } else { - c->failure(); - } - } - } - - /* - * Add a preset answer. If shared_ptr() is passed (eg. NULL), - * it will generate failure. If the question is not preset, - * it goes to requests and you can answer later. - */ - void addPresetAnswer(const isc::dns::Question& question, - RRsetPtr answer) - { - answers_[question] = answer; - } - - // Thrown if the query at the given index does not exist. - class NoSuchRequest : public std::exception { }; - - // Thrown if the answer does not match the query - class DifferentRequest : public std::exception { }; - - QuestionPtr operator[](size_t index) { - if (index >= requests.size()) { - throw NoSuchRequest(); - } - return (requests[index].first); - } - /* - * Looks if the two provided requests in resolver are A and AAAA. - * Sorts them so index1 is A. - * - * Returns false if there aren't enough elements - */ - bool asksIPs(const Name& name, size_t index1, size_t index2) { - size_t max = (index1 < index2) ? index2 : index1; - if (!checkIndex(max)) { - return false; - } - EXPECT_EQ(name, (*this)[index1]->getName()); - EXPECT_EQ(name, (*this)[index2]->getName()); - EXPECT_EQ(RRClass::IN(), (*this)[index1]->getClass()); - EXPECT_EQ(RRClass::IN(), (*this)[index2]->getClass()); - // If they are the other way around, swap - if ((*this)[index1]->getType() == RRType::AAAA() && - (*this)[index2]->getType() == RRType::A()) - { - TestResolver::Request tmp((*this).requests[index1]); - (*this).requests[index1] = - (*this).requests[index2]; - (*this).requests[index2] = tmp; - } - // Check the correct addresses - EXPECT_EQ(RRType::A(), (*this)[index1]->getType()); - EXPECT_EQ(RRType::AAAA(), (*this)[index2]->getType()); - return (true); - } - - /* - * Sends a simple answer to a query. - * 1) Provide index of a query and the address(es) to pass. - * 2) Provide index of query and components of address to pass. - */ - void answer(size_t index, RRsetPtr& set) { - if (index >= requests.size()) { - throw NoSuchRequest(); - } - requests[index].second->success(createResponseMessage(set)); - } - - void answer(size_t index, const Name& name, const RRType& type, - const rdata::Rdata& rdata, size_t TTL = 100) - { - RRsetPtr set(new RRset(name, RRClass::IN(), - type, RRTTL(TTL))); - set->addRdata(rdata); - answer(index, set); - } - - - void provideNS(size_t index, - RRsetPtr nameservers) - { - if (index >= requests.size()) { - throw NoSuchRequest(); - } - if (requests[index].first->getName() != nameservers->getName() || - requests[index].first->getType() != RRType::NS()) - { - throw DifferentRequest(); - } - requests[index].second->success(createResponseMessage(nameservers)); - } -}; - // String constants. These should end in a dot. static const std::string EXAMPLE_CO_UK("example.co.uk."); static const std::string EXAMPLE_NET("example.net."); diff --git a/src/lib/nsas/tests/run_unittests.cc b/src/lib/nsas/tests/run_unittests.cc index d1277ad03f..bc672d0eec 100644 --- a/src/lib/nsas/tests/run_unittests.cc +++ b/src/lib/nsas/tests/run_unittests.cc @@ -13,14 +13,23 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include + +#include +#include #include #include +#include + +using namespace std; int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + return (RUN_ALL_TESTS()); } diff --git a/src/lib/python/bind10_config.py.in b/src/lib/python/bind10_config.py.in index 3f2947d7cb..4a38903731 100644 --- a/src/lib/python/bind10_config.py.in +++ b/src/lib/python/bind10_config.py.in @@ -17,7 +17,37 @@ # variables to python scripts and libraries. import os -BIND10_MSGQ_SOCKET_FILE = os.path.join("@localstatedir@", - "@PACKAGE_NAME@", - "msgq_socket").replace("${prefix}", - "@prefix@") +def reload(): + # In a function, for testing purposes + global BIND10_MSGQ_SOCKET_FILE + global DATA_PATH + global PLUGIN_PATHS + global PREFIX + BIND10_MSGQ_SOCKET_FILE = os.path.join("@localstatedir@", + "@PACKAGE_NAME@", + "msgq_socket").replace("${prefix}", + "@prefix@") + PREFIX = "@prefix@" + + # If B10_FROM_SOURCE is set in the environment, we use data files + # from a directory relative to the value of that variable, or, if defined, + # relative to the value of B10_FROM_SOURCE_LOCALSTATEDIR. Otherwise + # we use the ones installed on the system. + # B10_FROM_SOURCE_LOCALSTATEDIR is specifically intended to be used for + # tests where we want to use variuos types of configuration within the test + # environment. (We may want to make it even more generic so that the path is + # passed from the boss process) + if "B10_FROM_SOURCE" in os.environ: + if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ: + DATA_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + else: + DATA_PATH = os.environ["B10_FROM_SOURCE"] + PLUGIN_PATHS = [DATA_PATH + '/src/bin/cfgmgr/plugins'] + else: + DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX) + PLUGIN_PATHS = ["@prefix@/share/@PACKAGE@/config_plugins"] + # For testing the plugins so they can find their own spec files + if "B10_TEST_PLUGIN_DIR" in os.environ: + PLUGIN_PATHS = os.environ["B10_TEST_PLUGIN_DIR"].split(':') + +reload() diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py index db80c3378a..178a983eac 100644 --- a/src/lib/python/isc/notify/notify_out.py +++ b/src/lib/python/isc/notify/notify_out.py @@ -304,7 +304,7 @@ class NotifyOut: try: r_fds, w, e = select.select(valid_socks, [], [], block_timeout) except select.error as err: - if err.args[0] != EINTR: + if err.args[0] != errno.EINTR: return {}, {} if self._read_sock in r_fds: # user has called shutdown() diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am index 07129ec76b..a83ff86a00 100644 --- a/src/lib/python/isc/notify/tests/Makefile.am +++ b/src/lib/python/isc/notify/tests/Makefile.am @@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS) # required by loadable python modules. LIBRARY_PATH_PLACEHOLDER = if SET_ENV_LIBRARY_PATH -LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) +LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH) endif # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/lib/python/isc/util/Makefile.am b/src/lib/python/isc/util/Makefile.am index 7ab8048823..f6cbb783cf 100644 --- a/src/lib/python/isc/util/Makefile.am +++ b/src/lib/python/isc/util/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = . tests -python_PYTHON = __init__.py process.py socketserver_mixin.py +python_PYTHON = __init__.py process.py socketserver_mixin.py file.py pythondir = $(pyexecdir)/isc/util diff --git a/src/bin/stats/run_b10-stats_stub.sh.in b/src/lib/python/isc/util/file.py similarity index 60% rename from src/bin/stats/run_b10-stats_stub.sh.in rename to src/lib/python/isc/util/file.py index 19ade5c76a..faef9a84ab 100644 --- a/src/bin/stats/run_b10-stats_stub.sh.in +++ b/src/lib/python/isc/util/file.py @@ -1,6 +1,4 @@ -#! /bin/sh - -# Copyright (C) 2010 Internet Systems Consortium. +# Copyright (C) 2011 Internet Systems Consortium. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -15,19 +13,17 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@} -export PYTHON_EXEC +"""Various functions for working with files and directories.""" -PYTHONPATH=@abs_top_builddir@/src/lib/python -export PYTHONPATH +from os.path import exists, join -B10_FROM_BUILD=@abs_top_srcdir@ -export B10_FROM_BUILD - -BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket -export BIND10_MSGQ_SOCKET_FILE - -STATS_PATH=@abs_top_builddir@/src/bin/stats - -cd ${STATS_PATH} -exec ${PYTHON_EXEC} -O b10-stats_stub "$@" +def path_search(filename, paths): + """ + Searches list of paths to find filename in one of them. The found one will + be returned or IOError will be returned if it isn't found. + """ + for p in paths: + f = join(p, filename) + if exists(f): + return f + raise IOError("'" + filename + "' not found in " + str(paths)) diff --git a/src/lib/python/isc/util/tests/Makefile.am b/src/lib/python/isc/util/tests/Makefile.am index f32fda0043..0ce96de5df 100644 --- a/src/lib/python/isc/util/tests/Makefile.am +++ b/src/lib/python/isc/util/tests/Makefile.am @@ -1,5 +1,5 @@ PYCOVERAGE_RUN = @PYCOVERAGE_RUN@ -PYTESTS = process_test.py socketserver_mixin_test.py +PYTESTS = process_test.py socketserver_mixin_test.py file_test.py EXTRA_DIST = $(PYTESTS) # test using command-line arguments, so use check-local target instead of TESTS diff --git a/src/bin/stats/tests/stats_test.in b/src/lib/python/isc/util/tests/file_test.py similarity index 51% rename from src/bin/stats/tests/stats_test.in rename to src/lib/python/isc/util/tests/file_test.py index a3279a7709..fb765d7248 100644 --- a/src/bin/stats/tests/stats_test.in +++ b/src/lib/python/isc/util/tests/file_test.py @@ -1,6 +1,4 @@ -#! /bin/sh - -# Copyright (C) 2010 Internet Systems Consortium. +# Copyright (C) 2011 Internet Systems Consortium. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -15,17 +13,20 @@ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@} -export PYTHON_EXEC +import isc.util.file +import unittest -PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_srcdir@/src/bin/stats:@abs_top_srcdir@/src/bin/stats/tests -export PYTHONPATH +class FileTest(unittest.TestCase): + def test_search_path_find(self): + """Test it returns the first occurence of the file""" + self.assertEqual('./Makefile', + isc.util.file.path_search('Makefile', + ['/no/such/directory/', '.', + '../tests/'])) -B10_FROM_BUILD=@abs_top_builddir@ -export B10_FROM_BUILD + def test_search_path_notfound(self): + """Test it throws an exception when the file can't be found""" + self.assertRaises(IOError, isc.util.file.path_search, 'no file', ['/no/such/directory']) -TEST_PATH=@abs_top_srcdir@/src/bin/stats/tests - -cd ${TEST_PATH} -${PYTHON_EXEC} -O b10-stats_test.py $* -${PYTHON_EXEC} -O b10-stats_stub_test.py $* +if __name__ == "__main__": + unittest.main() diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc index c0d5ee64fb..b753cc92b8 100644 --- a/src/lib/resolve/recursive_query.cc +++ b/src/lib/resolve/recursive_query.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,65 @@ using namespace isc::asiolink; namespace isc { namespace asiodns { +namespace { +// Function to check if the given name/class has any address in the cache +bool +hasAddress(const Name& name, const RRClass& rrClass, + const isc::cache::ResolverCache& cache) +{ + // FIXME: If we are single-stack and we get only the other type of + // address, what should we do? In that case, it will be considered + // unreachable, which is most probably true, because A and AAAA will + // usually have the same RTT, so we should have both or none from the + // glue. + return (cache.lookup(name, RRType::A(), rrClass) != RRsetPtr() || + cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr()); +} + +} + +/// \brief Find deepest usable delegation in the cache +/// +/// This finds the deepest delegation we have in cache and is safe to use. +/// It is not public function, therefore it's not in header. But it's not +/// in anonymous namespace, so we can call it from unittests. +/// \param name The name we want to delegate to. +/// \param cache The place too look for known delegations. +std::string +deepestDelegation(Name name, RRClass rrclass, + isc::cache::ResolverCache& cache) +{ + RRsetPtr cachedNS; + // Look for delegation point from bottom, until we find one with + // IP address or get to root. + // + // We need delegation with IP address so we can ask it right away. + // If we don't have the IP address, we would need to ask above it + // anyway in the best case, and the NS could be inside the zone, + // and we could get all loopy with the NSAS in the worst case. + while (name.getLabelCount() > 1 && + (cachedNS = cache.lookupDeepestNS(name, rrclass)) != RRsetPtr()) { + // Look if we have an IP address for the NS + for (RdataIteratorPtr ns(cachedNS->getRdataIterator()); + !ns->isLast(); ns->next()) { + // Do we have IP for this specific NS? + if (hasAddress(dynamic_cast( + ns->getCurrent()).getNSName(), rrclass, + cache)) { + // Found one, stop checking and use this zone + // (there may be more addresses, that's only better) + return (cachedNS->getName().toText()); + } + } + // We don't have anything for this one, so try something higher + if (name.getLabelCount() > 1) { + name = name.split(1); + } + } + // Fallback, nothing found, start at root + return ("."); +} + typedef std::vector > AddressVector; // Here we do not use the typedef above, as the SunStudio compiler @@ -131,13 +191,12 @@ private: // Info for (re)sending the query (the question and destination) Question question_; + // This is the query message got from client + ConstMessagePtr query_message_; + // This is where we build and store our final answer MessagePtr answer_message_; - // currently we use upstream as the current list of NS records - // we should differentiate between forwarding and resolving - boost::shared_ptr upstream_; - // Test server - only used for testing. This takes precedence over all // other servers if the port is non-zero. std::pair test_server_; @@ -239,7 +298,6 @@ private: // if we have a response for our query stored already. if // so, call handlerecursiveresponse(), if not, we call send() void doLookup() { - cur_zone_ = "."; dlog("doLookup: try cache"); Message cached_message(Message::RENDER); isc::resolve::initResponseMessage(question_, cached_message); @@ -255,9 +313,12 @@ private: stop(); } } else { + dlog("doLookup: get lowest usable delegation from cache"); + cur_zone_ = deepestDelegation(question_.getName(), + question_.getClass(), cache_); send(); } - + } // Send the current question to the given nameserver address @@ -282,13 +343,8 @@ private: } } - // 'general' send; if we are in forwarder mode, send a query to - // a random nameserver in our forwarders list. If we are in - // recursive mode, ask the NSAS to give us an address. + // 'general' send, ask the NSAS to give us an address. void send(IOFetch::Protocol protocol = IOFetch::UDP) { - // If are in forwarder mode, send it to a random - // forwarder. If not, ask the NSAS for an address - const int uc = upstream_->size(); protocol_ = protocol; // Store protocol being used for this if (test_server_.second != 0) { dlog("Sending upstream query (" + question_.toText() + @@ -300,18 +356,6 @@ private: test_server_.second, buffer_, this, query_timeout_); io_.get_io_service().post(query); - } else if (uc > 0) { - // TODO: use boost, or rand()-utility function we provide - int serverIndex = rand() % uc; - dlog("Sending upstream query (" + question_.toText() + - ") to " + upstream_->at(serverIndex).first); - ++outstanding_events_; - gettimeofday(¤t_ns_qsent_time, NULL); - IOFetch query(protocol, io_, question_, - upstream_->at(serverIndex).first, - upstream_->at(serverIndex).second, buffer_, this, - query_timeout_); - io_.get_io_service().post(query); } else { // Ask the NSAS for an address for the current zone, // the callback will call the actual sendTo() @@ -486,7 +530,6 @@ public: RunningQuery(IOService& io, const Question& question, MessagePtr answer_message, - boost::shared_ptr upstream, std::pair& test_server, OutputBufferPtr buffer, isc::resolve::ResolverInterface::CallbackPtr cb, @@ -498,8 +541,8 @@ public: : io_(io), question_(question), + query_message_(), answer_message_(answer_message), - upstream_(upstream), test_server_(test_server), buffer_(buffer), resolvercallback_(cb), @@ -647,8 +690,7 @@ public: incoming.fromWire(ibuf); buffer_->clear(); - if (recursive_mode() && - incoming.getRcode() == Rcode::NOERROR()) { + if (incoming.getRcode() == Rcode::NOERROR()) { done_ = handleRecursiveAnswer(incoming); } else { isc::resolve::copyResponseMessage(incoming, answer_message_); @@ -682,13 +724,11 @@ public: } else if (!done_ && retries_--) { // Query timed out, but we have some retries, so send again dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", resending query"); - if (recursive_mode()) { - current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE); - } + current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE); send(); } else { // We are either already done, or out of retries - if (recursive_mode() && result == IOFetch::TIME_OUT) { + if (result == IOFetch::TIME_OUT) { dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", giving up"); current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE); } @@ -705,12 +745,179 @@ public: void makeSERVFAIL() { isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL()); } - - // Returns true if we are in 'recursive' mode - // Returns false if we are in 'forwarding' mode - // (i.e. if we have anything in upstream_) - bool recursive_mode() const { - return upstream_->empty(); +}; + +class ForwardQuery : public IOFetch::Callback { +private: + // The io service to handle async calls + IOService& io_; + + // This is the query message got from client + ConstMessagePtr query_message_; + + // This is where we build and store our final answer + MessagePtr answer_message_; + + // List of nameservers to forward to + boost::shared_ptr upstream_; + + // Buffer to store the result. + OutputBufferPtr buffer_; + + // This will be notified when we succeed or fail + isc::resolve::ResolverInterface::CallbackPtr resolvercallback_; + + /* + * TODO Do something more clever with timeouts. In the long term, some + * computation of average RTT, increase with each retry, etc. + */ + // Timeout information + int query_timeout_; + + // TODO: replace by our wrapper + asio::deadline_timer client_timer; + asio::deadline_timer lookup_timer; + + // Make FowardQuery deletes itself safely. for more information see + // the comments of outstanding_events in RunningQuery. + size_t outstanding_events_; + + // If we have a client timeout, we call back with a failure message, + // but we do not stop yet. We use this variable to make sure we + // don't call back a second time later + bool callback_called_; + + // send the query to the server. + void send(IOFetch::Protocol protocol = IOFetch::UDP) { + const int uc = upstream_->size(); + buffer_->clear(); + int serverIndex = rand() % uc; + ConstQuestionPtr question = *(query_message_->beginQuestion()); + dlog("Sending upstream query (" + question->toText() + + ") to " + upstream_->at(serverIndex).first); + ++outstanding_events_; + // Forward the query, create the IOFetch with + // query message, so that query flags can be forwarded + // together. + IOFetch query(protocol, io_, query_message_, + upstream_->at(serverIndex).first, + upstream_->at(serverIndex).second, + buffer_, this, query_timeout_); + + io_.get_io_service().post(query); + } + +public: + ForwardQuery(IOService& io, + ConstMessagePtr query_message, + MessagePtr answer_message, + boost::shared_ptr upstream, + OutputBufferPtr buffer, + isc::resolve::ResolverInterface::CallbackPtr cb, + int query_timeout, int client_timeout, int lookup_timeout) : + io_(io), + query_message_(query_message), + answer_message_(answer_message), + upstream_(upstream), + buffer_(buffer), + resolvercallback_(cb), + query_timeout_(query_timeout), + client_timer(io.get_io_service()), + lookup_timer(io.get_io_service()), + outstanding_events_(0), + callback_called_(false) + { + // Setup the timer to stop trying (lookup_timeout) + if (lookup_timeout >= 0) { + lookup_timer.expires_from_now( + boost::posix_time::milliseconds(lookup_timeout)); + ++outstanding_events_; + lookup_timer.async_wait(boost::bind(&ForwardQuery::lookupTimeout, this)); + } + + // Setup the timer to send an answer (client_timeout) + if (client_timeout >= 0) { + client_timer.expires_from_now( + boost::posix_time::milliseconds(client_timeout)); + ++outstanding_events_; + client_timer.async_wait(boost::bind(&ForwardQuery::clientTimeout, this)); + } + + send(); + } + + virtual void lookupTimeout() { + if (!callback_called_) { + makeSERVFAIL(); + callCallback(false); + } + assert(outstanding_events_ > 0); + --outstanding_events_; + stop(); + } + + virtual void clientTimeout() { + if (!callback_called_) { + makeSERVFAIL(); + callCallback(false); + } + assert(outstanding_events_ > 0); + --outstanding_events_; + stop(); + } + + // If the callback has not been called yet, call it now + // If success is true, we call 'success' with our answer_message + // If it is false, we call failure() + void callCallback(bool success) { + if (!callback_called_) { + callback_called_ = true; + if (success) { + resolvercallback_->success(answer_message_); + } else { + resolvercallback_->failure(); + } + } + } + + virtual void stop() { + // if we cancel our timers, we will still get an event for + // that, so we cannot delete ourselves just yet (those events + // would be bound to a deleted object) + // cancel them one by one, both cancels should get us back + // here again. + // same goes if we have an outstanding query (can't delete + // until that one comes back to us) + lookup_timer.cancel(); + client_timer.cancel(); + if (outstanding_events_ > 0) { + return; + } else { + delete this; + } + } + + // This function is used as callback from DNSQuery. + virtual void operator()(IOFetch::Result result) { + // XXX is this the place for TCP retry? + assert(outstanding_events_ > 0); + --outstanding_events_; + if (result != IOFetch::TIME_OUT) { + // we got an answer + Message incoming(Message::PARSE); + InputBuffer ibuf(buffer_->getData(), buffer_->getLength()); + incoming.fromWire(ibuf); + isc::resolve::copyResponseMessage(incoming, answer_message_); + callCallback(true); + } + + stop(); + } + + // Clear the answer parts of answer_message, and set the rcode + // to servfail + void makeSERVFAIL() { + isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL()); } }; @@ -753,7 +960,7 @@ RecursiveQuery::resolve(const QuestionPtr& question, } else { dlog("Message not found in cache, starting recursive query"); // It will delete itself when it is done - new RunningQuery(io, *question, answer_message, upstream_, + new RunningQuery(io, *question, answer_message, test_server_, buffer, callback, query_timeout_, client_timeout_, lookup_timeout_, retries_, nsas_, @@ -807,7 +1014,7 @@ RecursiveQuery::resolve(const Question& question, } else { dlog("Message not found in cache, starting recursive query"); // It will delete itself when it is done - new RunningQuery(io, question, answer_message, upstream_, + new RunningQuery(io, question, answer_message, test_server_, buffer, crs, query_timeout_, client_timeout_, lookup_timeout_, retries_, nsas_, cache_, rtt_recorder_); @@ -815,5 +1022,36 @@ RecursiveQuery::resolve(const Question& question, } } +void +RecursiveQuery::forward(ConstMessagePtr query_message, + MessagePtr answer_message, + OutputBufferPtr buffer, + DNSServer* server, + isc::resolve::ResolverInterface::CallbackPtr callback) +{ + // XXX: eventually we will need to be able to determine whether + // the message should be sent via TCP or UDP, or sent initially via + // UDP and then fall back to TCP on failure, but for the moment + // we're only going to handle UDP. + IOService& io = dns_service_.getIOService(); + + if (!callback) { + callback.reset(new isc::resolve::ResolverCallbackServer(server)); + } + + // TODO: general 'prepareinitialanswer' + answer_message->setOpcode(isc::dns::Opcode::QUERY()); + ConstQuestionPtr question = *query_message->beginQuestion(); + answer_message->addQuestion(*question); + + // implement the simplest forwarder, which will pass + // everything throught without interpretation, except + // QID, port number. The response will not be cached. + // It will delete itself when it is done + new ForwardQuery(io, query_message, answer_message, + upstream_, buffer, callback, query_timeout_, + client_timeout_, lookup_timeout_); +} + } // namespace asiodns } // namespace isc diff --git a/src/lib/resolve/recursive_query.h b/src/lib/resolve/recursive_query.h index c082426b4a..b9fb80df0b 100644 --- a/src/lib/resolve/recursive_query.h +++ b/src/lib/resolve/recursive_query.h @@ -141,6 +141,20 @@ public: isc::util::OutputBufferPtr buffer, DNSServer* server); + /// \brief Initiates forwarding for the given query. + /// + /// Others parameters are same with the parameters of + /// function resolve(). + /// + /// \param query_message the full query got from client. + /// \param callback callback object + void forward(isc::dns::ConstMessagePtr query_message, + isc::dns::MessagePtr answer_message, + isc::util::OutputBufferPtr buffer, + DNSServer* server, + isc::resolve::ResolverInterface::CallbackPtr callback = + isc::resolve::ResolverInterface::CallbackPtr()); + /// \brief Set Test Server /// /// This method is *only* for unit testing the class. If set, it enables diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc index 3338893651..4e939fa70d 100644 --- a/src/lib/resolve/tests/recursive_query_unittest.cc +++ b/src/lib/resolve/tests/recursive_query_unittest.cc @@ -31,10 +31,13 @@ #include #include +#include #include +#include #include #include +#include // IMPORTANT: We shouldn't directly use ASIO definitions in this test. // In particular, we must not include asio.hpp in this file. @@ -59,6 +62,18 @@ using namespace isc::asiolink; using namespace isc::dns; using namespace isc::util; +namespace isc { +namespace asiodns { + +// This is defined in recursive_query.cc, but not in header (it's not public +// function). So bring it in to be tested. +std::string +deepestDelegation(Name name, RRClass rrclass, + isc::cache::ResolverCache& cache); + +} +} + namespace { const char* const TEST_SERVER_PORT = "53535"; const char* const TEST_CLIENT_PORT = "53536"; @@ -110,6 +125,9 @@ class RecursiveQueryTest : public ::testing::Test { protected: RecursiveQueryTest(); ~RecursiveQueryTest() { + // It would delete itself, but after the io_service_, which could + // segfailt in case there were unhandled requests + resolver_.reset(); if (res_ != NULL) { freeaddrinfo(res_); } @@ -348,12 +366,6 @@ protected: private: bool* done_; }; - - class MockResolver : public isc::resolve::ResolverInterface { - void resolve(const QuestionPtr& question, - const ResolverInterface::CallbackPtr& callback) { - } - }; // This version of mock server just stops the io_service when it is resumed // the second time. (Used in the clientTimeout test, where resume @@ -423,16 +435,17 @@ protected: vector callback_data_; int sock_; struct addrinfo* res_; + boost::shared_ptr resolver_; }; RecursiveQueryTest::RecursiveQueryTest() : dns_service_(NULL), callback_(NULL), callback_protocol_(0), - callback_native_(-1), sock_(-1), res_(NULL) + callback_native_(-1), sock_(-1), res_(NULL), + resolver_(new isc::util::unittests::TestResolver()) { io_service_ = new IOService(); setDNSService(true, true); - boost::shared_ptrmock_resolver(new MockResolver()); - nsas_ = new isc::nsas::NameserverAddressStore(mock_resolver); + nsas_ = new isc::nsas::NameserverAddressStore(resolver_); } TEST_F(RecursiveQueryTest, v6UDPSend) { @@ -567,9 +580,12 @@ TEST_F(RecursiveQueryTest, forwarderSend) { singleAddress(TEST_IPV4_ADDR, port)); Question q(Name("example.com"), RRClass::IN(), RRType::TXT()); + Message query_message(Message::RENDER); + isc::resolve::initResponseMessage(q, query_message); + OutputBufferPtr buffer(new OutputBuffer(0)); MessagePtr answer(new Message(Message::RENDER)); - rq.resolve(q, answer, buffer, &server); + rq.forward(ConstMessagePtr(&query_message), answer, buffer, &server); char data[4096]; size_t size = sizeof(data); @@ -634,8 +650,41 @@ bool tryRead(int sock_, int recv_options, size_t max, int* num) { return true; } +// Mock resolver callback for testing forward query. +class MockResolverCallback : public isc::resolve::ResolverInterface::Callback { +public: + enum ResultValue { + DEFAULT = 0, + SUCCESS = 1, + FAILURE = 2 + }; -// Test it tries the correct amount of times before giving up + MockResolverCallback(DNSServer* server): + result(DEFAULT), + server_(server->clone()) + {} + + ~MockResolverCallback() { + delete server_; + } + + void success(const isc::dns::MessagePtr response) { + result = SUCCESS; + server_->resume(true); + } + + void failure() { + result = FAILURE; + server_->resume(false); + } + + uint32_t result; +private: + DNSServer* server_; +}; + +// Test query timeout, set query timeout is lower than client timeout +// and lookup timeout. TEST_F(RecursiveQueryTest, forwardQueryTimeout) { // Prepare the service (we do not use the common setup, we do not answer setDNSService(); @@ -657,26 +706,20 @@ TEST_F(RecursiveQueryTest, forwardQueryTimeout) { Question question(Name("example.net"), RRClass::IN(), RRType::A()); OutputBufferPtr buffer(new OutputBuffer(0)); MessagePtr answer(new Message(Message::RENDER)); - query.resolve(question, answer, buffer, &server); + Message query_message(Message::RENDER); + isc::resolve::initResponseMessage(question, query_message); + boost::shared_ptr callback(new MockResolverCallback(&server)); + query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback); // Run the test io_service_->run(); - - // Read up to 3 packets. Use some ad hoc timeout to prevent an infinite - // block (see also recvUDP()). - int recv_options = setSocketTimeout(sock_, 10, 0); - int num = 0; - bool read_success = tryRead(sock_, recv_options, 3, &num); - - // The query should 'succeed' with an error response - EXPECT_TRUE(done); - EXPECT_EQ(3, num); - EXPECT_TRUE(read_success); + EXPECT_EQ(callback->result, MockResolverCallback::FAILURE); } // If we set client timeout to lower than querytimeout, we should -// get a failure answer, but still see retries -// (no actual answer is given here yet) +// get a failure answer +// (no actual answer is given here yet. TODO the returned error message +// should be tested) TEST_F(RecursiveQueryTest, forwardClientTimeout) { // Prepare the service (we do not use the common setup, we do not answer setDNSService(); @@ -691,36 +734,25 @@ TEST_F(RecursiveQueryTest, forwardClientTimeout) { // Do the answer const uint16_t port = boost::lexical_cast(TEST_CLIENT_PORT); - // Set it up to retry twice before client timeout fires - // Since the lookup timer has not fired, it should retry - // four times RecursiveQuery query(*dns_service_, *nsas_, cache_, singleAddress(TEST_IPV4_ADDR, port), singleAddress(TEST_IPV4_ADDR, port), - 200, 480, 4000, 4); - Question question(Name("example.net"), RRClass::IN(), RRType::A()); + 1000, 10, 4000, 4); + Question q(Name("example.net"), RRClass::IN(), RRType::A()); OutputBufferPtr buffer(new OutputBuffer(0)); - query.resolve(question, answer, buffer, &server); + Message query_message(Message::RENDER); + isc::resolve::initResponseMessage(q, query_message); + boost::shared_ptr callback(new MockResolverCallback(&server)); + query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback); // Run the test io_service_->run(); - - // we know it'll fail, so make it a shorter timeout - int recv_options = setSocketTimeout(sock_, 1, 0); - - // Try to read 4 times - int num = 0; - bool read_success = tryRead(sock_, recv_options, 4, &num); - - // The query should fail - EXPECT_TRUE(done1); - EXPECT_EQ(3, num); - EXPECT_FALSE(read_success); + EXPECT_EQ(callback->result, MockResolverCallback::FAILURE); } -// If we set lookup timeout to lower than querytimeout*retries, we should -// fail before the full amount of retries +// If we set lookup timeout to lower than querytimeout, the lookup +// will fail. TEST_F(RecursiveQueryTest, forwardLookupTimeout) { // Prepare the service (we do not use the common setup, we do not answer setDNSService(); @@ -736,30 +768,22 @@ TEST_F(RecursiveQueryTest, forwardLookupTimeout) { // Do the answer const uint16_t port = boost::lexical_cast(TEST_CLIENT_PORT); - // Set up the test so that it will retry 5 times, but the lookup - // timeout will fire after only 3 normal timeouts RecursiveQuery query(*dns_service_, *nsas_, cache_, singleAddress(TEST_IPV4_ADDR, port), singleAddress(TEST_IPV4_ADDR, port), - 200, 4000, 480, 5); + 1000, 4000, 10, 5); Question question(Name("example.net"), RRClass::IN(), RRType::A()); OutputBufferPtr buffer(new OutputBuffer(0)); - query.resolve(question, answer, buffer, &server); + Message query_message(Message::RENDER); + isc::resolve::initResponseMessage(question, query_message); + + boost::shared_ptr callback(new MockResolverCallback(&server)); + query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback); // Run the test io_service_->run(); - - int recv_options = setSocketTimeout(sock_, 1, 0); - - // Try to read 5 times, should stop after 3 reads - int num = 0; - bool read_success = tryRead(sock_, recv_options, 5, &num); - - // The query should fail and respond with an error - EXPECT_TRUE(done); - EXPECT_EQ(3, num); - EXPECT_FALSE(read_success); + EXPECT_EQ(callback->result, MockResolverCallback::FAILURE); } // Set everything very low and see if this doesn't cause weird @@ -779,8 +803,6 @@ TEST_F(RecursiveQueryTest, lowtimeouts) { // Do the answer const uint16_t port = boost::lexical_cast(TEST_CLIENT_PORT); - // Set up the test so that it will retry 5 times, but the lookup - // timeout will fire after only 3 normal timeouts RecursiveQuery query(*dns_service_, *nsas_, cache_, singleAddress(TEST_IPV4_ADDR, port), @@ -788,21 +810,15 @@ TEST_F(RecursiveQueryTest, lowtimeouts) { 1, 1, 1, 1); Question question(Name("example.net"), RRClass::IN(), RRType::A()); OutputBufferPtr buffer(new OutputBuffer(0)); - query.resolve(question, answer, buffer, &server); + Message query_message(Message::RENDER); + isc::resolve::initResponseMessage(question, query_message); + + boost::shared_ptr callback(new MockResolverCallback(&server)); + query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback); // Run the test io_service_->run(); - - int recv_options = setSocketTimeout(sock_, 1, 0); - - // Try to read 5 times, should stop after 3 reads - int num = 0; - bool read_success = tryRead(sock_, recv_options, 5, &num); - - // The query should fail and respond with an error - EXPECT_TRUE(done); - EXPECT_EQ(1, num); - EXPECT_FALSE(read_success); + EXPECT_EQ(callback->result, MockResolverCallback::FAILURE); } // as mentioned above, we need a more better framework for this, @@ -857,7 +873,88 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) { EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER)); } +// Test that we don't start at root when we have a lower NS cached. +TEST_F(RecursiveQueryTest, CachedNS) { + setDNSService(true, true); + + // Check we have a reasonable fallback - if there's nothing of interest + // in the cache, start at root. + EXPECT_EQ(".", deepestDelegation(Name("www.somewhere.deep.example.org"), + RRClass::IN(), cache_)); + + // Prefill the cache. There's a zone with a NS and IP address for one + // of them (to see that one is enough) and another deeper one, with NS, + // but without IP. + RRsetPtr nsUpper(new RRset(Name("example.org"), RRClass::IN(), + RRType::NS(), RRTTL(300))); + nsUpper->addRdata(rdata::generic::NS(Name("ns.example.org"))); + nsUpper->addRdata(rdata::generic::NS(Name("ns2.example.org"))); + + RRsetPtr nsLower(new RRset(Name("somewhere.deep.example.org"), + RRClass::IN(), RRType::NS(), RRTTL(300))); + nsLower->addRdata(rdata::generic::NS(Name("ns.somewhere.deep.example.org")) + ); + + RRsetPtr nsIp(new RRset(Name("ns2.example.org"), RRClass::IN(), + RRType::A(), RRTTL(300))); + nsIp->addRdata(rdata::in::A("192.0.2.1")); + + // Make sure the test runs in the correct environment (we don't test + // the cache, but we need it to unswer this way for the test, so we + // just make sure) + ASSERT_TRUE(cache_.update(nsUpper)); + ASSERT_TRUE(cache_.update(nsLower)); + ASSERT_TRUE(cache_.update(nsIp)); + RRsetPtr deepest(cache_.lookupDeepestNS(Name( + "www.somewhere.deep.example.org"), RRClass::IN())); + ASSERT_NE(RRsetPtr(), deepest); + ASSERT_EQ(nsLower->getName(), deepest->getName()); + + // Direct check of the function that chooses the delegation point + // It should not use nsLower, because we don't have IP address for + // that one. But it can choose nsUpper. + EXPECT_EQ("example.org.", + deepestDelegation(Name("www.somewhere.deep.example.org"), + RRClass::IN(), cache_)); + + // Now more complex and indirect test: + // We ask it to resolve the name for us. It will pick up a delegation + // point and ask NSAS for it. NSAS will in turn ask resolver for NS record + // of the delegation point. We then pick it up from the fake resolver + // and check it is the correct one. This checks the delegation point + // travels safely trough the whole path there (it would be enough to check + // it up to NSAS, but replacing NSAS is more complicated, so we just + // include in the test as well for simplicity). + + // Prepare the recursive query + vector > roots; + roots.push_back(pair("192.0.2.2", 53)); + + RecursiveQuery rq(*dns_service_, *nsas_, cache_, + vector >(), roots); + // Ask a question at the bottom. It should not use the lower NS, because + // it would lead to a loop in NS. But it can use the nsUpper one, it has + // an IP address and we can avoid asking root. + Question q(Name("www.somewhere.deep.example.org"), RRClass::IN(), + RRType::A()); + OutputBufferPtr buffer(new OutputBuffer(0)); + MessagePtr answer(new Message(Message::RENDER)); + // The server is here so we have something to pass there + MockServer server(*io_service_); + rq.resolve(q, answer, buffer, &server); + // We don't need to run the service in this test. We are interested only + // in the place it starts resolving at + + // Look what is asked by NSAS - it should be our delegation point. + EXPECT_NO_THROW(EXPECT_EQ(nsUpper->getName(), + (*resolver_)[0]->getName()) << + "It starts resolving at the wrong place") << + "It does not ask NSAS anything, how does it know where to send?"; +} + // TODO: add tests that check whether the cache is updated on succesfull // responses, and not updated on failures. + + } diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc index 941644e63d..7b2b3ddc27 100644 --- a/src/lib/server_common/portconfig.cc +++ b/src/lib/server_common/portconfig.cc @@ -96,11 +96,17 @@ installListenAddresses(const AddressList& newAddresses, } catch (const exception& e) { /* - * We couldn't set it. So return it back. If that fails as well, - * we have a problem. + * If one of the addresses isn't set successfully, we will restore + * the old addresses, the behavior is that either all address are + * set successuflly or none of them will be used. whether this + * behavior is user desired, maybe we need revisited it later. And + * if address setting is more smarter, it should check whether some + * part of the new address already in used to avoid interuption the + * service. * - * If that fails, bad luck, but we are useless anyway, so just die - * and let boss start us again. + * If the address setting still failed, we can live with it, since + * user will get error info, command control can be used to set new + * address. So we just catch the exception without propagating outside */ dlog(string("Unable to set new address: ") + e.what(), true); try { @@ -109,9 +115,10 @@ installListenAddresses(const AddressList& newAddresses, catch (const exception& e2) { dlog("Unable to recover from error;", true); dlog(string("Rollback failed with: ") + e2.what(), true); - abort(); } - throw; // Let it fly a little bit further + //Anyway the new configure has problem, we need to notify configure + //manager the new configure doesn't work + throw; } } diff --git a/src/lib/server_common/tests/portconfig_unittest.cc b/src/lib/server_common/tests/portconfig_unittest.cc index 52625657bc..65963eb31c 100644 --- a/src/lib/server_common/tests/portconfig_unittest.cc +++ b/src/lib/server_common/tests/portconfig_unittest.cc @@ -177,7 +177,7 @@ TEST_F(InstallListenAddresses, rollback) { EXPECT_NO_THROW(installListenAddresses(valid_, store_, dnss_)); checkAddresses(valid_, "Before rollback"); // This should not bind them, but should leave the original addresses - EXPECT_THROW(installListenAddresses(invalid_, store_, dnss_), IOError); + EXPECT_THROW(installListenAddresses(invalid_, store_, dnss_), exception); checkAddresses(valid_, "After rollback"); } diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index 77a3e4a1ed..6deec27e72 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am @@ -24,5 +24,7 @@ libutil_la_SOURCES += encode/binary_from_base16.h libutil_la_SOURCES += random/qid_gen.h random/qid_gen.cc libutil_la_SOURCES += random/random_number_generator.h +EXTRA_DIST = python/pycppwrapper_util.h + libutil_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la CLEANFILES = *.gcno *.gcda diff --git a/src/lib/util/encode/base_n.cc b/src/lib/util/encode/base_n.cc index e79f11d11f..406dc77042 100644 --- a/src/lib/util/encode/base_n.cc +++ b/src/lib/util/encode/base_n.cc @@ -138,7 +138,7 @@ private: // DecodeNormalizer is an input iterator intended to be used as a filter // between the encoded baseX stream and binary_from_baseXX. // A DecodeNormalizer object is configured with three string iterators -// (base, base_beinpad, and base_beginpad), specifying the head of the string, +// (base, base_beginpad, and base_end), specifying the head of the string, // the beginning position of baseX padding (when there's padding), and // end of the string, respectively. It internally iterators over the original // stream, and return each character of the encoded string via its dereference diff --git a/src/lib/util/python/mkpywrapper.py.in b/src/lib/util/python/mkpywrapper.py.in new file mode 100755 index 0000000000..4bf7752edd --- /dev/null +++ b/src/lib/util/python/mkpywrapper.py.in @@ -0,0 +1,100 @@ +#!@PYTHON@ + +# Copyright (C) 2011 Internet Systems Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM +# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""This utility program generates a C++ header and implementation files +that can be used as a template of C++ python binding for a C++ class. + +Usage: ./mkpywrapper.py ClassName +(the script should be run on this directory) + +It will generate two files: classname_python.h and classname_python.cc, +many of whose definitions are in the namespace isc::MODULE_NAME::python. +By default MODULE_NAME will be 'dns' (because this tool is originally +intended to be used for the C++ python binding of the DNS library), but +can be changed via the -m command line option. + +The generated files contain code fragments that are commonly used in +C++ python binding implementations. It will define a class named +s_ClassName which is a derived class of PyModule and can meet the +requirement of the CPPPyObjectContainer template class (see +pycppwrapper_util.h). It also defines (and declares in the header file) +"classname_type", which is of PyTypeObject and is intended to be used +to define details of the python bindings for the ClassName class. + +In many cases the header file can be used as a startpoint of the +binding development without modification. But you may want to make +ClassName::cppobj a constant variable (and you should if you can). +Many definitions of classname_python.cc should also be able to be used +just as defined, but some will need to be changed or removed. In +particular, you should at least adjust ClassName_init(). You'll +probably also need to add more definitions to that file to provide +complete features of the C++ class. +""" + +import datetime, string, sys +from optparse import OptionParser + +# Remember the current year to produce the copyright boilerplate +YEAR = datetime.date.today().timetuple()[0] + +def dump_file(out_file, temp_file, class_name, module): + for line in temp_file.readlines(): + line = line.replace("@YEAR@", str(YEAR)) + line = line.replace("@CPPCLASS@_H", class_name.upper() + "_H") + line = line.replace("@CPPCLASS@", class_name) + line = line.replace("@cppclass@", class_name.lower()) + line = line.replace("@MODULE@", module) + out_file.write(line) + +def dump_wrappers(class_name, output, module): + try: + if output == "-": + header_file = sys.stdout + else: + header_file = open(output + "_python.h", "w") + header_template_file = open("wrapper_template.h", "r") + if output == "-": + impl_file = sys.stdout + else: + impl_file = open(output + "_python.cc", "w") + impl_template_file = open("wrapper_template.cc", "r") + except: + sys.stderr.write('Failed to open C++ file(s)\n') + sys.exit(1) + dump_file(header_file, header_template_file, class_name, module) + dump_file(impl_file, impl_template_file, class_name, module) + +usage = '''usage: %prog [options] class_name''' + +if __name__ == "__main__": + parser = OptionParser(usage=usage) + parser.add_option('-o', '--output', action='store', dest='output', + default=None, metavar='FILE', + help='prefix of output file names [default: derived from the class name]') + parser.add_option('-m', '--module', action='store', dest='module', + default='dns', + help='C++ module name of the wrapper (for namespaces) [default: dns]') + (options, args) = parser.parse_args() + + if len(args) == 0: + parser.error('input file is missing') + + class_name = args[0] + if not options.output: + options.output = class_name.lower() + + dump_wrappers(class_name, options.output, options.module) diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h new file mode 100644 index 0000000000..fd55c19039 --- /dev/null +++ b/src/lib/util/python/pycppwrapper_util.h @@ -0,0 +1,308 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __PYCPPWRAPPER_UTIL_H +#define __PYCPPWRAPPER_UTIL_H 1 + +#include + +#include + +/** + * @file pycppwrapper_util.h + * @short Shared definitions for python/C(++) API + * + * This utility defines a set of convenient wrappers for the python C API + * to use it safely from our C++ bindings. The python C API has many pitfalls + * such as not-so-consistent reference count policies. Also, many existing + * examples are careless about error handling. It's easy to find on the net + * example (even of "production use") python extensions like this: + * + * \code + * new_exception = PyErr_NewException("mymodule.Exception", NULL, NULL); + * // new_exception can be NULL, in which case the call to + * // PyModule_AddObject will cause a surprising disruption. + * PyModule_AddObject(mymodule, "Exception", new_exception); \endcode + * + * When using the python C API with C++, we should also be careful about + * exception safety. The underlying C++ code (including standard C++ libraries + * and memory allocation) can throw exceptions, in which case we need to + * make sure any intermediate python objects are cleaned up (we also need to + * catch the C++ exceptions inside the binding and convert them to python + * errors, but that's a different subject). This is not a trivial task + * because the python objects are represented as bare C pointers (so there's + * no destructor) and we need to address the exception safety along with python + * reference counters (so we cannot naively apply standard smart pointers). + * + * This utility tries to help address these issues. + * + * Also, it's intentional that this is a header-only utility. This way the + * C++ loadable module won't depend on another C++ library (which is not + * necessarily wrong, but would increase management cost such as link-time + * troubles only for a small utility feature). + */ + +namespace isc { +namespace util { +namespace python { + +/// This is thrown inside this utility when it finds a NULL pointer is passed +/// when it should not be NULL. +class PyCPPWrapperException : public isc::Exception { +public: + PyCPPWrapperException(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// This helper class is similar to the standard autoptr and manages PyObject +/// using some kind of RAII techniques. It is, however, customized for the +/// python C API. +/// +/// A PyObjectContainer object is constructed with a pointer to PyObject, +/// which is often just created dynamically. The caller will eventually +/// attach the object to a different python object (often a module or class) +/// via specific methods or directly return it to the python interpreter. +/// +/// There are two cases in destructing the object: with or without decreasing +/// a reference to the PyObject. If the object is intended to be an argument +/// to another python C library that increases the reference to the object for +/// itself, we should normally release our own reference; otherwise the +/// reference will leak and the object won't be garbage collected. Also, when +/// an unexpected error happens in the form of C++ exception, we should +/// release the reference to prevent resource leak. +/// +/// In some other cases, we should simply give our reference to the caller. +/// That is the case when the created object itself is a return value of +/// an extended python method written in the C++ binding. Likewise, some +/// python C library functions "steal" the reference. In these cases we +/// should not decrease the reference; otherwise it would cause duplicate free. +/// +/// By default, the destructor of this class releases the reference to the +/// PyObject. If this behavior is desirable, you can extract the original +/// bare pointer to the PyObject by the \c get() method. If you don't want +/// the reference to be decreased, the original bare pointer should be +/// extracted using the \c release() method. +/// +/// There are two convenience methods for commonly used operations: +/// \c installAsClassVariable() to add the PyObject as a class variable +/// and \c installToModule to add the PyObject to a specified python module. +/// These methods (at least to some extent) take care of the reference to +/// the object (either release or keep) depending on the usage context so +/// that the user don't have to worry about it. +/// +/// On construction, this class expects the pointer can be NULL. +/// If it happens it immediately throws a \c PyCPPWrapperException exception. +/// This behavior is to convert failures in the python C API (such as +/// PyObject_New() returning NULL) to C++ exception so that we can unify +/// error handling in the style of C++ exceptions. +/// +/// Examples 1: To create a tuple of two python objects, do this: +/// +/// \code +/// try { +/// PyObjectContainer container0(Py_BuildValue("I", 0)); +/// PyObjectContainer container1(Py_BuildValue("s", cppobj.toText().c_str())); +/// return (Py_BuildValue("OO", container0.get(), container1.get())); +/// } catch { ... set python exception, etc ... } \endcode +/// +/// Commonly deployed buggy implementation to achieve this would be like this: +/// \code +/// return (Py_BuildValue("OO", Py_BuildValue("I", 0), +/// Py_BuildValue("s", cppobj.toText().c_str()))); +/// \endcode +/// One clear bug of this code is that references to the element objects of +/// the tuple will leak. +/// (Assuming \c cppobj.toText() can throw) this code is also not exception +/// safe; if \c cppobj.toText() throws the reference to the first object +/// will leak, even if the code tried to do the necessary cleanup in the +/// successful case. +/// Further, this code naively passes the result of the first two calls to +/// \c Py_BuildValue() to the third one even if they can be NULL. +/// In this specific case, it happens to be okay because \c Py_BuildValue() +/// accepts NULL and treats it as an indication of error. But not all +/// python C library works that way (remember, the API is so inconsistent) +/// and we need to refer to the API manual every time we have to worry about +/// passing a NULL object to a library function. We'd certainly like to +/// avoid such development overhead. The code using \c PyObjectContainer +/// addresses all these problems. +/// +/// Examples 2: Install a (constant) variable to a class. +/// +/// \code +/// try { +/// // installClassVariable is a wrapper of +/// // PyObjectContainer::installAsClassVariable. See below. +/// installClassVariable(myclass_type, "SOME_CONSTANT", +/// Py_BuildValue("I", 0)); +/// } catch { ... } +/// \endcode +/// +/// Examples 3: Install a custom exception to a module. +/// +/// \code +/// PyObject* new_exception; // publicly visible +/// ... +/// try { +/// new_exception = PyErr_NewException("mymodule.NewException", +/// NULL, NULL); +/// PyObjectContainer(new_exception).installToModule(mymodule, +/// "NewException"); +/// } catch { ... } +/// \endcode +/// +/// Note that \c installToModule() keeps the reference to \c new_exception +/// by default. This is a common practice when we introduce a custom +/// exception in a python biding written in C/C++. See the code comment +/// of the method for more details. +struct PyObjectContainer { + PyObjectContainer(PyObject* obj) : obj_(obj) { + if (obj_ == NULL) { + isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, " + "probably due to short memory"); + } + } + virtual ~PyObjectContainer() { + if (obj_ != NULL) { + Py_DECREF(obj_); + } + } + PyObject* get() { + return (obj_); + } + PyObject* release() { + PyObject* ret = obj_; + obj_ = NULL; + return (ret); + } + + // Install the enclosed PyObject to the specified python class 'pyclass' + // as a variable named 'name'. + void installAsClassVariable(PyTypeObject& pyclass, const char* name) { + if (PyDict_SetItemString(pyclass.tp_dict, name, obj_) < 0) { + isc_throw(PyCPPWrapperException, "Failed to set a class variable, " + "probably due to short memory"); + } + // Ownership successfully transferred to the class object. We'll let + // it be released in the destructor. + } + + // Install the enclosed PyObject to the specified module 'mod' as an + // object named 'name'. + // By default, this method explicitly keeps the reference to the object + // even after the module "steals" it. To cancel this behavior and give + // the reference to the module completely, the third parameter 'keep_ref' + // should be set to false. + void installToModule(PyObject* mod, const char* name, + bool keep_ref = true) + { + if (PyModule_AddObject(mod, name, obj_) < 0) { + isc_throw(PyCPPWrapperException, "Failed to add an object to " + "module, probably due to short memory"); + } + // PyModule_AddObject has "stolen" the reference, so unless we + // have to retain it ourselves we don't (shouldn't) decrease it. + // However, we actually often need to keep our own reference because + // objects added to a module are often referenced via non local + // C/C++ variables in various places of the C/C++ code. In order + // for the code to run safely even if some buggy/evil python program + // performs 'del mod.obj', we need the extra reference. See, e.g.: + // http://docs.python.org/py3k/c-api/init.html#Py_Initialize + // http://mail.python.org/pipermail/python-dev/2005-June/054238.html + if (keep_ref) { + Py_INCREF(obj_); + } + obj_ = NULL; + } + +protected: + PyObject* obj_; +}; + +/// This templated class is a derived class of \c PyObjectContainer and +/// manages C++-class based python objects. +/// +/// The template parameter \c PYSTRUCT must be a derived class (structure) of +/// \c PyObject that has a member variable named \c cppobj, which must be a +/// a pointer to \c CPPCLASS (the second template parameter). +/// +/// For example, to define a custom python class based on a C++ class, MyClass, +/// we'd define a class (struct) named \c s_MyClass like this: +/// \code +/// class s_MyClass : public PyObject { +/// public: +/// s_MyClass() : cppobj(NULL) {} +/// MyClass* cppobj; +/// }; +/// \endcode +/// +/// And, to build and return a python version of MyClass object, write the +/// following C++ code: +/// \code +/// typedef CPPPyObjectContainer MyContainer; +/// try { +/// // below, myclass_type is of \c PyTypeObject that defines +/// // a python class (type) for MyClass +/// MyContainer container(PyObject_New(s_MyClass, myclass_type)); +/// container.set(new MyClass()); +/// return (container.release()); // give the reference to the caller +/// } catch { ... } +/// \endcode +/// +/// This code prevents bugs like NULL pointer dereference when \c PyObject_New +/// fails or resource leaks when new'ing \c MyClass results in an exception. +/// Note that we use \c release() (derived from the base class) instead of +/// \c get(); in this case we should simply pass the reference generated in +/// \c PyObject_New() to the caller. +template +struct CPPPyObjectContainer : public PyObjectContainer { + CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {} + + // This method associates a C++ object with the corresponding python + // object enclosed in this class. + void set(CPPCLASS* value) { + if (value == NULL) { + isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, " + "probably due to short memory"); + } + static_cast(obj_)->cppobj = value; + } + + // This is a convenience short cut to associate a C++ object with the + // python object and install it to the specified python class \c pyclass + // as a variable named \c name. + void installAsClassVariable(PyTypeObject& pyclass, const char* name, + CPPCLASS* value) + { + set(value); + PyObjectContainer::installAsClassVariable(pyclass, name); + } +}; + +/// A shortcut function to install a python class variable. +/// +/// It installs a python object \c obj to a specified class \c pyclass +/// as a variable named \c name. +inline void +installClassVariable(PyTypeObject& pyclass, const char* name, PyObject* obj) { + PyObjectContainer(obj).installAsClassVariable(pyclass, name); +} + +} // namespace python +} // namespace util +} // namespace isc +#endif // __PYCPPWRAPPER_UTIL_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc new file mode 100644 index 0000000000..2fe7567a21 --- /dev/null +++ b/src/lib/util/python/wrapper_template.cc @@ -0,0 +1,293 @@ +// Copyright (C) @YEAR@ Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include + +#include "@cppclass@_python.h" + +using namespace std; +using namespace isc::util::python; +using namespace isc::@MODULE@; +using namespace isc::@MODULE@::python; + +// +// Definition of the classes +// + +// For each class, we need a struct, a helper functions (init, destroy, +// and static wrappers around the methods we export), a list of methods, +// and a type description + +// +// @CPPCLASS@ +// + +// Trivial constructor. +s_@CPPCLASS@::s_@CPPCLASS@() : cppobj(NULL) { +} + +namespace { +// Shortcut type which would be convenient for adding class variables safely. +typedef CPPPyObjectContainer @CPPCLASS@Container; + +// +// We declare the functions here, the definitions are below +// the type definition of the object, since both can use the other +// + +// General creation and destruction +int @CPPCLASS@_init(s_@CPPCLASS@* self, PyObject* args); +void @CPPCLASS@_destroy(s_@CPPCLASS@* self); + +// These are the functions we export +// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS. +// +PyObject* @CPPCLASS@_toText(const s_@CPPCLASS@* const self); +PyObject* @CPPCLASS@_str(PyObject* self); +PyObject* @CPPCLASS@_richcmp(const s_@CPPCLASS@* const self, + const s_@CPPCLASS@* const other, int op); + +// This is quite specific pydnspp. For other wrappers this should probably +// be removed. +PyObject* @CPPCLASS@_toWire(const s_@CPPCLASS@* self, PyObject* args); + +// These are the functions we export +// For a minimal support, we don't need them. + +// This list contains the actual set of functions we have in +// python. Each entry has +// 1. Python method name +// 2. Our static function here +// 3. Argument type +// 4. Documentation +PyMethodDef @CPPCLASS@_methods[] = { + { "to_text", reinterpret_cast(@CPPCLASS@_toText), METH_NOARGS, + "Returns the text representation" }, + // This is quite specific pydnspp. For other wrappers this should probably + // be removed: + { "to_wire", reinterpret_cast(@CPPCLASS@_toWire), METH_VARARGS, + "Converts the @CPPCLASS@ object to wire format.\n" + "The argument can be either a MessageRenderer or an object that " + "implements the sequence interface. If the object is mutable " + "(for instance a bytearray()), the wire data is added in-place.\n" + "If it is not (for instance a bytes() object), a new object is " + "returned" }, + { NULL, NULL, 0, NULL } +}; + +// This is a template of typical code logic of python class initialization +// with C++ backend. You'll need to adjust it according to details of the +// actual C++ class. +int +@CPPCLASS@_init(s_@CPPCLASS@* self, PyObject* args) { + try { + if (PyArg_ParseTuple(args, "REPLACE ME")) { + // YOU'LL NEED SOME VALIDATION, PREPARATION, ETC, HERE. + self->cppobj = new @CPPCLASS@(/*NECESSARY PARAMS*/); + return (0); + } + } catch (const exception& ex) { + const string ex_what = "Failed to construct @CPPCLASS@ object: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (-1); + } catch (...) { + PyErr_SetString(po_IscException, + "Unexpected exception in constructing @CPPCLASS@"); + return (-1); + } + + PyErr_SetString(PyExc_TypeError, + "Invalid arguments to @CPPCLASS@ constructor"); + + return (-1); +} + +// This is a template of typical code logic of python object destructor. +// In many cases you can use it without modification, but check that carefully. +void +@CPPCLASS@_destroy(s_@CPPCLASS@* const self) { + delete self->cppobj; + self->cppobj = NULL; + Py_TYPE(self)->tp_free(self); +} + +// This should be able to be used without modification as long as the +// underlying C++ class has toText(). +PyObject* +@CPPCLASS@_toText(const s_@CPPCLASS@* const self) { + try { + // toText() could throw, so we need to catch any exceptions below. + return (Py_BuildValue("s", self->cppobj->toText().c_str())); + } catch (const exception& ex) { + const string ex_what = + "Failed to convert @CPPCLASS@ object to text: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + } catch (...) { + PyErr_SetString(PyExc_SystemError, "Unexpected failure in " + "converting @CPPCLASS@ object to text"); + } + return (NULL); +} + +PyObject* +@CPPCLASS@_str(PyObject* self) { + // Simply call the to_text method we already defined + return (PyObject_CallMethod(self, const_cast("to_text"), + const_cast(""))); +} + +PyObject* +@CPPCLASS@_richcmp(const s_@CPPCLASS@* const self, + const s_@CPPCLASS@* const other, + const int op) +{ + bool c = false; + + // Check for null and if the types match. If different type, + // simply return False + if (other == NULL || (self->ob_type != other->ob_type)) { + Py_RETURN_FALSE; + } + + // Only equals and not equals here, unorderable type + switch (op) { + case Py_LT: + PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@"); + return (NULL); + case Py_LE: + PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@"); + return (NULL); + case Py_EQ: + c = (*self->cppobj == *other->cppobj); + break; + case Py_NE: + c = (*self->cppobj != *other->cppobj); + break; + case Py_GT: + PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@"); + return (NULL); + case Py_GE: + PyErr_SetString(PyExc_TypeError, "Unorderable type; @CPPCLASS@"); + return (NULL); + } + if (c) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} +} // end of unnamed namespace + +namespace isc { +namespace @MODULE@ { +namespace python { +// This defines the complete type for reflection in python and +// parsing of PyObject* to s_@CPPCLASS@ +// Most of the functions are not actually implemented and NULL here. +PyTypeObject @cppclass@_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "libdns_python.@CPPCLASS@", + sizeof(s_@CPPCLASS@), // tp_basicsize + 0, // tp_itemsize + reinterpret_cast(@CPPCLASS@_destroy), // tp_dealloc + NULL, // tp_print + NULL, // tp_getattr + NULL, // tp_setattr + NULL, // tp_reserved + NULL, // tp_repr + NULL, // tp_as_number + NULL, // tp_as_sequence + NULL, // tp_as_mapping + NULL, // tp_hash + NULL, // tp_call + // THIS MAY HAVE TO BE CHANGED TO NULL: + @CPPCLASS@_str, // tp_str + NULL, // tp_getattro + NULL, // tp_setattro + NULL, // tp_as_buffer + Py_TPFLAGS_DEFAULT, // tp_flags + "The @CPPCLASS@ class objects is...(COMPLETE THIS)", + NULL, // tp_traverse + NULL, // tp_clear + // THIS MAY HAVE TO BE CHANGED TO NULL: + reinterpret_cast(@CPPCLASS@_richcmp), // tp_richcompare + 0, // tp_weaklistoffset + NULL, // tp_iter + NULL, // tp_iternext + @CPPCLASS@_methods, // tp_methods + NULL, // tp_members + NULL, // tp_getset + NULL, // tp_base + NULL, // tp_dict + NULL, // tp_descr_get + NULL, // tp_descr_set + 0, // tp_dictoffset + reinterpret_cast(@CPPCLASS@_init), // tp_init + NULL, // tp_alloc + PyType_GenericNew, // tp_new + NULL, // tp_free + NULL, // tp_is_gc + NULL, // tp_bases + NULL, // tp_mro + NULL, // tp_cache + NULL, // tp_subclasses + NULL, // tp_weaklist + NULL, // tp_del + 0 // tp_version_tag +}; + +// Module Initialization, all statics are initialized here +bool +initModulePart_@CPPCLASS@(PyObject* mod) { + // We initialize the static description object with PyType_Ready(), + // then add it to the module. This is not just a check! (leaving + // this out results in segmentation faults) + if (PyType_Ready(&@cppclass@_type) < 0) { + return (false); + } + void* p = &@cppclass@_type; + if (PyModule_AddObject(mod, "@CPPCLASS@", static_cast(p)) < 0) { + return (false); + } + Py_INCREF(&@cppclass@_type); + + // The following template is the typical procedure for installing class + // variables. If the class doesn't have a class variable, remove the + // entire try-catch clauses. + try { + // Constant class variables + installClassVariable(@cppclass@_type, "REPLACE_ME", + Py_BuildValue("REPLACE ME")); + } catch (const exception& ex) { + const string ex_what = + "Unexpected failure in @CPPCLASS@ initialization: " + + string(ex.what()); + PyErr_SetString(po_IscException, ex_what.c_str()); + return (false); + } catch (...) { + PyErr_SetString(PyExc_SystemError, + "Unexpected failure in @CPPCLASS@ initialization"); + return (false); + } + + return (true); +} +} // namespace python +} // namespace @MODULE@ +} // namespace isc diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h new file mode 100644 index 0000000000..1f983e400e --- /dev/null +++ b/src/lib/util/python/wrapper_template.h @@ -0,0 +1,44 @@ +// Copyright (C) @YEAR@ Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __PYTHON_@CPPCLASS@_H +#define __PYTHON_@CPPCLASS@_H 1 + +#include + +namespace isc { +namespace @MODULE@ { +class @CPPCLASS@; + +namespace python { + +// The s_* Class simply covers one instantiation of the object +class s_@CPPCLASS@ : public PyObject { +public: + s_@CPPCLASS@(); + @CPPCLASS@* cppobj; +}; + +extern PyTypeObject @cppclass@_type; + +bool initModulePart_@CPPCLASS@(PyObject* mod); + +} // namespace python +} // namespace @MODULE@ +} // namespace isc +#endif // __PYTHON_@CPPCLASS@_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/util/time_utilities.cc b/src/lib/util/time_utilities.cc index 229800db02..9303ab5f06 100644 --- a/src/lib/util/time_utilities.cc +++ b/src/lib/util/time_utilities.cc @@ -110,12 +110,9 @@ timeToText64(uint64_t value) { // library, it's not even declared in a header file. namespace detail { int64_t (*gettimeFunction)() = NULL; -} -namespace { int64_t -gettimeofdayWrapper() { - using namespace detail; +gettimeWrapper() { if (gettimeFunction != NULL) { return (gettimeFunction()); } @@ -132,7 +129,7 @@ timeToText32(const uint32_t value) { // We first adjust the time to the closest epoch based on the current time. // Note that the following variables must be signed in order to handle // time until year 2038 correctly. - const int64_t start = gettimeofdayWrapper() - 0x7fffffff; + const int64_t start = detail::gettimeWrapper() - 0x7fffffff; int64_t base = 0; int64_t t; while ((t = (base + value)) < start) { diff --git a/src/lib/util/time_utilities.h b/src/lib/util/time_utilities.h index 0558f16580..a53089d577 100644 --- a/src/lib/util/time_utilities.h +++ b/src/lib/util/time_utilities.h @@ -15,6 +15,8 @@ #ifndef __TIME_UTILITIES_H #define __TIME_UTILITIES_H 1 +#include + #include #include @@ -39,6 +41,32 @@ public: isc::Exception(file, line, what) {} }; +namespace detail { +/// Return the current time in seconds +/// +/// This function returns the "current" time in seconds from epoch +/// (00:00:00 January 1, 1970) as a 64-bit signed integer. The return +/// value can represent a point of time before epoch as a negative number. +/// +/// This function is provided to help test time conscious implementations +/// such as DNSSEC and TSIG signatures. It is difficult to test them with +/// an unusual or a specifically chosen "current" via system-provided +/// library functions to get time. This function acts as a straightforward +/// wrapper of such a library function, but provides test code with a hook +/// to return an arbitrary time value: if \c isc::util::detail::gettimeFunction +/// is set to a pointer of function that returns 64-bit signed integer, +/// \c gettimeWrapper() calls that function instead of the system library. +/// +/// This hook variable is specifically intended for testing purposes, so, +/// even if it's visible outside of this library, it's not even declared in a +/// header file. +/// +/// If the implementation doesn't need to be tested with faked current time, +/// it should simply use the system supplied library function instead of +/// this one. +int64_t gettimeWrapper(); +} + /// /// \name DNSSEC time conversion functions. /// diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am index b7026478ff..340cd1fe7c 100644 --- a/src/lib/util/unittests/Makefile.am +++ b/src/lib/util/unittests/Makefile.am @@ -2,6 +2,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CXXFLAGS = $(B10_CXXFLAGS) lib_LTLIBRARIES = libutil_unittests.la -libutil_unittests_la_SOURCES = fork.h fork.cc +libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h +libutil_unittests_la_SOURCES += newhook.h newhook.cc +libutil_unittests_la_SOURCES += testdata.h testdata.cc +libutil_unittests_la_SOURCES += textdata.h CLEANFILES = *.gcno *.gcda diff --git a/src/lib/util/unittests/newhook.cc b/src/lib/util/unittests/newhook.cc new file mode 100644 index 0000000000..9e545a5a76 --- /dev/null +++ b/src/lib/util/unittests/newhook.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include +#include + +#include "newhook.h" + +#ifdef ENABLE_CUSTOM_OPERATOR_NEW +void* +operator new(size_t size) throw(std::bad_alloc) { + if (isc::util::unittests::force_throw_on_new && + size == isc::util::unittests::throw_size_on_new) { + throw std::bad_alloc(); + } + void* p = malloc(size); + if (p == NULL) { + throw std::bad_alloc(); + } + return (p); +} + +void +operator delete(void* p) throw() { + if (p != NULL) { + free(p); + } +} +#endif + +namespace isc { +namespace util { +namespace unittests { +bool force_throw_on_new = false; +size_t throw_size_on_new = 0; +} +} +} diff --git a/src/lib/util/unittests/newhook.h b/src/lib/util/unittests/newhook.h new file mode 100644 index 0000000000..7eb8adec39 --- /dev/null +++ b/src/lib/util/unittests/newhook.h @@ -0,0 +1,82 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __UTIL_UNITTESTS_NEWHOOK_H +#define __UTIL_UNITTESTS_NEWHOOK_H 1 + +/** + * @file newhook.h + * @short Enable the use of special operator new that throws for testing. + * + * This small utility allows a test case to force the global operator new + * to throw for a given size to test a case where memory allocation fails + * (which normally doesn't happen). To enable the feature, everything must + * be built with defining ENABLE_CUSTOM_OPERATOR_NEW beforehand, and set + * \c force_throw_on_new to \c true and \c throw_size_on_new to the size + * of data that should trigger the exception, immediately before starting + * the specific test that needs the exception. + * + * Example: + * \code #include + * ... + * TEST(SomeTest, newException) { + * isc::util::unittests::force_throw_on_new = true; + * isc::util::unittests::throw_size_on_new = sizeof(Foo); + * try { + * // this will do 'new Foo()' internally and should throw + * createFoo(); + * isc::util::unittests::force_throw_on_new = false; + * ASSERT_FALSE(true) << "Expected throw on new"; + * } catch (const std::bad_alloc&) { + * isc::util::unittests::force_throw_on_new = false; + * // do some integrity check, etc, if necessary + * } + * } \endcode + * + * Replacing the global operator new (and delete) is a dangerous technique, + * and triggering an exception solely based on the allocation size is not + * reliable, so this feature is disabled by default two-fold: The + * ENABLE_CUSTOM_OPERATOR_NEW build time variable, and run-time + * \c force_throw_on_new. + */ + +namespace isc { +namespace util { +namespace unittests { +/// Switch to enable the use of special operator new +/// +/// This is set to \c false by default. +extern bool force_throw_on_new; + +/// The allocation size that triggers an exception in the special operator new +/// +/// This is the exact size that causes an exception to be thrown; +/// for example, if it is set to 100, an attempt of allocating 100 bytes +/// will result in an exception, but allocation attempt for 101 bytes won't +/// (unless, of course, memory is really exhausted and allocation really +/// fails). +/// +/// The default value is 0. The value of this variable has no meaning +/// unless the use of the special operator is enabled at build time and +/// via \c force_throw_on_new. +extern size_t throw_size_on_new; +} +} +} + +#endif // __UTIL_UNITTESTS_NEWHOOK_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/util/unittests/resolver.h b/src/lib/util/unittests/resolver.h new file mode 100644 index 0000000000..560a8922ba --- /dev/null +++ b/src/lib/util/unittests/resolver.h @@ -0,0 +1,193 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef UTIL_UNITTEST_RESOLVER_H +#define UTIL_UNITTEST_RESOLVER_H + +/// \file resolver.h +/// \brief Fake resolver + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace util { +namespace unittests { + +/// \brief Put rrset into a message as an answer +inline static isc::dns::MessagePtr +createResponseMessage(isc::dns::RRsetPtr answer_rrset) +{ + isc::dns::MessagePtr response(new isc::dns::Message( + isc::dns::Message::RENDER)); + response->setOpcode(isc::dns::Opcode::QUERY()); + response->setRcode(isc::dns::Rcode::NOERROR()); + response->addRRset(isc::dns::Message::SECTION_ANSWER, answer_rrset); + return response; +} + +/// \brief Mock resolver +/// +/// This class pretends to be a resolver. However, it only stores the +/// requests and can answer them right away by prepared answers. It doesn't +/// do any real work and is intended for testing purposes. +class TestResolver : public isc::resolve::ResolverInterface { + private: + bool checkIndex(size_t index) { + return (requests.size() > index); + } + + typedef std::map + PresetAnswers; + PresetAnswers answers_; + public: + typedef std::pair Request; + /// \brief List of requests the tested class sent trough resolve + std::vector requests; + + /// \brief Destructor + /// + /// This is important. All callbacks in the requests vector must be + /// called to remove them from internal loops. Without this, destroying + /// the NSAS object will leave memory assigned. + ~TestResolver() { + for (size_t i = 0; i < requests.size(); ++i) { + requests[i].second->failure(); + } + } + + /// \brief Testing version of resolve + /// + /// If there's a prepared answer (provided by addPresetAnswer), it + /// answers it right away. Otherwise it just stores the request in + /// the requests member so it can be examined later. + virtual void resolve(const isc::dns::QuestionPtr& q, + const CallbackPtr& c) + { + PresetAnswers::iterator it(answers_.find(*q)); + if (it == answers_.end()) { + requests.push_back(Request(q, c)); + } else { + if (it->second) { + c->success(createResponseMessage(it->second)); + } else { + c->failure(); + } + } + } + + /// \brief Add a preset answer. + /// + /// Add a preset answer. If shared_ptr() is passed (eg. NULL), + /// it will generate failure. If the question is not preset, + /// it goes to requests and you can answer later. + void addPresetAnswer(const isc::dns::Question& question, + isc::dns::RRsetPtr answer) + { + answers_[question] = answer; + } + + /// \brief Thrown if the query at the given index does not exist. + class NoSuchRequest : public std::exception { }; + + /// \brief Thrown if the answer does not match the query + class DifferentRequest : public std::exception { }; + + /// \brief Provides the question of request on given answer + isc::dns::QuestionPtr operator[](size_t index) { + if (index >= requests.size()) { + throw NoSuchRequest(); + } + return (requests[index].first); + } + /// \brief Test it asks for IP addresses + /// Looks if the two provided requests in resolver are A and AAAA. + /// Sorts them so index1 is A. + /// + /// Returns false if there aren't enough elements + bool asksIPs(const isc::dns::Name& name, size_t index1, + size_t index2) + { + size_t max = (index1 < index2) ? index2 : index1; + if (!checkIndex(max)) { + return false; + } + EXPECT_EQ(name, (*this)[index1]->getName()); + EXPECT_EQ(name, (*this)[index2]->getName()); + EXPECT_EQ(isc::dns::RRClass::IN(), (*this)[index1]->getClass()); + EXPECT_EQ(isc::dns::RRClass::IN(), (*this)[index2]->getClass()); + // If they are the other way around, swap + if ((*this)[index1]->getType() == isc::dns::RRType::AAAA() && + (*this)[index2]->getType() == isc::dns::RRType::A()) + { + TestResolver::Request tmp((*this).requests[index1]); + (*this).requests[index1] = + (*this).requests[index2]; + (*this).requests[index2] = tmp; + } + // Check the correct addresses + EXPECT_EQ(isc::dns::RRType::A(), (*this)[index1]->getType()); + EXPECT_EQ(isc::dns::RRType::AAAA(), (*this)[index2]->getType()); + return (true); + } + + /// \brief Answer a request + /// Sends a simple answer to a query. + /// 1) Provide index of a query and the address(es) to pass. + /// 2) Provide index of query and components of address to pass. + void answer(size_t index, isc::dns::RRsetPtr& set) { + if (index >= requests.size()) { + throw NoSuchRequest(); + } + requests[index].second->success(createResponseMessage(set)); + } + + void answer(size_t index, const isc::dns::Name& name, + const isc::dns::RRType& type, + const isc::dns::rdata::Rdata& rdata, size_t TTL = 100) + { + isc::dns::RRsetPtr set(new isc::dns::RRset(name, + isc::dns::RRClass::IN(), + type, + isc::dns::RRTTL(TTL))); + set->addRdata(rdata); + answer(index, set); + } + /// \Answer the query at index by list of nameservers + void provideNS(size_t index, isc::dns::RRsetPtr nameservers) + { + if (index >= requests.size()) { + throw NoSuchRequest(); + } + if (requests[index].first->getName() != nameservers->getName() || + requests[index].first->getType() != isc::dns::RRType::NS()) + { + throw DifferentRequest(); + } + requests[index].second->success(createResponseMessage(nameservers)); + } +}; + +} +} +} + +#endif diff --git a/src/lib/util/unittests/testdata.cc b/src/lib/util/unittests/testdata.cc new file mode 100644 index 0000000000..2148d3117d --- /dev/null +++ b/src/lib/util/unittests/testdata.cc @@ -0,0 +1,61 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include + +#include "testdata.h" + +using namespace std; + +namespace { +vector& +getDataPaths() { + static vector data_path; + return (data_path); +} +} + +namespace isc { +namespace util { +namespace unittests { + +void +addTestDataPath(const string& path) { + getDataPaths().push_back(path); +} + +void +openTestData(const char* const datafile, ifstream& ifs) { + vector::const_iterator it = getDataPaths().begin(); + for (; it != getDataPaths().end(); ++it) { + string data_path = *it; + if (data_path.empty() || *data_path.rbegin() != '/') { + data_path.push_back('/'); + } + ifs.open((data_path + datafile).c_str(), ios_base::in); + if (!ifs.fail()) { + return; + } + } + + throw runtime_error("failed to open data file in data paths: " + + string(datafile)); +} + +} +} +} diff --git a/src/lib/util/unittests/testdata.h b/src/lib/util/unittests/testdata.h new file mode 100644 index 0000000000..03bd83aa35 --- /dev/null +++ b/src/lib/util/unittests/testdata.h @@ -0,0 +1,54 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef __UTIL_UNITTESTS_TESTDATA_H +#define __UTIL_UNITTESTS_TESTDATA_H 1 + +/** + * @file testdata.h + * @short Manipulating test data files. + * + * This utility defines functions that help test case handle test data + * stored in a file. + */ + +namespace isc { +namespace util { +namespace unittests { +/// Add a path (directory) that \c openTestData() will search for test data +/// files. +void addTestDataPath(const std::string& path); + +/// Open a file specified by 'datafile' using the data paths registered via +/// addTestDataPath(). On success, ifs will be ready for reading the data +/// stored in 'datafile'. If the data file cannot be open with any of the +/// registered paths, a runtime_error exception will be thrown. +/// +/// \note Care should be taken if you want to reuse the same single \c ifs +/// for multiple input data. Some standard C++ library implementations retain +/// the failure bit if the first stream reaches the end of the first file, +/// and make the second call to \c ifstream::open() fail. The safest way +/// is to use a different \c ifstream object for a new call to this function; +/// alternatively make sure you explicitly clear the error bit by calling +/// \c ifstream::clear() on \c ifs. +void openTestData(const char* const datafile, std::ifstream& ifs); +} +} +} + +#endif // __UTIL_UNITTESTS_TESTDATA_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/util/unittests/textdata.h b/src/lib/util/unittests/textdata.h new file mode 100644 index 0000000000..3e9b1aaf4e --- /dev/null +++ b/src/lib/util/unittests/textdata.h @@ -0,0 +1,103 @@ +// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include + +#include + +#ifndef __UTIL_UNITTESTS_TEXTDATA_H +#define __UTIL_UNITTESTS_TEXTDATA_H 1 + +/** + * @file textdata.h + * @short Utilities for tests with text data. + * + * This utility provides convenient helper functions for unit tests using + * textual data. + */ + +namespace isc { +namespace util { +namespace unittests { + +/// Line-by-line text comparison. +/// +/// This templated function takes two standard input streams, extracts +/// strings from them, and compares the two sets of strings line by line. +template +void +matchTextData(EXPECTED_STREAM& expected, ACTUAL_STREAM& actual) { + std::string actual_line; + std::string expected_line; + while (std::getline(actual, actual_line), !actual.eof()) { + std::getline(expected, expected_line); + if (expected.eof()) { + FAIL() << "Redundant line in actual output: " << actual_line; + break; + } + if (actual.bad() || actual.fail() || + expected.bad() || expected.fail()) { + throw std::runtime_error("Unexpected error in data streams"); + } + EXPECT_EQ(expected_line, actual_line); + } + while (std::getline(expected, expected_line), !expected.eof()) { + ADD_FAILURE() << "Missing line in actual output: " << expected_line; + } +} + +/// Similar to the fully templated version, but takes string for the second +/// (actual) data. +/// +/// Due to the nature of textual data, it will often be the case that test +/// data is given as a string object. This shortcut version helps such cases +/// so that the test code doesn't have to create a string stream with the +/// string data just for testing. +template +void +matchTextData(EXPECTED_STREAM& expected, const std::string& actual_text) { + std::istringstream iss(actual_text); + matchTextData(expected, iss); +} + +/// Same for the previous version, but the first argument is string. +template +void +matchTextData(const std::string& expected_text, ACTUAL_STREAM& actual) { + std::istringstream iss(expected_text); + matchTextData(iss, actual); +} + +/// Same for the previous two, but takes strings for both expected and +/// actual data. +void +matchTextData(const std::string& expected_text, + const std::string& actual_text) +{ + std::istringstream expected_is(expected_text); + std::istringstream actual_is(actual_text); + matchTextData(expected_is, actual_is); +} + +} +} +} + +#endif // __UTIL_UNITTESTS_TEXTDATA_H + +// Local Variables: +// mode: c++ +// End: diff --git a/tests/tools/badpacket/scan.cc b/tests/tools/badpacket/scan.cc index b0c605cc12..a6e7229e54 100644 --- a/tests/tools/badpacket/scan.cc +++ b/tests/tools/badpacket/scan.cc @@ -21,10 +21,6 @@ #include -// on sunstudio, asio.hpp needs unistd.h for pipe() to be defined -#ifdef HAVE_UNISTD_H -#include -#endif #include #include diff --git a/tests/tools/badpacket/tests/Makefile.am b/tests/tools/badpacket/tests/Makefile.am index 7cde4aa117..e83c3b6d65 100644 --- a/tests/tools/badpacket/tests/Makefile.am +++ b/tests/tools/badpacket/tests/Makefile.am @@ -24,6 +24,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDFLAGS += $(top_builddir)/src/lib/log/liblog.la run_unittests_LDFLAGS += $(top_builddir)/src/lib/exceptions/libexceptions.la +run_unittests_LDFLAGS += $(top_builddir)/src/lib/util/libutil.la run_unittests_LDADD = $(GTEST_LDADD) endif