diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py new file mode 100644 index 000000000..dc2334945 --- /dev/null +++ b/.ycm_extra_conf.py @@ -0,0 +1,187 @@ +# Generated by YCM Generator at 2021-11-18 20:34:43.424020 + +# This file is NOT licensed under the GPLv3, which is the license for the rest +# of YouCompleteMe. +# +# Here's the license text for this file: +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to + +import os +import ycm_core + +flags = [ + '-isystem' + './lib_acl/include', + '-isystem' + './lib_protocol/include', + '-isystem' + './lib_acl_cpp/include', + '-isystem' + './lib_fiber/c/include', + '-isystem' + './lib_fiber/cpp/include', + + '-x', + 'c++', + '-I.', + '-I./lib_acl/include', + '-I./lib_acl/src', + '-I./lib_protocol/include', + '-I./lib_protocol/src', + '-I./lib_acl_cpp/include', + '-I./lib_acl_cpp/src', + '-I./lib_fiber/c/include', + '-I./lib_fiber/c/src', + '-I./lib_fiber/cpp/include', + '-I./lib_fiber/cpp/src', + '-DACL_PREPARE_COMPILE', + '-DACL_WRITEABLE_CHECK', + '-DHAVE_CONFIG_H', + '-DNO_TCMALLOC_SAMPLES', + '-DUSE_BOOST_JMP', + '-DUSE_FAST_RING', + '-D_FILE_OFFSET_BITS=64', + '-D_POSIX_PTHREAD_SEMANTICS', + '-D_REENTRANT', + '-D_USE_FAST_MACRO', + '-D__CLASSIC_C__', + '-W', + '-Wall', + '-Wdeclaration-after-statement', + '-Werror', + '-Wno-long-long', + '-Wpointer-arith', + '-Wshadow', + '-Wuninitialized', + '-std=c++11', +] + + +# Set this to the absolute path to the folder (NOT the file!) containing the +# compile_commands.json file to use that instead of 'flags'. See here for +# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html +# +# You can get CMake to generate this file for you by adding: +# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) +# to your CMakeLists.txt file. +# +# Most projects will NOT need to set this to anything; you can just change the +# 'flags' list of compilation flags. Notice that YCM itself uses that approach. +compilation_database_folder = '' + +if os.path.exists( compilation_database_folder ): + database = ycm_core.CompilationDatabase( compilation_database_folder ) +else: + database = None + +SOURCE_EXTENSIONS = ['.cpp', '.cxx', '.cc', '.c' ] + +def DirectoryOfThisScript(): + return os.path.dirname( os.path.abspath( __file__ ) ) + + +def MakeRelativePathsInFlagsAbsolute( flags, working_directory ): + if not working_directory: + return list( flags ) + new_flags = [] + make_next_absolute = False + path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith( '/' ): + new_flag = os.path.join( working_directory, flag ) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith( path_flag ): + path = flag[ len( path_flag ): ] + new_flag = path_flag + os.path.join( working_directory, path ) + break + + if new_flag: + new_flags.append( new_flag ) + return new_flags + + +def IsHeaderFile( filename ): + extension = os.path.splitext( filename )[ 1 ] + return extension in [ '.H', '.h', '.hxx', '.hpp', '.hh' ] + + +def GetCompilationInfoForFile( filename ): + # The compilation_commands.json file generated by CMake does not have entries + # for header files. So we do our best by asking the db for flags for a + # corresponding source file, if any. If one exists, the flags for that file + # should be good enough. + if IsHeaderFile( filename ): + basename = os.path.splitext( filename )[ 0 ] + for extension in SOURCE_EXTENSIONS: + replacement_file = basename + extension + if os.path.exists( replacement_file ): + compilation_info = database.GetCompilationInfoForFile( + replacement_file ) + if compilation_info.compiler_flags_: + return compilation_info + return None + return database.GetCompilationInfoForFile( filename ) + + +def FlagsForFile( filename, **kwargs ): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = GetCompilationInfoForFile( filename ) + if not compilation_info: + return None + + final_flags = MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_ ) + + else: + relative_to = DirectoryOfThisScript() + final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to ) + + return { + 'flags': final_flags, + 'do_cache': True + } + +def Settings( **kwargs ): + language = kwargs[ 'language' ] + if language == 'cfamily': + return { + 'flags': flags + } + + return {} diff --git a/app/master/daemon/main.cpp b/app/master/daemon/main.cpp index f84e57e09..8fac35f07 100644 --- a/app/master/daemon/main.cpp +++ b/app/master/daemon/main.cpp @@ -185,7 +185,7 @@ int main(int argc, char **argv) } if (setsid() == -1 && getsid(0) != getpid()) - acl_msg_fatal("unable to set session %s", acl_last_serror()); + acl_msg_warn("unable to set session %s", acl_last_serror()); /* * Make some room for plumbing with file descriptors. XXX This breaks diff --git a/app/wizard/tmpl/http/master_fiber.cf b/app/wizard/tmpl/http/master_fiber.cf index 30317fc47..200551b10 100644 --- a/app/wizard/tmpl/http/master_fiber.cf +++ b/app/wizard/tmpl/http/master_fiber.cf @@ -99,6 +99,9 @@ service $ # 当 acl_master 退出时,如果该值置1则该程序不等所有连接处理完毕便立即退出 fiber_quick_abort = 1 +# 是否启用协程共享栈模式 + fiber_share_stack = 0 + # 当 fiber_quick_abort 为 0 且本配置项大于 0 时,该配置项才有效,指定了 # 本进程在所有连接退出前的最大等待时间(秒) fiber_wait_limit = 0 diff --git a/app/wizard/tmpl/master/master_fiber.cf b/app/wizard/tmpl/master/master_fiber.cf index 3df3986aa..712e1c01f 100644 --- a/app/wizard/tmpl/master/master_fiber.cf +++ b/app/wizard/tmpl/master/master_fiber.cf @@ -99,6 +99,9 @@ service $ # 当 acl_master 退出时,如果该值置1则该程序不等所有连接处理完毕便立即退出 fiber_quick_abort = 1 +# 是否启用协程共享栈模式 + fiber_share_stack = 0 + # 当 fiber_quick_abort 为 0 且本配置项大于 0 时,该配置项才有效,指定了 # 本进程在所有连接退出前的最大等待时间(秒) fiber_wait_limit = 0 diff --git a/app/wizard_demo/httpd_proxy/http_servlet.cpp b/app/wizard_demo/httpd_proxy/http_servlet.cpp index dffe68b7f..b93414b6b 100644 --- a/app/wizard_demo/httpd_proxy/http_servlet.cpp +++ b/app/wizard_demo/httpd_proxy/http_servlet.cpp @@ -81,8 +81,7 @@ bool http_servlet::on_hello(request_t& req, response_t& res) return false; } - if (i % 10000 == 0) - { + if (i % 10000 == 0) { sleep(1); printf("i=%d\n", (int) i); } @@ -96,22 +95,27 @@ bool http_servlet::on_hello(request_t& req, response_t& res) bool http_servlet::transfer_get(request_t& req, response_t& res) { - http_transfer fiber_peer(acl::HTTP_METHOD_GET, req, res, port_); - fiber_peer.start(); + http_transfer* fiber_peer = new + http_transfer(acl::HTTP_METHOD_GET, req, res, port_); + fiber_peer->start(); bool keep_alive; - fiber_peer.wait(&keep_alive); + fiber_peer->wait(&keep_alive); + + delete fiber_peer; return keep_alive && req.isKeepAlive(); } bool http_servlet::transfer_post(request_t& req, response_t& res) { - http_transfer fiber_peer(acl::HTTP_METHOD_POST, req, res, port_); - fiber_peer.start(); + http_transfer* fiber_peer = new + http_transfer(acl::HTTP_METHOD_POST, req, res, port_); + fiber_peer->start(); bool keep_alive; - fiber_peer.wait(&keep_alive); + fiber_peer->wait(&keep_alive); + delete fiber_peer; printf("transfer_post finished\r\n"); return keep_alive && req.isKeepAlive(); } @@ -136,17 +140,25 @@ bool http_servlet::doConnect(request_t& req, response_t&) } else { host = phost; } + printf("remote host=%s, current fiber=%p\r\n", host.c_str(), acl_fiber_running()); - acl::socket_stream peer; - if (!peer.open(host, 5, 5, acl::time_unit_s)) { + acl::socket_stream* peer = new acl::socket_stream; + if (!peer->open(host, 5, 5, acl::time_unit_s)) { logger_error("connect %s error %s", host.c_str(), acl::last_serror()); + delete peer; return false; } - printf("connect %s ok, fd=%d\r\n", host.c_str(), peer.sock_handle()); + printf("connect %s ok, fd=%d\r\n", host.c_str(), peer->sock_handle()); - acl::socket_stream& local = req.getSocketStream(); - local.set_rw_timeout(3); +#define USE_REFER + +#ifdef USE_REFER + acl::socket_stream* local = &req.getSocketStream(); +#else + acl::socket_stream* local = new acl::socket_stream; + local->open(req.getSocketStream().sock_handle()); +#endif #if 0 const char* ok = ""; @@ -159,43 +171,68 @@ bool http_servlet::doConnect(request_t& req, response_t&) const char* ok = "HTTP/1.1 200 Connection Established\r\n\r\n"; size_t n = strlen(ok); - if (local.write(ok, n) != (int) n) { + if (local->write(ok, n) != (int) n) { logger_error("write connect response error"); + delete peer; + + local->unbind_sock(); + delete local; return false; } #endif - transfer_tcp(local, peer); + transfer_tcp(*local, *peer); + +#ifndef USE_REFER + int fd = local->unbind_sock(); + if (fd == -1) { + acl::socket_stream& ss = req.getSocketStream(); + logger_warn("The socket=%d has been closed before!", + ss.sock_handle()); + ss.unbind_sock(); + } + delete local; +#endif + delete peer; return false; } -bool http_servlet::transfer_tcp(acl::socket_stream& local, acl::socket_stream& peer) +bool http_servlet::transfer_tcp(acl::socket_stream& local, + acl::socket_stream& peer) { - local.set_rw_timeout(5); - peer.set_rw_timeout(5); + local.set_rw_timeout(20); + peer.set_rw_timeout(20); - tcp_transfer fiber_local(acl_fiber_running(), local, peer, false); - tcp_transfer fiber_peer(acl_fiber_running(), peer, local, false); + tcp_transfer* fiber_local = new + tcp_transfer(acl_fiber_running(), local, peer, false); + tcp_transfer* fiber_peer = new + tcp_transfer(acl_fiber_running(), peer, local, false); - fiber_local.set_peer(fiber_peer); - fiber_peer.set_peer(fiber_local); + fiber_local->set_peer(fiber_peer); + fiber_local->set_local(true); - fiber_peer.start(); - fiber_local.start(); + fiber_peer->set_peer(fiber_local); + fiber_peer->set_local(false); + + fiber_peer->start(); + fiber_local->start(); //int fd_local = local.sock_handle(); //int fd_peer = peer.sock_handle(); //printf("wait local fiber, local fd=%d, peer fd=%d\r\n", fd_local, fd_peer); - fiber_local.wait(); + fiber_local->wait(); //printf("local fiber done, local fd=%d, peer fd=%d\r\n", fd_local, fd_peer); //printf("wait peer fiber, local fd=%d, peer fd=%d\r\n", fd_local, fd_peer); - fiber_peer.wait(); + fiber_peer->wait(); //printf("peer fiber done, local fd=%d, peer fd=%d\r\n", fd_local, fd_peer); //printf("transfer_tcp finished, local fd=%d, %d, peer fd=%d, %d\r\n", // fiber_local.get_input().sock_handle(), fd_local, // fiber_local.get_output().sock_handle(), fd_peer); + + delete fiber_peer; + delete fiber_local; return true; } diff --git a/app/wizard_demo/httpd_proxy/http_transfer.cpp b/app/wizard_demo/httpd_proxy/http_transfer.cpp index 01d1e1324..8537306f0 100644 --- a/app/wizard_demo/httpd_proxy/http_transfer.cpp +++ b/app/wizard_demo/httpd_proxy/http_transfer.cpp @@ -8,14 +8,27 @@ http_transfer::http_transfer(acl::http_method_t method, request_t& req, , req_(req) , res_(res) , client_(NULL) -{} +{ + box_ = new acl::fiber_tbox; + + acl::socket_stream& sin = req_.getSocketStream(); + req_in_.open(sin.sock_handle()); + + acl::socket_stream& sout = res_.getSocketStream(); + res_out_.open(sout.sock_handle()); + res_client_ = res_.getClient(); +} http_transfer::~http_transfer(void) { + req_in_.unbind_sock(); + res_out_.unbind_sock(); + delete client_; + delete box_; } void http_transfer::wait(bool* keep_alive) { - bool* res = box_.pop(); + bool* res = box_->pop(); assert(res); *keep_alive = *res; delete res; @@ -36,7 +49,7 @@ void http_transfer::run(void) { break; } - box_.push(res); + box_->push(res); } bool http_transfer::open_peer(request_t& req, acl::socket_stream& conn) @@ -61,13 +74,17 @@ bool http_transfer::open_peer(request_t& req, acl::socket_stream& conn) acl::string addr; addr.format("%s|%d", buf.c_str(), port_); - if (conn.open(addr, 0, 0)) { - logger("connect %s ok", addr.c_str()); - return true; + if (!conn.open(addr, 0, 0)) { + logger_error("connect %s error %s", + addr.c_str(), acl::last_serror()); + return false; } - logger_error("connect %s error %s", addr.c_str(), acl::last_serror()); - return false; + logger("connect %s ok", addr.c_str()); + + bool is_request = true, unzip = false, fixed_stream = true; + client_ = new acl::http_client(&conn, is_request, unzip, fixed_stream); + return true; } bool http_transfer::transfer_request_head(acl::socket_stream& conn) { @@ -85,8 +102,7 @@ bool http_transfer::transfer_request_head(acl::socket_stream& conn) { return false; } - //printf(">>>send head: [%s]\r\n", header.c_str()); - client_ = new acl::http_client(&conn, true); + printf(">>>send head: [%s]\r\n", header.c_str()); return true; } @@ -96,12 +112,11 @@ bool http_transfer::transfer_request_body(acl::socket_stream& conn) { return true; } - acl::istream& in = req_.getInputStream(); long long n = 0; char buf[8192]; while (n < length) { - int ret = in.read(buf, sizeof(buf), false); + int ret = req_in_.read(buf, sizeof(buf), false); if (ret == -1) { logger_error("read request body error"); return false; @@ -156,6 +171,9 @@ bool http_transfer::transfer_response(void) { return false; } + bool keep_alive = false; // xxxx + client_->header_update("Connection", "Close"); + acl::string header; client_->sprint_header(header, NULL); if (header.empty()) { @@ -167,32 +185,47 @@ bool http_transfer::transfer_response(void) { printf("response head:\r\n[%s]\r\n", header.c_str()); - acl::ostream& out = res_.getOutputStream(); - if (out.write(header) == -1) { + //acl::ostream* out = &res_->getOutputStream(); + if (res_out_.write(header) == -1) { logger_error("send response head error"); return false; } + //acl::http_client* out_client = res_->getClient(); + //assert(out_client); + long long length = client_->body_length(); if (length == 0) { - return client_->is_server_keep_alive(); + return client_->is_server_keep_alive() && keep_alive; } + HTTP_HDR_RES* hdr_res = client_->get_respond_head(NULL); + assert(hdr_res); + bool chunked = hdr_res->hdr.chunked ? true : false; + char buf[8192]; + while (true) { int ret = client_->read_body(buf, sizeof(buf)); if (ret <= 0) { break; - } else if (out.write(buf, ret) == -1) { + } else if (chunked) { + if (!res_client_->write_chunk(res_out_, buf, ret)) { + logger_error("send response body error"); + return false; + } + } else if (res_out_.write(buf, ret) == -1) { logger_error("send response body error"); return false; } } - - if (length < 0) { - return false; + if (chunked) { + if (!res_client_->write_chunk_trailer(res_out_)) { + logger_error("write chunked trailer error"); + return false; + } } - return client_->is_server_keep_alive(); + return client_->is_server_keep_alive() && false; } diff --git a/app/wizard_demo/httpd_proxy/http_transfer.h b/app/wizard_demo/httpd_proxy/http_transfer.h index fe34b3acc..635a81142 100644 --- a/app/wizard_demo/httpd_proxy/http_transfer.h +++ b/app/wizard_demo/httpd_proxy/http_transfer.h @@ -14,7 +14,7 @@ protected: void run(void); private: - acl::fiber_tbox box_; + acl::fiber_tbox* box_; int port_; acl::http_method_t method_; @@ -23,6 +23,10 @@ private: acl::socket_stream conn_; acl::http_client* client_; + acl::socket_stream req_in_; + acl::socket_stream res_out_; + acl::http_client* res_client_; + bool open_peer(request_t& req, acl::socket_stream& conn); bool transfer_get(void); diff --git a/app/wizard_demo/httpd_proxy/httpd_proxy_vc2019.vcxproj b/app/wizard_demo/httpd_proxy/httpd_proxy_vc2019.vcxproj index 3dfbffced..9fb1dfa5d 100644 --- a/app/wizard_demo/httpd_proxy/httpd_proxy_vc2019.vcxproj +++ b/app/wizard_demo/httpd_proxy/httpd_proxy_vc2019.vcxproj @@ -155,7 +155,7 @@ Disabled - ..\..\..\lib_acl_cpp\include;..\..\..\lib_acl\include;..\..\..\lib_fiber\c\include;..\..\..\lib_fiber\cpp\include;%(AdditionalIncludeDirectories) + ..\..\..\lib_acl_cpp\include;..\..\..\lib_acl\include;..\..\..\lib_fiber\c\include;..\..\..\lib_fiber\cpp\include;..\..\..\lib_protocol\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) false EnableFastChecks @@ -199,7 +199,7 @@ - ..\..\..\lib_acl_cpp\include;..\..\..\lib_acl\include;..\..\..\lib_fiber\c\include;..\..\..\lib_fiber\cpp\include;%(AdditionalIncludeDirectories) + ..\..\..\lib_acl_cpp\include;..\..\..\lib_acl\include;..\..\..\lib_fiber\c\include;..\..\..\lib_fiber\cpp\include;..\..\..\lib_protocol\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDLL Use diff --git a/app/wizard_demo/httpd_proxy/master_service.cpp b/app/wizard_demo/httpd_proxy/master_service.cpp index 46f679141..22cdafc56 100644 --- a/app/wizard_demo/httpd_proxy/master_service.cpp +++ b/app/wizard_demo/httpd_proxy/master_service.cpp @@ -47,15 +47,18 @@ void master_service::on_accept(acl::socket_stream& conn) conn.set_rw_timeout(120); - acl::memcache_session session("127.0.0.1:11211"); - http_servlet servlet(&conn, &session); + acl::memcache_session* session = new acl::memcache_session("127.0.0.1:11211"); + http_servlet* servlet = new http_servlet(&conn, session); // charset: big5, gb2312, gb18030, gbk, utf-8 - servlet.setLocalCharset("utf-8"); + servlet->setLocalCharset("utf-8"); + servlet->setParseBody(false); - while(servlet.doRun()) {} + while(servlet->doRun()) {} logger("disconnect from %s, fd %d", conn.get_peer(), conn.sock_handle()); + delete session; + delete servlet; } void master_service::proc_pre_jail(void) @@ -70,7 +73,8 @@ void master_service::proc_on_listen(acl::server_socket& ss) void master_service::proc_on_init(void) { - logger(">>>proc_on_init<<<"); + logger(">>>proc_on_init: shared stack size=%zd<<<", + acl::fiber::get_shared_stack_size()); } void master_service::proc_on_exit(void) diff --git a/app/wizard_demo/httpd_proxy/stdafx.h b/app/wizard_demo/httpd_proxy/stdafx.h index cfb5b4e11..a127ef2d8 100644 --- a/app/wizard_demo/httpd_proxy/stdafx.h +++ b/app/wizard_demo/httpd_proxy/stdafx.h @@ -11,6 +11,7 @@ // TODO: ڴ˴óҪĸͷļ #include "lib_acl.h" +#include "lib_protocol.h" #include "acl_cpp/lib_acl.hpp" #include "fiber/lib_fiber.h" #include "fiber/lib_fiber.hpp" diff --git a/app/wizard_demo/httpd_proxy/tcp_transfer.cpp b/app/wizard_demo/httpd_proxy/tcp_transfer.cpp index 7acc0490e..97236b6bd 100644 --- a/app/wizard_demo/httpd_proxy/tcp_transfer.cpp +++ b/app/wizard_demo/httpd_proxy/tcp_transfer.cpp @@ -9,6 +9,7 @@ tcp_transfer::tcp_transfer(ACL_FIBER* parent, acl::socket_stream& in, , in_(in) , out_(out) , peer_(NULL) +, is_local_(false) { } @@ -16,9 +17,14 @@ tcp_transfer::~tcp_transfer(void) { } -void tcp_transfer::set_peer(tcp_transfer& peer) +void tcp_transfer::set_peer(tcp_transfer* peer) { - peer_ = &peer; + peer_ = peer; +} + +void tcp_transfer::set_local(bool yes) +{ + is_local_ = yes; } void tcp_transfer::unset_peer(void) @@ -45,7 +51,9 @@ void tcp_transfer::wait(void) void tcp_transfer::run(void) { me_ = acl_fiber_running(); + char buf[8192]; + while (true) { int fd = in_.sock_handle(); int ret = in_.read(buf, sizeof(buf) - 1, false); @@ -57,9 +65,24 @@ void tcp_transfer::run(void) break; } + buf[ret] = 0; + +#if 0 + printf("send from %s data, in=%d, out=%d\r\n", + is_local_ ? "local" : "remote", + in_->sock_handle(), out_->sock_handle()); +#endif + if (out_.write(buf, ret) == -1) { + printf(">>>write error\n"); break; } + +#if 0 + printf("send to %s data, in=%d, out=%d ok\r\n", + is_local_ ? "local" : "remote", + in_->sock_handle(), out_->sock_handle()); +#endif } if (peer_) { diff --git a/app/wizard_demo/httpd_proxy/tcp_transfer.h b/app/wizard_demo/httpd_proxy/tcp_transfer.h index aad85ce12..a27fd0d1d 100644 --- a/app/wizard_demo/httpd_proxy/tcp_transfer.h +++ b/app/wizard_demo/httpd_proxy/tcp_transfer.h @@ -3,23 +3,16 @@ class tcp_transfer : public acl::fiber { public: - tcp_transfer(ACL_FIBER* parent, acl::socket_stream& in, acl::socket_stream& out, bool running); + tcp_transfer(ACL_FIBER* parent, acl::socket_stream& in, + acl::socket_stream& out, bool running); ~tcp_transfer(void); - void set_peer(tcp_transfer& peer); + void set_peer(tcp_transfer* peer); void unset_peer(void); void close(void); void wait(void); - acl::socket_stream& get_input(void) const - { - return in_; - } - - acl::socket_stream& get_output(void) const - { - return out_; - } + void set_local(bool yes); ACL_FIBER* peer_fiber(void) const { return me_; @@ -36,5 +29,6 @@ private: acl::socket_stream& in_; acl::socket_stream& out_; tcp_transfer* peer_; + bool is_local_; }; diff --git a/lib_acl/samples/dgate/dgate.cf b/lib_acl/samples/dgate/dgate.cf index 685d8a3a4..83807017e 100644 --- a/lib_acl/samples/dgate/dgate.cf +++ b/lib_acl/samples/dgate/dgate.cf @@ -28,7 +28,7 @@ service server { # DNS IP # dns_neighbor_ip = 211.157.131.7 # dns_neighbor_ip = 192.168.198.47 - dns_neighbor_ip = 8.8.8.8 + dns_neighbor_ip = 114.114.114.114 # DNS PORT dns_neighbor_port = 53 } diff --git a/lib_acl/samples/dgate/service_udp.cpp b/lib_acl/samples/dgate/service_udp.cpp index c758bf9d7..5c2f92553 100644 --- a/lib_acl/samples/dgate/service_udp.cpp +++ b/lib_acl/samples/dgate/service_udp.cpp @@ -149,7 +149,8 @@ static ACL_VSTREAM *stream_udp_bind(struct sockaddr_in addr) stream = stream_udp_open(); fd = ACL_VSTREAM_SOCK(stream); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) - acl_msg_fatal("%s(%d): can't bind", myname, __LINE__); + acl_msg_fatal("%s(%d): bind, error: %s", myname, + __LINE__, acl_last_serror()); return stream; } @@ -456,7 +457,7 @@ void service_udp_init(SERVICE *service, const char *local_ip, ACL_SAFE_STRNCPY(ctx->local_ip, local_ip, sizeof(ctx->local_ip)); ctx->local_port = local_port; ctx->local_addr.sin_addr.s_addr = inet_addr(local_ip); - ctx->local_addr.sin_port = htons(local_port); + ctx->local_addr.sin_port = htons(local_port + 1); ctx->local_addr.sin_family = AF_INET; ACL_SAFE_STRNCPY(ctx->remote_ip, remote_ip, sizeof(ctx->remote_ip)); @@ -465,6 +466,8 @@ void service_udp_init(SERVICE *service, const char *local_ip, ctx->remote_addr.sin_port = htons(remote_port); ctx->remote_addr.sin_family = AF_INET; + printf("begin bind %s:%d\r\n", local_ip, local_port); + // տͻ stream = stream_udp_bind(ctx->local_addr); acl_vstream_ctl(stream, diff --git a/lib_acl/src/stdlib/iostuff/acl_read_wait.c b/lib_acl/src/stdlib/iostuff/acl_read_wait.c index cb25d944c..a034e1738 100644 --- a/lib_acl/src/stdlib/iostuff/acl_read_wait.c +++ b/lib_acl/src/stdlib/iostuff/acl_read_wait.c @@ -539,6 +539,8 @@ int acl_read_wait(ACL_SOCKET fd, int timeout) return acl_read_wait_ms(fd, timeout * 1000); } +/* #define USE_EPOLL */ + int acl_read_wait_ms(ACL_SOCKET fd, int timeout) { #if defined(ACL_LINUX) && !defined(MINGW) && defined(USE_EPOLL) diff --git a/lib_acl_cpp/include/acl_cpp/http/HttpServlet.hpp b/lib_acl_cpp/include/acl_cpp/http/HttpServlet.hpp index e2efa097e..9c8444517 100644 --- a/lib_acl_cpp/include/acl_cpp/http/HttpServlet.hpp +++ b/lib_acl_cpp/include/acl_cpp/http/HttpServlet.hpp @@ -69,14 +69,13 @@ public: HttpServlet& setRwTimeout(int rw_timeout); /** - * POST ÷ǷҪݣĬΪú - * doRun ֮ǰòЧΪ MIME ʽʹ - * ñ˽ݣҲн - * @param on {bool} ǷҪ + * POST ÷ǷҪ Form ݣĬΪ + * ú doRun ֮ǰòЧΪ MIME ʽ + * ʹñ˽ݣҲн + * @param yes {bool} ǷҪ * @return {HttpServlet&} - * xxxx: ÷Ѿ */ - HttpServlet& setParseBody(bool on); + HttpServlet& setParseBody(bool yes); /** * POST ÷ý󳤶ȣ壬ú @@ -207,6 +206,7 @@ protected: protected: HttpServletRequest* req_; HttpServletResponse* res_; + bool parse_body_; private: session* session_; diff --git a/lib_acl_cpp/include/acl_cpp/http/HttpServletRequest.hpp b/lib_acl_cpp/include/acl_cpp/http/HttpServletRequest.hpp index ee978bf19..06466c6a3 100644 --- a/lib_acl_cpp/include/acl_cpp/http/HttpServletRequest.hpp +++ b/lib_acl_cpp/include/acl_cpp/http/HttpServletRequest.hpp @@ -47,6 +47,14 @@ public: int body_limit = 102400); ~HttpServletRequest(void); + /** + * POST ÷ǷҪ Form ݣĬΪ + * ú doRun ֮ǰòЧΪ MIME ʽ + * ʹñ˽ݣҲн + * @param yes {bool} ǷҪ + */ + void setParseBody(bool yes); + /** * HTTP ͻ󷽷GET, POST, PUT, CONNECT, PURGE * @param method_s {string*} ǿʱ洢ַʽ󷽷 @@ -413,6 +421,7 @@ private: int rw_timeout_; std::vector params_; http_request_t request_type_; + bool parse_body_; http_mime* mime_; string* body_; json* json_; diff --git a/lib_acl_cpp/include/acl_cpp/redis/redis_client_pipeline.hpp b/lib_acl_cpp/include/acl_cpp/redis/redis_client_pipeline.hpp index 1d38fc778..ad6aa03f4 100644 --- a/lib_acl_cpp/include/acl_cpp/redis/redis_client_pipeline.hpp +++ b/lib_acl_cpp/include/acl_cpp/redis/redis_client_pipeline.hpp @@ -54,11 +54,18 @@ public: tbox_ = new tbox(false); mbox_ = NULL; } + + size_ = 10; + argc_ = 0; + argv_ = new const char* [size_]; + lens_ = new size_t [size_]; } ~redis_pipeline_message(void) { delete mbox_; delete tbox_; + delete [] argv_; + delete [] lens_; } redis_pipeline_message& set_type(redis_pipeline_type_t type) { @@ -83,9 +90,25 @@ public: } void set_request(size_t argc, const char** argv, size_t* lens) { +#if 0 argc_ = argc; argv_ = argv; lens_ = lens; +#else + if (argc_ > size_) { + delete [] argv_; + delete [] lens_; + size_ = argc; + argv_ = new const char* [size_]; + lens_ = new size_t [size_]; + } + + argc_ = argc; + for (size_t i = 0; i < size_; i++) { + argv_[i] = argv[i]; + lens_[i] = lens[i]; + } +#endif } void set_addr(const char* addr) { @@ -142,6 +165,7 @@ private: size_t redirect_count_; public: + size_t size_; size_t argc_; const char** argv_; size_t* lens_; diff --git a/lib_acl_cpp/src/http/HttpServlet.cpp b/lib_acl_cpp/src/http/HttpServlet.cpp index 55e7f5cee..9d18e35a5 100644 --- a/lib_acl_cpp/src/http/HttpServlet.cpp +++ b/lib_acl_cpp/src/http/HttpServlet.cpp @@ -19,6 +19,7 @@ namespace acl HttpServlet::HttpServlet(socket_stream* stream, session* session) : req_(NULL) , res_(NULL) +, parse_body_(true) , stream_(stream) { init(); @@ -88,8 +89,9 @@ HttpServlet& HttpServlet::setRwTimeout(int rw_timeout) return *this; } -HttpServlet& HttpServlet::setParseBody(bool) +HttpServlet& HttpServlet::setParseBody(bool yes) { + parse_body_ = yes; return *this; } @@ -161,6 +163,7 @@ bool HttpServlet::start(void) res_ = NEW HttpServletResponse(*out); req_ = NEW HttpServletRequest(*res_, *session_, *in, local_charset_, parse_body_limit_); + req_->setParseBody(parse_body_); // HttpServletRequest res_->setHttpServletRequest(req_); diff --git a/lib_acl_cpp/src/http/HttpServletRequest.cpp b/lib_acl_cpp/src/http/HttpServletRequest.cpp index bf68cd9e8..6eb6aad00 100644 --- a/lib_acl_cpp/src/http/HttpServletRequest.cpp +++ b/lib_acl_cpp/src/http/HttpServletRequest.cpp @@ -43,6 +43,7 @@ HttpServletRequest::HttpServletRequest(HttpServletResponse& res, , client_(NULL) , method_(HTTP_METHOD_UNKNOWN) , request_type_(HTTP_REQUEST_NORMAL) +, parse_body_(true) , mime_(NULL) , body_(NULL) , json_(NULL) @@ -90,6 +91,11 @@ http_method_t HttpServletRequest::getMethod(string* method_s /* = NULL */) const return method_; } +void HttpServletRequest::setParseBody(bool yes) +{ + parse_body_ = yes; +} + void HttpServletRequest::add_cookie(char* data) { SKIP_SPACE(data); @@ -826,6 +832,10 @@ bool HttpServletRequest::readHeader(string* method_s) // Ϊ form ʽʱ if (EQ(ctype, "application") && EQ(stype, "x-www-form-urlencoded")) { request_type_ = HTTP_REQUEST_NORMAL; + if (!parse_body_) { + return true; + } + char* query = (char*) dbuf_->dbuf_alloc((size_t) len + 1); int ret = getInputStream().read(query, (size_t) len); if (ret > 0) { diff --git a/lib_acl_cpp/src/redis/redis_client_cluster.cpp b/lib_acl_cpp/src/redis/redis_client_cluster.cpp index 433f7b748..c0bc47351 100644 --- a/lib_acl_cpp/src/redis/redis_client_cluster.cpp +++ b/lib_acl_cpp/src/redis/redis_client_cluster.cpp @@ -164,11 +164,13 @@ void redis_client_cluster::set_all_slot(const char* addr, size_t max_conns, char buf[128]; safe_snprintf(buf, sizeof(buf), "%s:%d", ip, port); redis_client_pool* conns = (redis_client_pool*) get(buf); - if (conns == NULL) + if (conns == NULL) { set(buf, max_conns, conn_timeout, rw_timeout); + } - for (size_t i = slot_min; i <= slot_max; i++) + for (size_t i = slot_min; i <= slot_max; i++) { set_slot((int) i, buf); + } } } diff --git a/lib_acl_cpp/src/redis/redis_cluster.cpp b/lib_acl_cpp/src/redis/redis_cluster.cpp index 647327ff7..942e1955a 100644 --- a/lib_acl_cpp/src/redis/redis_cluster.cpp +++ b/lib_acl_cpp/src/redis/redis_cluster.cpp @@ -112,13 +112,13 @@ int redis_cluster::cluster_getkeysinslot(size_t slot, size_t max, argv[1] = "GETKEYSINSLOT"; lens[1] = sizeof("GETKEYSINSLOT") - 1; - char slot_s[LONG_LEN]; - safe_snprintf(slot_s, sizeof(slot_s), "%lu", (unsigned long) slot); + char* slot_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(slot_s, LONG_LEN, "%lu", (unsigned long) slot); argv[2] = slot_s; lens[2] = strlen(slot_s); - char max_s[LONG_LEN]; - safe_snprintf(max_s, sizeof(max_s), "%lu", (unsigned long) max); + char* max_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(max_s, LONG_LEN, "%lu", (unsigned long) max); argv[3] = max_s; lens[3] = strlen(max_s); @@ -140,8 +140,8 @@ bool redis_cluster::cluster_meet(const char* ip, int port) argv[2] = ip; lens[2] = strlen(ip); - char port_s[INT_LEN]; - safe_snprintf(port_s, sizeof(port_s), "%d", port); + char* port_s = (char*) dbuf_->dbuf_alloc(INT_LEN); + safe_snprintf(port_s, INT_LEN, "%d", port); argv[3] = port_s; lens[3] = strlen(port_s); @@ -212,8 +212,8 @@ bool redis_cluster::cluster_setslot_importing(size_t slot, const char* src_node) argv[1] = "SETSLOT"; lens[1] = sizeof("SETSLOT") - 1; - char slot_s[LONG_LEN]; - safe_snprintf(slot_s, sizeof(slot_s), "%lu", (unsigned long) slot); + char* slot_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(slot_s, LONG_LEN, "%lu", (unsigned long) slot); argv[2] = slot_s; lens[2] = strlen(slot_s); @@ -238,8 +238,8 @@ bool redis_cluster::cluster_setslot_migrating(size_t slot, const char* dst_node) argv[1] = "SETSLOT"; lens[1] = sizeof("SETSLOT") - 1; - char slot_s[LONG_LEN]; - safe_snprintf(slot_s, sizeof(slot_s), "%lu", (unsigned long) slot); + char* slot_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(slot_s, LONG_LEN, "%lu", (unsigned long) slot); argv[2] = slot_s; lens[2] = strlen(slot_s); @@ -264,8 +264,8 @@ bool redis_cluster::cluster_setslot_stable(size_t slot) argv[1] = "SETSLOT"; lens[1] = sizeof("SETSLOT") - 1; - char slot_s[LONG_LEN]; - safe_snprintf(slot_s, sizeof(slot_s), "%lu", (unsigned long) slot); + char* slot_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(slot_s, LONG_LEN, "%lu", (unsigned long) slot); argv[2] = slot_s; lens[2] = strlen(slot_s); @@ -287,8 +287,8 @@ bool redis_cluster::cluster_setslot_node(size_t slot, const char* node) argv[1] = "SETSLOT"; lens[1] = sizeof("SETSLOT") - 1; - char slot_s[LONG_LEN]; - safe_snprintf(slot_s, sizeof(slot_s), "%lu", (unsigned long) slot); + char* slot_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(slot_s, LONG_LEN, "%lu", (unsigned long) slot); argv[2] = slot_s; lens[2] = strlen(slot_s); @@ -432,8 +432,8 @@ int redis_cluster::cluster_countkeysinslot(size_t slot) argv[1] = "COUNTKEYSINSLOT"; lens[1] = sizeof("COUNTKEYSINSLOT") - 1; - char slot_s[LONG_LEN]; - safe_snprintf(slot_s, sizeof(slot_s), "%lu", (unsigned long) slot); + char* slot_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(slot_s, LONG_LEN, "%lu", (unsigned long) slot); argv[2] = slot_s; lens[2] = strlen(slot_s); diff --git a/lib_acl_cpp/src/redis/redis_command.cpp b/lib_acl_cpp/src/redis/redis_command.cpp index 8f4a00772..2714a1c59 100644 --- a/lib_acl_cpp/src/redis/redis_command.cpp +++ b/lib_acl_cpp/src/redis/redis_command.cpp @@ -859,8 +859,8 @@ const redis_result** redis_command::scan_keys(const char* cmd, const char* key, argc++; } - char cursor_s[INT_LEN]; - safe_snprintf(cursor_s, sizeof(cursor_s), "%d", cursor); + char* cursor_s = (char*) dbuf_->dbuf_alloc(INT_LEN); + safe_snprintf(cursor_s, INT_LEN, "%d", cursor); argv[argc] = cursor_s; lens[argc] = strlen(cursor_s); argc++; @@ -880,8 +880,8 @@ const redis_result** redis_command::scan_keys(const char* cmd, const char* key, lens[argc] = sizeof("COUNT") - 1; argc++; - char count_s[LONG_LEN]; - safe_snprintf(count_s, sizeof(count_s), "%lu", + char* count_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(count_s, LONG_LEN, "%lu", (unsigned long) (*count)); argv[argc] = count_s; lens[argc] = strlen(count_s); diff --git a/lib_acl_cpp/src/redis/redis_connection.cpp b/lib_acl_cpp/src/redis/redis_connection.cpp index 9ac03d1ef..aeaa51207 100644 --- a/lib_acl_cpp/src/redis/redis_connection.cpp +++ b/lib_acl_cpp/src/redis/redis_connection.cpp @@ -62,8 +62,8 @@ bool redis_connection::select(int dbnum) argv[0] = "SELECT"; lens[0] = strlen(argv[0]); - char buf[21]; - safe_snprintf(buf, sizeof(buf), "%d", dbnum); + char* buf = (char*) dbuf_->dbuf_alloc(21); + safe_snprintf(buf, 21, "%d", dbnum); argv[1] = buf; lens[1] = strlen(argv[1]); diff --git a/lib_acl_cpp/src/redis/redis_hash.cpp b/lib_acl_cpp/src/redis/redis_hash.cpp index ea08148ce..285861ea2 100644 --- a/lib_acl_cpp/src/redis/redis_hash.cpp +++ b/lib_acl_cpp/src/redis/redis_hash.cpp @@ -440,8 +440,8 @@ bool redis_hash::hincrbyfloat(const char* key, const char* name, const char* values[1]; names[0] = name; - char buf[FLOAT_LEN]; - (void) safe_snprintf(buf, sizeof(buf), "%f", inc); + char* buf = (char*) dbuf_->dbuf_alloc(FLOAT_LEN); + (void) safe_snprintf(buf, FLOAT_LEN, "%f", inc); values[0] = buf; hash_slot(key); diff --git a/lib_acl_cpp/src/redis/redis_key.cpp b/lib_acl_cpp/src/redis/redis_key.cpp index ded0d76b7..4b2809a52 100644 --- a/lib_acl_cpp/src/redis/redis_key.cpp +++ b/lib_acl_cpp/src/redis/redis_key.cpp @@ -195,7 +195,7 @@ int redis_key::expire(const char* key, size_t len, int n) argv[1] = key; lens[1] = len; - char buf[INT_LEN]; + char* buf = (char*) dbuf_->dbuf_alloc(INT_LEN); (void) safe_snprintf(buf, INT_LEN, "%d", n); argv[2] = buf; lens[2] = strlen(buf); @@ -221,8 +221,8 @@ int redis_key::expireat(const char* key, size_t len, time_t stamp) argv[1] = key; lens[1] = len; - char stamp_s[LONG_LEN]; - safe_snprintf(stamp_s, sizeof(stamp_s), "%lu", (unsigned long) stamp); + char* stamp_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(stamp_s, LONG_LEN, "%lu", (unsigned long) stamp); argv[2] = stamp_s; lens[2] = strlen(stamp_s); @@ -284,7 +284,7 @@ int redis_key::pexpire(const char* key, size_t len, int n) argv[1] = key; lens[1] = len; - char buf[INT_LEN]; + char* buf = (char*) dbuf_->dbuf_alloc(INT_LEN); (void) safe_snprintf(buf, INT_LEN, "%d", n); argv[2] = buf; lens[2] = strlen(buf); @@ -422,8 +422,8 @@ bool redis_key::restore(const char* key, const char* value, size_t len, argv[1] = key; lens[1] = strlen(key); - char ttl_s[INT_LEN]; - safe_snprintf(ttl_s, sizeof(ttl_s), "%d", nttl); + char* ttl_s = (char*) dbuf_->dbuf_alloc(INT_LEN); + safe_snprintf(ttl_s, INT_LEN, "%d", nttl); argv[2] = ttl_s; lens[2] = strlen(ttl_s); @@ -517,8 +517,8 @@ bool redis_key::migrate(const char* key, const char* addr, unsigned dest_db, bool redis_key::migrate(const char* key, size_t len, const char* addr, unsigned dest_db, unsigned timeout, const char* option /* = NULL */) { - char addrbuf[64]; - safe_snprintf(addrbuf, sizeof(addrbuf), "%s", addr); + char* addrbuf = (char*) dbuf_->dbuf_alloc(64); + safe_snprintf(addrbuf, 64, "%s", addr); char* at = strchr(addrbuf, ':'); if (at == NULL || *(at + 1) == 0) return false; @@ -540,13 +540,13 @@ bool redis_key::migrate(const char* key, size_t len, const char* addr, argv[3] = key; lens[3] = len; - char db_s[11]; - safe_snprintf(db_s, sizeof(db_s), "%u", dest_db); + char* db_s = (char*) dbuf_->dbuf_alloc(11); + safe_snprintf(db_s, 11, "%u", dest_db); argv[4] = db_s; lens[4] = strlen(db_s); - char timeout_s[11]; - safe_snprintf(timeout_s, sizeof(timeout_s), "%u", timeout); + char* timeout_s = (char*) dbuf_->dbuf_alloc(11); + safe_snprintf(timeout_s, 11, "%u", timeout); argv[5] = timeout_s; lens[5] = strlen(timeout_s); @@ -575,8 +575,8 @@ int redis_key::move(const char* key, size_t len, unsigned dest_db) argv[1] = key; lens[1] = len; - char db_s[11]; - safe_snprintf(db_s, sizeof(db_s), "%u", dest_db); + char* db_s = (char*) dbuf_->dbuf_alloc(11); + safe_snprintf(db_s, 11, "%u", dest_db); argv[2] = db_s; lens[2] = strlen(db_s); diff --git a/lib_acl_cpp/src/redis/redis_list.cpp b/lib_acl_cpp/src/redis/redis_list.cpp index 98ded9852..20a79b432 100644 --- a/lib_acl_cpp/src/redis/redis_list.cpp +++ b/lib_acl_cpp/src/redis/redis_list.cpp @@ -71,8 +71,8 @@ bool redis_list::lindex(const char* key, size_t idx, string& buf) argv[1] = key; lens[1] = strlen(key); - char tmp[LONG_LEN]; - (void) safe_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) idx); + char* tmp = (char*) dbuf_->dbuf_alloc(LONG_LEN); + (void) safe_snprintf(tmp, LONG_LEN, "%lu", (unsigned long) idx); argv[2] = tmp; lens[2] = strlen(tmp); @@ -96,8 +96,8 @@ bool redis_list::lset(const char* key, int idx, const char* value, size_t len) argv[1] = key; lens[1] = strlen(key); - char tmp[LONG_LEN]; - (void) safe_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) idx); + char* tmp = (char*) dbuf_->dbuf_alloc(LONG_LEN); + (void) safe_snprintf(tmp, LONG_LEN, "%lu", (unsigned long) idx); argv[2] = tmp; lens[2] = strlen(tmp); @@ -388,8 +388,8 @@ bool redis_list::bpop(const char* cmd, const std::vector& keys, i++; } - char buf[LONG_LEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) timeout); + char* buf = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(buf, LONG_LEN, "%lu", (unsigned long) timeout); args[i] = buf; lens[i] = strlen(args[i]); @@ -415,8 +415,8 @@ bool redis_list::bpop(const char* cmd, const std::vector& keys, i++; } - char buf[LONG_LEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) timeout); + char* buf = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(buf, LONG_LEN, "%lu", (unsigned long) timeout); args[i] = buf; lens[i] = strlen(args[i]); @@ -486,8 +486,8 @@ bool redis_list::brpoplpush(const char* src, const char* dst, argv[2] = dst; lens[2] = strlen(dst); - char tmp[LONG_LEN]; - safe_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) timeout); + char* tmp = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(tmp, LONG_LEN, "%lu", (unsigned long) timeout); argv[3] = tmp; lens[3] = strlen(argv[3]); @@ -506,9 +506,10 @@ bool redis_list::lrange(const char* key, int start, int end, argv[1] = key; lens[1] = strlen(key); - char start_s[LONG_LEN], end_s[LONG_LEN]; - safe_snprintf(start_s, sizeof(start_s), "%d", start); - safe_snprintf(end_s, sizeof(end_s), "%d", end); + char* start_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + char* end_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(start_s, LONG_LEN, "%d", start); + safe_snprintf(end_s, LONG_LEN, "%d", end); argv[2] = start_s; lens[2] = strlen(start_s); @@ -535,8 +536,8 @@ int redis_list::lrem(const char* key, int count, const char* value, size_t len) argv[1] = key; lens[1] = strlen(key); - char buf[INT_LEN]; - safe_snprintf(buf, sizeof(buf), "%d", count); + char* buf = (char*) dbuf_->dbuf_alloc(INT_LEN); + safe_snprintf(buf, INT_LEN, "%d", count); argv[2] = buf; lens[2] = strlen(buf); @@ -558,9 +559,10 @@ bool redis_list::ltrim(const char* key, int start, int end) argv[1] = key; lens[1] = strlen(key); - char start_s[LONG_LEN], end_s[LONG_LEN]; - safe_snprintf(start_s, sizeof(start_s), "%d", start); - safe_snprintf(end_s, sizeof(end_s), "%d", end); + char* start_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + char* end_s = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(start_s, LONG_LEN, "%d", start); + safe_snprintf(end_s, LONG_LEN, "%d", end); argv[2] = start_s; lens[2] = strlen(start_s); diff --git a/lib_acl_cpp/src/redis/redis_role.cpp b/lib_acl_cpp/src/redis/redis_role.cpp index 5a81941e8..c2718ecfe 100644 --- a/lib_acl_cpp/src/redis/redis_role.cpp +++ b/lib_acl_cpp/src/redis/redis_role.cpp @@ -1,6 +1,5 @@ #include "acl_stdafx.hpp" #ifndef ACL_PREPARE_COMPILE -#include "acl_cpp/stdlib/snprintf.hpp" #include "acl_cpp/stdlib/dbuf_pool.hpp" #include "acl_cpp/stdlib/string.hpp" #include "acl_cpp/redis/redis_client.hpp" diff --git a/lib_acl_cpp/src/redis/redis_script.cpp b/lib_acl_cpp/src/redis/redis_script.cpp index 06752c5cc..2bd4f71bb 100644 --- a/lib_acl_cpp/src/redis/redis_script.cpp +++ b/lib_acl_cpp/src/redis/redis_script.cpp @@ -392,8 +392,8 @@ const redis_result* redis_script::eval_cmd(const char* cmd, argv[1] = script; lens[1] = strlen(script); - char buf[LONG_LEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) keys.size()); + char* buf = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(buf, LONG_LEN, "%lu", (unsigned long) keys.size()); argv[2] = buf; lens[2] = strlen(buf); @@ -437,8 +437,8 @@ const redis_result* redis_script::eval_cmd(const char* cmd, argv[1] = script; lens[1] = strlen(script); - char buf[LONG_LEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) keys.size()); + char* buf = (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(buf, LONG_LEN, "%lu", (unsigned long) keys.size()); argv[2] = buf; lens[2] = strlen(buf); diff --git a/lib_acl_cpp/src/redis/redis_sentinel.cpp b/lib_acl_cpp/src/redis/redis_sentinel.cpp index 399ad0442..baa8b681a 100644 --- a/lib_acl_cpp/src/redis/redis_sentinel.cpp +++ b/lib_acl_cpp/src/redis/redis_sentinel.cpp @@ -399,13 +399,13 @@ bool redis_sentinel::sentinel_monitor(const char* master_name, const char* ip, argv[3] = ip; lens[3] = strlen(ip); - char port_s[64]; - safe_snprintf(port_s, sizeof(port_s), "%d", port); + char* port_s = (char*) dbuf_->dbuf_alloc(64); + safe_snprintf(port_s, 64, "%d", port); argv[4] = port_s; lens[4] = strlen(argv[4]); - char quorum_s[64]; - safe_snprintf(quorum_s, sizeof(quorum_s), "%d", quorum); + char* quorum_s = (char*) dbuf_->dbuf_alloc(64); + safe_snprintf(quorum_s, 64, "%d", quorum); argv[5] = quorum_s; lens[5] = strlen(argv[5]); @@ -441,8 +441,8 @@ bool redis_sentinel::sentinel_set(const char* master_name, const char* name, bool redis_sentinel::sentinel_set(const char* master_name, const char* name, unsigned value) { - char buf[64]; - safe_snprintf(buf, sizeof(buf), "%u", value); + char* buf = (char*) dbuf_->dbuf_alloc(64); + safe_snprintf(buf, 64, "%u", value); return sentinel_set(master_name, name, buf); } diff --git a/lib_acl_cpp/src/redis/redis_server.cpp b/lib_acl_cpp/src/redis/redis_server.cpp index 1237efcfa..ae35e81d0 100644 --- a/lib_acl_cpp/src/redis/redis_server.cpp +++ b/lib_acl_cpp/src/redis/redis_server.cpp @@ -358,8 +358,8 @@ bool redis_server::slaveof(const char* ip, int port) argv[1] = ip; lens[1] = strlen(ip); - char port_s[INT_LEN]; - safe_snprintf(port_s, sizeof(port_s), "%d", port); + char* port_s= (char*) dbuf_->dbuf_alloc(INT_LEN); + safe_snprintf(port_s, INT_LEN, "%d", port); argv[2] = port_s; lens[2] = strlen(port_s); @@ -380,9 +380,9 @@ const redis_result* redis_server::slowlog_get(int number /* = 0 */) size_t argc = 2; - char buf[INT_LEN]; if (number > 0) { - safe_snprintf(buf, sizeof(buf), "%d", number); + char* buf = (char*) dbuf_->dbuf_alloc(INT_LEN); + safe_snprintf(buf, INT_LEN, "%d", number); argv[2] = buf; lens[2] = strlen(buf); argc++; diff --git a/lib_acl_cpp/src/redis/redis_set.cpp b/lib_acl_cpp/src/redis/redis_set.cpp index 806d50717..4c35c0ab6 100644 --- a/lib_acl_cpp/src/redis/redis_set.cpp +++ b/lib_acl_cpp/src/redis/redis_set.cpp @@ -380,8 +380,8 @@ int redis_set::srandmember(const char* key, size_t n, std::vector& out) argv[1] = key; lens[1] = strlen(key); - char buf[LONG_LEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) n); + char* buf= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(buf, LONG_LEN, "%lu", (unsigned long) n); argv[2] = buf; lens[2] = strlen(buf); diff --git a/lib_acl_cpp/src/redis/redis_stream.cpp b/lib_acl_cpp/src/redis/redis_stream.cpp index 5e4ebea56..e53e6eb09 100644 --- a/lib_acl_cpp/src/redis/redis_stream.cpp +++ b/lib_acl_cpp/src/redis/redis_stream.cpp @@ -149,8 +149,8 @@ bool redis_stream::xadd_with_maxlen(const char* key, size_t maxlen, argv_lens_[i] = 1; i++; - char buf[LONG_LEN]; - safe_snprintf(buf, sizeof(buf), "%ld", (long) maxlen); + char* buf= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(buf, LONG_LEN, "%ld", (long) maxlen); argv_[i] = buf; argv_lens_[i] = strlen(buf); i++; @@ -196,26 +196,25 @@ int redis_stream::xlen(const char* key) void redis_stream::build(const std::map& streams, size_t i, size_t count, ssize_t block, bool noack /* = false */) { - char count_s[LONG_LEN]; if (count > 0) { argv_[i] = "COUNT"; argv_lens_[i] = sizeof("COUNT") - 1; i++; - safe_snprintf(count_s, sizeof(count_s), "%lu", - (unsigned long) count); + char* count_s= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(count_s, LONG_LEN, "%lu", (unsigned long) count); argv_[i] = count_s; argv_lens_[i] = strlen(count_s); i++; } - char block_s[LONG_LEN]; if (block >= 0) { argv_[i] = "BLOCK"; argv_lens_[i] = sizeof("BLOCK") - 1; i++; - safe_snprintf(block_s, sizeof(block_s), "%ld", (long) block); + char* block_s= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(block_s, LONG_LEN, "%ld", (long) block); argv_[i] = block_s; argv_lens_[i] = strlen(block_s); i++; @@ -359,14 +358,13 @@ bool redis_stream::range(redis_stream_messages& messages, const char* cmd, lens[i] = strlen(end); i++; - char count_s[LONG_LEN]; if (count > 0) { argv[i] = "COUNT"; lens[i] = sizeof("COUNT") - 1; i++; - safe_snprintf(count_s, sizeof(count_s), "%lu", - (unsigned long) count); + char* count_s= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(count_s, LONG_LEN, "%lu", (unsigned long) count); argv[i] = count_s; lens[i] = strlen(count_s); i++; @@ -545,8 +543,8 @@ void redis_stream::xclaim_build(const char* key, const char* group, argv_lens_[i] = strlen(consumer); i++; - char min_idle_s[LONG_LEN]; - safe_snprintf(min_idle_s, sizeof(min_idle_s), "%lu", min_idle_time); + char* min_idle_s= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(min_idle_s, LONG_LEN, "%lu", min_idle_time); argv_[i] = min_idle_s; argv_lens_[i] = strlen(min_idle_s); i++; @@ -559,22 +557,23 @@ void redis_stream::xclaim_build(const char* key, const char* group, i++; } - char tbuf[LONG_LEN]; if (idle > 0) { - safe_snprintf(tbuf, sizeof(tbuf), "%lu", (unsigned long) idle); + char* tbuf= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(tbuf, LONG_LEN, "%lu", (unsigned long) idle); argv_[i] = tbuf; argv_lens_[i] = strlen(tbuf); i++; } else if (time_ms > 0) { - safe_snprintf(tbuf, sizeof(tbuf), "%lld", time_ms); + char* tbuf= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(tbuf, LONG_LEN, "%lld", time_ms); argv_[i] = tbuf; argv_lens_[i] = strlen(tbuf); i++; } - char retry_buf[INT_LEN]; if (retry_count > 0) { - safe_snprintf(retry_buf, sizeof(retry_buf), "%d", retry_count); + char* retry_buf= (char*) dbuf_->dbuf_alloc(INT_LEN); + safe_snprintf(retry_buf, INT_LEN, "%d", retry_count); argv_[i] = retry_buf; argv_lens_[i] = strlen(retry_buf); i++; @@ -968,8 +967,8 @@ bool redis_stream::xpending_detail(redis_pending_detail& result, lens[i] = strlen(end_id); i++; - char count_s[LONG_LEN]; - safe_snprintf(count_s, sizeof(count_s), "%lu", (unsigned long) count); + char* count_s= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(count_s, LONG_LEN, "%lu", (unsigned long) count); argv[i] = count_s; lens[i] = strlen(count_s); i++; @@ -1105,8 +1104,8 @@ int redis_stream::xtrim(const char* key, size_t maxlen, bool tilde) i++; } - char buf[LONG_LEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) maxlen); + char* buf= (char*) dbuf_->dbuf_alloc(LONG_LEN); + safe_snprintf(buf, LONG_LEN, "%lu", (unsigned long) maxlen); argv[i] = buf; lens[i] = strlen(buf); i++; diff --git a/lib_acl_cpp/src/redis/redis_string.cpp b/lib_acl_cpp/src/redis/redis_string.cpp index 66182fa2f..00c904aa6 100644 --- a/lib_acl_cpp/src/redis/redis_string.cpp +++ b/lib_acl_cpp/src/redis/redis_string.cpp @@ -104,8 +104,8 @@ bool redis_string::set(const char* key, size_t key_len, const char* value, goto NEXT_X; } - char buf[INT_LEN]; - (void) safe_snprintf(buf, sizeof(buf), "%d", timeout); + char* buf= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(buf, INT_LEN, "%d", timeout); argv[n] = buf; lens[n] = strlen(buf); n++; @@ -144,8 +144,8 @@ bool redis_string::setex(const char* key, size_t key_len, const char* value, argv[1] = key; lens[1] = key_len; - char buf[INT_LEN]; - (void) safe_snprintf(buf, sizeof(buf), "%d", timeout); + char* buf= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(buf, INT_LEN, "%d", timeout); argv[2] = buf; lens[2] = strlen(buf); @@ -174,8 +174,8 @@ bool redis_string::psetex(const char* key, size_t key_len, const char* value, argv[1] = key; lens[1] = key_len; - char buf[INT_LEN]; - (void) safe_snprintf(buf, sizeof(buf), "%d", timeout); + char* buf= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(buf, INT_LEN, "%d", timeout); argv[2] = buf; lens[2] = strlen(buf); @@ -378,12 +378,13 @@ bool redis_string::getrange(const char* key, size_t key_len, argv[1] = key; lens[1] = key_len; - char start_buf[INT_LEN], end_buf[INT_LEN]; - (void) safe_snprintf(start_buf, sizeof(start_buf), "%d", start); + char* start_buf= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(start_buf, INT_LEN, "%d", start); argv[2] = start_buf; lens[2] = strlen(start_buf); - (void) safe_snprintf(end_buf, sizeof(end_buf), "%d", end); + char* end_buf= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(end_buf, INT_LEN, "%d", end); argv[3] = end_buf; lens[3] = strlen(end_buf); @@ -411,8 +412,8 @@ bool redis_string::setbit_(const char* key, size_t len, argv[1] = key; lens[1] = len; - char buf4off[INT_LEN]; - (void) safe_snprintf(buf4off, sizeof(buf4off), "%d", offset); + char* buf4off= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(buf4off, INT_LEN, "%d", offset); argv[2] = buf4off; lens[2] = strlen(buf4off); @@ -441,8 +442,8 @@ bool redis_string::getbit(const char* key, size_t len, argv[1] = key; lens[1] = len; - char buf4off[INT_LEN]; - (void) safe_snprintf(buf4off, sizeof(buf4off), "%d", offset); + char* buf4off= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(buf4off, INT_LEN, "%d", offset); argv[2] = buf4off; lens[2] = strlen(buf4off); @@ -492,13 +493,13 @@ int redis_string::bitcount(const char* key, size_t len, int start, int end) argv[1] = key; lens[1] = len; - char buf4start[INT_LEN]; - (void) safe_snprintf(buf4start, sizeof(buf4start), "%d", start); + char* buf4start= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(buf4start, INT_LEN, "%d", start); argv[2] = buf4start; lens[2] = strlen(buf4start); - char buf4end[INT_LEN]; - (void) safe_snprintf(buf4end, sizeof(buf4end), "%d", end); + char* buf4end= (char*) dbuf_->dbuf_alloc(INT_LEN); + (void) safe_snprintf(buf4end, INT_LEN, "%d", end); argv[3] = buf4end; lens[3] = strlen(buf4end); @@ -800,8 +801,8 @@ bool redis_string::incrbyfloat(const char* key, double inc, argv[1] = key; lens[1] = strlen(key); - char buf[FLOAT_LEN]; - (void) safe_snprintf(buf, sizeof(buf), "%f", inc); + char* buf= (char*) dbuf_->dbuf_alloc(FLOAT_LEN); + (void) safe_snprintf(buf, FLOAT_LEN, "%f", inc); argv[2] = buf; lens[2] = strlen(buf); diff --git a/lib_acl_cpp/src/redis/redis_zset.cpp b/lib_acl_cpp/src/redis/redis_zset.cpp index 7be242a69..c5ae24d6b 100644 --- a/lib_acl_cpp/src/redis/redis_zset.cpp +++ b/lib_acl_cpp/src/redis/redis_zset.cpp @@ -344,8 +344,8 @@ bool redis_zset::zadd_with_incr(const char* key, const char* member, i++; } - char score_s[BUFLEN]; - safe_snprintf(score_s, sizeof(score_s), "%.8f", score); + char* score_s= (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(score_s, BUFLEN, "%.8f", score); argv[i] = score_s; lens[i] = strlen(score_s); i++; @@ -410,9 +410,10 @@ int redis_zset::zcount(const char* key, double min, double max) argv[1] = key; lens[1] = strlen(key); - char min_buf[BUFLEN], max_buf[BUFLEN]; - safe_snprintf(min_buf, sizeof(min_buf), "%.8f", min); - safe_snprintf(max_buf, sizeof(max_buf), "%.8f", max); + char* min_buf= (char*) dbuf_->dbuf_alloc(BUFLEN); + char* max_buf= (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(min_buf, BUFLEN, "%.8f", min); + safe_snprintf(max_buf, BUFLEN, "%.8f", max); argv[2] = min_buf; lens[2] = strlen(min_buf); @@ -443,8 +444,8 @@ bool redis_zset::zincrby(const char* key, double inc, argv[1] = key; lens[1] = strlen(key); - char score[BUFLEN]; - safe_snprintf(score, sizeof(score), "%.8f", inc); + char* score= (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(score, BUFLEN, "%.8f", inc); argv[2] = score; lens[2] = strlen(score); @@ -474,9 +475,10 @@ int redis_zset::zrange_get(const char* cmd, const char* key, int start, argv[1] = key; lens[1] = strlen(key); - char start_s[INTLEN], stop_s[INTLEN]; - safe_snprintf(start_s, sizeof(start_s), "%d", start); - safe_snprintf(stop_s, sizeof(stop_s), "%d", stop); + char* start_s= (char*) dbuf_->dbuf_alloc(INTLEN); + char* stop_s= (char*) dbuf_->dbuf_alloc(INTLEN); + safe_snprintf(start_s, INTLEN, "%d", start); + safe_snprintf(stop_s, INTLEN, "%d", stop); argv[2] = start_s; lens[2] = strlen(start_s); @@ -550,9 +552,10 @@ int redis_zset::zrange_get_with_scores(const char* cmd, const char* key, argv[1] = key; lens[1] = strlen(key); - char start_s[INTLEN], stop_s[INTLEN]; - safe_snprintf(start_s, sizeof(start_s), "%d", start); - safe_snprintf(stop_s, sizeof(stop_s), "%d", stop); + char* start_s= (char*) dbuf_->dbuf_alloc(INTLEN); + char* stop_s= (char*) dbuf_->dbuf_alloc(INTLEN); + safe_snprintf(start_s, INTLEN, "%d", start); + safe_snprintf(stop_s, INTLEN, "%d", stop); argv[2] = start_s; lens[2] = strlen(start_s); @@ -595,10 +598,11 @@ int redis_zset::zrangebyscore_get(const char* cmd, const char* key, argv[3] = max; lens[3] = strlen(max); - char offset_s[INTLEN], count_s[INTLEN]; if (offset && count) { - safe_snprintf(offset_s, sizeof(offset_s), "%d", *offset); - safe_snprintf(count_s, sizeof(count_s), "%d", *count); + char* offset_s= (char*) dbuf_->dbuf_alloc(INTLEN); + char* count_s= (char*) dbuf_->dbuf_alloc(INTLEN); + safe_snprintf(offset_s, INTLEN, "%d", *offset); + safe_snprintf(count_s, INTLEN, "%d", *count); argv[4] = "LIMIT"; lens[4] = sizeof("LIMIT") - 1; @@ -629,9 +633,10 @@ int redis_zset::zrangebyscore(const char* key, double min, double max, std::vector* out, const int* offset /* = NULL */, const int* count /* = NULL */) { - char min_s[BUFLEN], max_s[BUFLEN]; - safe_snprintf(min_s, sizeof(min_s), "%.8f", min); - safe_snprintf(max_s, sizeof(max_s), "%.8f", max); + char* min_s= (char*) dbuf_->dbuf_alloc(BUFLEN); + char* max_s= (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(min_s, BUFLEN, "%.8f", min); + safe_snprintf(max_s, BUFLEN, "%.8f", max); return zrangebyscore(key, min_s, max_s, out, offset, count); } @@ -660,10 +665,11 @@ int redis_zset::zrangebyscore_get_with_scores(const char* cmd, argv[4] = "WITHSCORES"; lens[4] = sizeof("WITHSCORES") - 1; - char offset_s[INTLEN], count_s[INTLEN]; if (offset && count) { - safe_snprintf(offset_s, sizeof(offset_s), "%d", *offset); - safe_snprintf(count_s, sizeof(count_s), "%d", *count); + char* offset_s = (char*) dbuf_->dbuf_alloc(INTLEN); + char* count_s = (char*) dbuf_->dbuf_alloc(INTLEN); + safe_snprintf(offset_s, INTLEN, "%d", *offset); + safe_snprintf(count_s, INTLEN, "%d", *count); argv[5] = "LIMIT"; lens[5] = sizeof("LIMIT") - 1; @@ -695,9 +701,10 @@ int redis_zset::zrangebyscore_with_scores(const char* key, double min, double max, std::vector >& out, const int* offset /* = NULL */, const int* count /* = NULL */) { - char min_s[BUFLEN], max_s[BUFLEN]; - safe_snprintf(min_s, sizeof(min_s), "%.8f", min); - safe_snprintf(max_s, sizeof(max_s), "%.8f", max); + char* min_s = (char*) dbuf_->dbuf_alloc(BUFLEN); + char* max_s = (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(min_s, BUFLEN, "%.8f", min); + safe_snprintf(max_s, BUFLEN, "%.8f", max); return zrangebyscore_with_scores(key, min_s, max_s, out, offset, count); } @@ -772,9 +779,10 @@ int redis_zset::zremrangebyrank(const char* key, int start, int stop) argv[1] = key; lens[1] = strlen(key); - char start_s[INTLEN], stop_s[INTLEN]; - safe_snprintf(start_s, sizeof(start_s), "%d", start); - safe_snprintf(stop_s, sizeof(stop_s), "%d", stop); + char* start_s = (char*) dbuf_->dbuf_alloc(INTLEN); + char*stop_s = (char*) dbuf_->dbuf_alloc(INTLEN); + safe_snprintf(start_s, INTLEN, "%d", start); + safe_snprintf(stop_s, INTLEN, "%d", stop); argv[2] = start_s; lens[2] = strlen(start_s); @@ -789,9 +797,10 @@ int redis_zset::zremrangebyrank(const char* key, int start, int stop) int redis_zset::zremrangebyscore(const char* key, double min, double max) { - char min_s[BUFLEN], max_s[BUFLEN]; - safe_snprintf(min_s, sizeof(min_s), "%.8f", min); - safe_snprintf(max_s, sizeof(max_s), "%.8f", max); + char* min_s = (char*) dbuf_->dbuf_alloc(BUFLEN); + char* max_s = (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(min_s, BUFLEN, "%.8f", min); + safe_snprintf(max_s, BUFLEN, "%.8f", max); return zremrangebyscore(key, min_s, max_s); } @@ -843,9 +852,10 @@ int redis_zset::zrevrangebyscore_with_scores(const char* key, double min, double max, std::vector >& out, const int* offset /* = NULL */, const int* count /* = NULL */) { - char min_s[BUFLEN], max_s[BUFLEN]; - safe_snprintf(min_s, sizeof(min_s), "%.8f", min); - safe_snprintf(max_s, sizeof(max_s), "%.8f", max); + char* min_s = (char*) dbuf_->dbuf_alloc(BUFLEN); + char* max_s = (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(min_s, BUFLEN, "%.8f", min); + safe_snprintf(max_s, BUFLEN, "%.8f", max); return zrevrangebyscore_with_scores(key, min_s, max_s, out, offset, count); @@ -925,8 +935,8 @@ int redis_zset::zstore(const char* cmd, const char* dst, argv[1] = dst; lens[1] = strlen(dst); - char num_s[BUFLEN]; - safe_snprintf(num_s, sizeof(num_s), "%d", (int) num); + char* num_s = (char*) dbuf_->dbuf_alloc(INTLEN); + safe_snprintf(num_s, INTLEN, "%d", (int) num); argv[2] = num_s; lens[2] = strlen(num_s); @@ -1002,8 +1012,8 @@ int redis_zset::zstore(const char* cmd, const char* dst, argv[1] = dst; lens[1] = strlen(dst); - char num_s[INTLEN]; - safe_snprintf(num_s, sizeof(num_s), "%d", (int) keys.size()); + char* num_s = (char*) dbuf_->dbuf_alloc(INTLEN); + safe_snprintf(num_s, INTLEN, "%d", (int) keys.size()); argv[2] = num_s; lens[2] = strlen(num_s); @@ -1201,8 +1211,8 @@ int redis_zset::zpop(const char* cmd, const char* key, argv[1] = key; lens[1] = strlen(key); - char count_s[BUFLEN]; - safe_snprintf(count_s, sizeof(count_s), "%lu", (unsigned long) count); + char* count_s = (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(count_s, BUFLEN, "%lu", (unsigned long) count); argv[2] = count_s; lens[2] = strlen(count_s); argc = 3; @@ -1251,8 +1261,8 @@ int redis_zset::bzpop(const char* cmd, const char* key, size_t timeout, argv[1] = key; lens[1] = strlen(key); - char buf[BUFLEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) timeout); + char* buf = (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(buf, BUFLEN, "%lu", (unsigned long) timeout); argv[2] = buf; lens[2] = strlen(buf); @@ -1278,8 +1288,8 @@ int redis_zset::bzpop(const char* cmd, const std::vector& keys, i++; } - char buf[BUFLEN]; - safe_snprintf(buf, sizeof(buf), "%lu", (unsigned long) timeout); + char* buf = (char*) dbuf_->dbuf_alloc(BUFLEN); + safe_snprintf(buf, BUFLEN, "%lu", (unsigned long) timeout); argv_[i] = buf; argv_lens_[i] = strlen(buf); build_request(argc_, argv_, argv_lens_); diff --git a/lib_acl_cpp/src/stream/server_socket.cpp b/lib_acl_cpp/src/stream/server_socket.cpp index df5048cc5..b152db952 100644 --- a/lib_acl_cpp/src/stream/server_socket.cpp +++ b/lib_acl_cpp/src/stream/server_socket.cpp @@ -89,7 +89,7 @@ bool server_socket::opened(void) const bool server_socket::open(const char* addr) { if (fd_ != ACL_SOCKET_INVALID) { - logger_error("listen fd already opened"); + logger_error("listen fd already opened, fd_=%d", (int) fd_); return true; } diff --git a/lib_fiber/c/CMakeLists.txt b/lib_fiber/c/CMakeLists.txt index 49ccb9dfa..ee20a4d28 100644 --- a/lib_fiber/c/CMakeLists.txt +++ b/lib_fiber/c/CMakeLists.txt @@ -57,6 +57,7 @@ add_definitions( "-Wno-long-long" "-Wuninitialized" # "-DUSE_JMP" +# "-DSHARE_STACK" "-DUSE_FAST_RING" "-D_POSIX_PTHREAD_SEMANTICS" "-DACL_PREPARE_COMPILE" diff --git a/lib_fiber/c/Makefile b/lib_fiber/c/Makefile index ede1692af..7ad94aae4 100644 --- a/lib_fiber/c/Makefile +++ b/lib_fiber/c/Makefile @@ -7,7 +7,9 @@ ARFL = rv #ARFL = cru RANLIB = ${ENV_RANLIB} +#JMP_CTX = DUMMY JMP_CTX = USE_JMP_DEF +#JMP_CTX = USE_BOOST_JMP CFLAGS = -c -g -W \ -std=gnu99 \ @@ -22,8 +24,7 @@ CFLAGS = -c -g -W \ -Wmissing-prototypes \ -Wcast-qual \ -DUSE_FAST_RING \ -#-DUSE_JMP_DEF \ -#-DUSE_BOOST_JMP \ +#-DSHARE_STACK \ #-DUSE_VALGRIND \ #-DUSE_FAST_TIME \ #-Waggregate-return \ @@ -70,10 +71,10 @@ ifeq ($(findstring gcc, $(CC)), gcc) GCC_MINOR:=$(shell echo "$(GCC_VERSION)" | cut -d'.' -f2) GCC_SUB:=$(shell echo "$(GCC_VERSION)" | cut -d'.' -f3) GCC_VER:=$(shell [ $(GCC_MAJOR) -gt 4 -o \( $(GCC_MAJOR) -eq 4 -a $(GCC_MINOR) -gt 4 \) ] && echo true) - ifeq ($(GCC_VER), true) - CFLAGS += -Wno-implicit-fallthrough - CFLAGS += -Wno-absolute-value - endif +# ifeq ($(GCC_VER), true) +# CFLAGS += -Wno-implicit-fallthrough +# CFLAGS += -Wno-absolute-value +# endif endif ifeq ($(findstring clang, $(CC)), clang) diff --git a/lib_fiber/c/include/fiber/fiber_base.h b/lib_fiber/c/include/fiber/fiber_base.h index fc0e58be6..ae88230b8 100644 --- a/lib_fiber/c/include/fiber/fiber_base.h +++ b/lib_fiber/c/include/fiber/fiber_base.h @@ -8,7 +8,18 @@ extern "C" { #endif -typedef ACL_FIBER *((*FIBER_ALLOC_FN)(void (*)(ACL_FIBER *), size_t)); +typedef struct ACL_FIBER_ATTR { + unsigned int oflag; +#define ACL_FIBER_ATTR_SHARE_STACK (unsigned) 1 << 0 + + size_t stack_size; +} ACL_FIBER_ATTR; + +void acl_fiber_attr_init(ACL_FIBER_ATTR *attr); +void acl_fiber_attr_setstacksize(ACL_FIBER_ATTR *attr, size_t size); +void acl_fiber_attr_setsharestack(ACL_FIBER_ATTR *attr, int on); + +typedef ACL_FIBER *((*FIBER_ALLOC_FN)(void (*)(ACL_FIBER *), const ACL_FIBER_ATTR *)); typedef ACL_FIBER *((*FIBER_ORIGIN_FN)(void)); FIBER_API void acl_fiber_register(FIBER_ALLOC_FN alloc_fn, @@ -17,19 +28,31 @@ FIBER_API void acl_fiber_register(FIBER_ALLOC_FN alloc_fn, FIBER_API ACL_FIBER *acl_fiber_alloc(size_t size, void **pptr); /** - * set flag if the system API should be hooked, default value is 1 internal + * Set flag if the system API should be hooked, default value is 1 internal * @param onoff {int} if need to hook the system API */ FIBER_API void acl_fiber_hook_api(int onoff); /** - * set the global flag that if in non-blocking status, just for windows + * Set the global flag that if in non-blocking status, just for windows * @param yes {int} set in non-blocking status global if yes not 0 */ FIBER_API void acl_fiber_set_non_blocking(int yes); /** - * create and start one fiber + * Set the shared stack's memory size in shared stack mode + * @param size {size_t} must more than 1024 for shared stack mode. + */ +FIBER_API void acl_fiber_set_shared_stack_size(size_t size); + +/** + * Get the shared stack's memory size if shared stack was enabled + * @return {size_t} return > 0 if shared stack was enabled, or return 0. + */ +FIBER_API size_t acl_fiber_get_shared_stack_size(void); + +/** + * Create and start one fiber * @param fn {void (*)(ACL_FIBER*, void*)} the callback of fiber running * @param arg {void*} the second parameter of the callback fn * @param size {size_t} the virual memory size of the fiber created @@ -38,46 +61,56 @@ FIBER_API void acl_fiber_set_non_blocking(int yes); FIBER_API ACL_FIBER* acl_fiber_create(void (*fn)(ACL_FIBER*, void*), void* arg, size_t size); +FIBER_API ACL_FIBER* acl_fiber_create2(const ACL_FIBER_ATTR *attr, + void (*fn)(ACL_FIBER*, void*), void* arg); + /** - * get the fibers count in deading status + * Get the fibers count in deading status * @return {unsigned} */ FIBER_API unsigned acl_fiber_ndead(void); /** - * get the fibers count in aliving status + * Get the fibers count in aliving status * @return {unsigned} */ FIBER_API unsigned acl_fiber_number(void); /** - * create one fiber in background for freeing the dead fibers, specify the + * Create one fiber in background for freeing the dead fibers, specify the * maximum fibers in every recyling process * @param max {size_t} the maximum fibers to freed in every recyling process */ FIBER_API void acl_fiber_check_timer(size_t max); /** - * get the current running fiber + * Get the current running fiber * @retur {ACL_FIBER*} if no running fiber NULL will be returned */ FIBER_API ACL_FIBER* acl_fiber_running(void); /** - * get the fiber ID of the specified fiber + * If the fiber using shared stack? + * @param fiber {const ACL_FIBER*} + * @return {int} return 0 if using private stack, or the shared stack was used + */ +FIBER_API int acl_fiber_use_share_stack(const ACL_FIBER *fiber); + +/** + * Get the fiber ID of the specified fiber * @param fiber {const ACL_FIBER*} the specified fiber object * @return {unsigned int} return the fiber ID */ FIBER_API unsigned int acl_fiber_id(const ACL_FIBER* fiber); /** - * get the current running fiber's ID + * Get the current running fiber's ID * @return {unsigned int} the current fiber's ID */ FIBER_API unsigned int acl_fiber_self(void); /** - * set the error number to the specified fiber object + * Set the error number to the specified fiber object * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * fiber will be used * @param errnum {int} the error number @@ -85,7 +118,7 @@ FIBER_API unsigned int acl_fiber_self(void); FIBER_API void acl_fiber_set_errno(ACL_FIBER* fiber, int errnum); /** - * get the error number of assosiated fiber + * Get the error number of assosiated fiber * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * @return {int} get the error number of assosiated fiber */ @@ -99,79 +132,79 @@ FIBER_API int acl_fiber_errno(ACL_FIBER* fiber); FIBER_API void acl_fiber_keep_errno(ACL_FIBER* fiber, int yesno); /** - * get the assosiated fiber's status + * Get the assosiated fiber's status * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * @return {int} */ FIBER_API int acl_fiber_status(const ACL_FIBER* fiber); /** - * kill the suspended fiber and notify it to exit + * Kill the suspended fiber and notify it to exit * @param fiber {const ACL_FIBER*} the specified fiber, NOT NULL */ FIBER_API void acl_fiber_kill(ACL_FIBER* fiber); /** - * check if the specified fiber has been killed + * Check if the specified fiber has been killed * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * @return {int} non zero returned if been killed */ FIBER_API int acl_fiber_killed(ACL_FIBER* fiber); /** - * check if the specified fiber has been signaled + * Check if the specified fiber has been signaled * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * @return {int} non zero returned if been signed */ FIBER_API int acl_fiber_signaled(ACL_FIBER* fiber); /** - * check if the specified fiber's socket has been closed by another fiber + * Check if the specified fiber's socket has been closed by another fiber * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * @return {int} non zero returned if been closed */ FIBER_API int acl_fiber_closed(ACL_FIBER* fiber); /** - * check if the specified fiber has been canceled + * Check if the specified fiber has been canceled * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * @return {int} non zero returned if been canceled */ FIBER_API int acl_fiber_canceled(ACL_FIBER* fiber); /** - * wakeup the suspended fiber with the assosiated signal number + * Wakeup the suspended fiber with the assosiated signal number * @param fiber {const ACL_FIBER*} the specified fiber, NOT NULL * @param signum {int} SIGINT, SIGKILL, SIGTERM ... refer to bits/signum.h */ FIBER_API void acl_fiber_signal(ACL_FIBER* fiber, int signum); /** - * get the signal number got from other fiber + * Get the signal number got from other fiber * @param fiber {ACL_FIBER*} the specified fiber, if NULL the current running * @retur {int} the signal number got */ FIBER_API int acl_fiber_signum(ACL_FIBER* fiber); /** - * suspend the current running fiber + * Suspend the current running fiber * @return {int} */ FIBER_API int acl_fiber_yield(void); /** - * add the suspended fiber into resuming queue + * Add the suspended fiber into resuming queue * @param fiber {ACL_FIBER*} the fiber, NOT NULL */ FIBER_API void acl_fiber_ready(ACL_FIBER* fiber); /** - * suspend the current fiber and switch to run the next ready fiber + * Suspend the current fiber and switch to run the next ready fiber */ FIBER_API void acl_fiber_switch(void); /** - * set the fiber schedule process with automatically, in this way, when one + * Set the fiber schedule process with automatically, in this way, when one * fiber was created, the schedule process will start automatically, but only * the first fiber was started, so you can create the other fibers in this * fiber. The default schedule mode is non-automatically, you should call the @@ -180,13 +213,13 @@ FIBER_API void acl_fiber_switch(void); FIBER_API void acl_fiber_schedule_init(int on); /** - * start the fiber schedule process, the fibers in the ready quque will be + * Start the fiber schedule process, the fibers in the ready quque will be * started in sequence. */ FIBER_API void acl_fiber_schedule(void); /** - * start the fiber schedule process with the specified event type, the default + * Start the fiber schedule process with the specified event type, the default * event type is FIBER_EVENT_KERNEL. acl_fiber_schedule using the default * event type. FIBER_EVENT_KERNEL is diffrent for diffrent OS platform: * Linux: epoll; BSD: kqueue; Windows: iocp. @@ -199,39 +232,39 @@ FIBER_API void acl_fiber_schedule(void); FIBER_API void acl_fiber_schedule_with(int event_mode); /** - * set the event type, the default type is FIBER_EVENT_KERNEL, this function + * Set the event type, the default type is FIBER_EVENT_KERNEL, this function * must be called before acl_fiber_schedule. * @param event_mode {int} the event type, defined as FIBER_EVENT_XXX */ FIBER_API void acl_fiber_schedule_set_event(int event_mode); /** - * check if the current thread is in fiber schedule status + * Check if the current thread is in fiber schedule status * @return {int} non zero returned if in fiber schedule status */ FIBER_API int acl_fiber_scheduled(void); /** - * stop the fiber schedule process, all fibers will be stopped + * Stop the fiber schedule process, all fibers will be stopped */ FIBER_API void acl_fiber_schedule_stop(void); /** - * let the current fiber sleep for a while + * Let the current fiber sleep for a while * @param milliseconds {unsigned int} the milliseconds to sleep * @return {unsigned int} the rest milliseconds returned after wakeup */ FIBER_API unsigned int acl_fiber_delay(unsigned int milliseconds); /** - * let the current fiber sleep for a while + * Let the current fiber sleep for a while * @param seconds {unsigned int} the seconds to sleep * @return {unsigned int} the rest seconds returned after wakeup */ FIBER_API unsigned int acl_fiber_sleep(unsigned int seconds); /** - * create one fiber timer + * Create one fiber timer * @param milliseconds {unsigned int} the timer wakeup milliseconds * @param size {size_t} the virtual memory of the created fiber * @param fn {void (*)(ACL_FIBER*, void*)} the callback when fiber wakeup @@ -242,23 +275,23 @@ FIBER_API ACL_FIBER* acl_fiber_create_timer(unsigned int milliseconds, size_t size, void (*fn)(ACL_FIBER*, void*), void* ctx); /** - * reset the timer milliseconds time before the timer fiber wakeup + * Reset the timer milliseconds time before the timer fiber wakeup * @param timer {ACL_FIBER*} the fiber created by acl_fiber_create_timer * @param milliseconds {unsigned int} the new timer wakeup milliseconds */ FIBER_API void acl_fiber_reset_timer(ACL_FIBER* timer, unsigned int milliseconds); /** - * set the DNS service addr + * Set the DNS service addr * @param ip {const char*} ip of the DNS service * @param port {int} port of the DNS service */ FIBER_API void acl_fiber_set_dns(const char* ip, int port); -/* for fiber specific */ +/* For fiber specific */ /** - * set the current fiber's local object + * Set the current fiber's local object * @param key {int*} the addr of indexed key, its initial value should <= 0, * and one integer which > 0 will be set for it; the fiber local object will * be assosiated with the indexed key. @@ -271,7 +304,7 @@ FIBER_API void acl_fiber_set_dns(const char* ip, int port); FIBER_API int acl_fiber_set_specific(int* key, void* ctx, void (*free_fn)(void*)); /** - * get the current fiber's local object assosiated with the specified indexed key + * Get the current fiber's local object assosiated with the specified indexed key * @param key {int} the integer value returned by acl_fiber_set_specific * @retur {void*} NULL returned if no fiber local object with the specified key */ @@ -280,7 +313,7 @@ FIBER_API void* acl_fiber_get_specific(int key); /****************************************************************************/ /** - * log function type used in fiber logging process, should be set by the + * Log function type used in fiber logging process, should be set by the * function acl_fiber_msg_pre_write * @param ctx {void*} the user's context * @param fmt {const char*} format of parameters @@ -289,7 +322,7 @@ FIBER_API void* acl_fiber_get_specific(int key); typedef void (*FIBER_MSG_PRE_WRITE_FN)(void *ctx, const char *fmt, va_list ap); /** - * log function type used in fiber logging process, should be set by the + * Log function type used in fiber logging process, should be set by the * function acl_fiber_msg_register. This can be used by user for get the * logging information of fiber * @param ctx {void*} the user's context @@ -299,44 +332,44 @@ typedef void (*FIBER_MSG_PRE_WRITE_FN)(void *ctx, const char *fmt, va_list ap); typedef void (*FIBER_MSG_WRITE_FN) (void *ctx, const char *fmt, va_list ap); /** - * set the user's log saving function when process started + * Set the user's log saving function when process started * @param write_fn {MSG_WRITE_FN} log function defined by the user * @param ctx {void*} parameter will be transfered to write_fn */ FIBER_API void acl_fiber_msg_register(FIBER_MSG_WRITE_FN write_fn, void *ctx); /** - * cleanup the registered log callback by acl_fiber_msg_register + * Cleanup the registered log callback by acl_fiber_msg_register */ FIBER_API void acl_fiber_msg_unregister(void); /** - * register the user's callback + * Register the user's callback * @param pre_write {MSG_PRE_WRITE_FN} * @param ctx {void*} */ FIBER_API void acl_fiber_msg_pre_write(FIBER_MSG_PRE_WRITE_FN pre_write, void *ctx); /** - * if showing the fiber schedule process's log to stdout + * If showing the fiber schedule process's log to stdout * @param onoff {int} log will be showed to stdout if onoff isn't 0 */ FIBER_API void acl_fiber_msg_stdout_enable(int onoff); /** - * get the system error number of last system API calling + * Get the system error number of last system API calling * @return {int} error number */ FIBER_API int acl_fiber_last_error(void); /** - * get the error information of last system API calling + * Get the error information of last system API calling * @return {const char*} */ FIBER_API const char *acl_fiber_last_serror(void); /** - * convert errno to string + * Convert errno to string * @param errnum {int} * @param buf {char*} hold the result * @param size {size_t} buf's size @@ -345,13 +378,13 @@ FIBER_API const char *acl_fiber_last_serror(void); FIBER_API const char *acl_fiber_strerror(int errnum, char *buf, size_t size); /** - * set the system error number + * Set the system error number * @param errnum {int} the error number */ FIBER_API void acl_fiber_set_error(int errnum); /** - * set the fd limit for the current process + * Set the fd limit for the current process * @param limit {int} the fd limit to be set * @return {int} the real fd limit will be returned */ diff --git a/lib_fiber/c/include/fiber/fiber_channel.h b/lib_fiber/c/include/fiber/fiber_channel.h index de8cc961f..08ecf004d 100644 --- a/lib_fiber/c/include/fiber/fiber_channel.h +++ b/lib_fiber/c/include/fiber/fiber_channel.h @@ -7,15 +7,15 @@ extern "C" { #endif -/* channel communication */ +/* Channel communication */ /** - * the fiber channel type definition + * The fiber channel type definition */ typedef struct ACL_CHANNEL ACL_CHANNEL; /** - * create one fiber channel + * Create one fiber channel * @param elemsize {int} the fixed object size transfered in fiber channel * @param bufsize {int} the buffered of objects in fiber channel * @return {ACL_CHANNNEL*} @@ -23,13 +23,13 @@ typedef struct ACL_CHANNEL ACL_CHANNEL; FIBER_API ACL_CHANNEL* acl_channel_create(int elemsize, int bufsize); /** - * free fiber channel created by acl_channel_create + * Free fiber channel created by acl_channel_create * @param c {ACL_CHANNEL*} created by acl_channel_create */ FIBER_API void acl_channel_free(ACL_CHANNEL* c); /** - * send object to specified fiber channel in block mode + * Send object to specified fiber channel in block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @param v {void*} the object to be transfered * @return {int} value (>= 0) returned @@ -37,7 +37,7 @@ FIBER_API void acl_channel_free(ACL_CHANNEL* c); FIBER_API int acl_channel_send(ACL_CHANNEL* c, void* v); /** - * send object to specified fiber channel in non-block mode, one new object + * Send object to specified fiber channel in non-block mode, one new object * copied from which will be created internal * @param c {ACL_CHANNEL*} created by acl_channel_create * @param v {void*} the object to be transfered @@ -46,7 +46,7 @@ FIBER_API int acl_channel_send(ACL_CHANNEL* c, void* v); FIBER_API int acl_channel_send_nb(ACL_CHANNEL* c, void* v); /** - * read one object from specified channel in block mode + * Read one object from specified channel in block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @param v {void*} will store the result * @return {int} value(>= 0) returned if get one object @@ -54,7 +54,7 @@ FIBER_API int acl_channel_send_nb(ACL_CHANNEL* c, void* v); FIBER_API int acl_channel_recv(ACL_CHANNEL* c, void* v); /** - * read one object from specified channel in non-block ode + * Read one object from specified channel in non-block ode * @param c {ACL_CHANNEL*} created by acl_channel_create * @param v {void*} will store the result * @return {int} value(>= 0) returned if get one object, or NULL returned if @@ -63,7 +63,7 @@ FIBER_API int acl_channel_recv(ACL_CHANNEL* c, void* v); FIBER_API int acl_channel_recv_nb(ACL_CHANNEL* c, void* v); /** - * send object's addr to specified channel in block mode + * Send object's addr to specified channel in block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @param v {void*} the addr of the object to be transfered * @return {int} value (>= 0) returned @@ -71,14 +71,14 @@ FIBER_API int acl_channel_recv_nb(ACL_CHANNEL* c, void* v); FIBER_API int acl_channel_sendp(ACL_CHANNEL* c, void* v); /** - * get object's addr from specified channel in block mode + * Get object's addr from specified channel in block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @return {void*} non-NULL addr returned */ FIBER_API void *acl_channel_recvp(ACL_CHANNEL* c); /** - * send the object's addr to specified channel in non-block mode + * Send the object's addr to specified channel in non-block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @param v {void*} the addr of the object to be transfered * @return {int} value which is >= 0 returned @@ -86,14 +86,14 @@ FIBER_API void *acl_channel_recvp(ACL_CHANNEL* c); FIBER_API int acl_channel_sendp_nb(ACL_CHANNEL* c, void* v); /** - * get the object's addr form specified channel in non-block mode + * Get the object's addr form specified channel in non-block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @return {void*} * non-NULL returned when got one, or NULL returned */ FIBER_API void *acl_channel_recvp_nb(ACL_CHANNEL* c); /** - * send unsigned integer to specified channel in block mode + * Send unsigned integer to specified channel in block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @param val {unsigned long} the integer to be sent * @return {int} value (>= 0) returned @@ -101,14 +101,14 @@ FIBER_API void *acl_channel_recvp_nb(ACL_CHANNEL* c); FIBER_API int acl_channel_sendul(ACL_CHANNEL* c, unsigned long val); /** - * get unsigned integer from specified channel in block mode + * Get unsigned integer from specified channel in block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @return {unsigned long} */ FIBER_API unsigned long acl_channel_recvul(ACL_CHANNEL* c); /** - * sent unsigned integer to specified channel in non-block mode + * Sent unsigned integer to specified channel in non-block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @param val {unsigned long} integer to be sent * @return {int} value(>= 0) returned @@ -116,7 +116,7 @@ FIBER_API unsigned long acl_channel_recvul(ACL_CHANNEL* c); FIBER_API int acl_channel_sendul_nb(ACL_CHANNEL* c, unsigned long val); /** - * get one unsigned integer from specified channel in non-block mode + * Get one unsigned integer from specified channel in non-block mode * @param c {ACL_CHANNEL*} created by acl_channel_create * @return {unsigned long} */ diff --git a/lib_fiber/c/include/fiber/fiber_cond.h b/lib_fiber/c/include/fiber/fiber_cond.h index 95d541bad..053b72b3d 100644 --- a/lib_fiber/c/include/fiber/fiber_cond.h +++ b/lib_fiber/c/include/fiber/fiber_cond.h @@ -11,26 +11,26 @@ extern "C" { /* fiber_cond.h */ /** - * fiber_cond object look like pthread_cond_t which is used between threads + * Fiber_cond object look like pthread_cond_t which is used between threads * and fibers */ typedef struct ACL_FIBER_COND ACL_FIBER_COND; /** - * create fiber cond which can be used in fibers more or threads mode + * Create fiber cond which can be used in fibers more or threads mode * @param flag {unsigned} current not used, just for the future extend * @return {ACL_FIBER_COND *} */ FIBER_API ACL_FIBER_COND *acl_fiber_cond_create(unsigned flag); /** - * free cond created by acl_fiber_cond_create + * Free cond created by acl_fiber_cond_create * @param cond {ACL_FIBER_COND *} */ FIBER_API void acl_fiber_cond_free(ACL_FIBER_COND *cond); /** - * wait for cond event to be signaled + * Wait for cond event to be signaled * @param cond {ACL_FIBER_COND *} * @param event {ACL_FIBER_EVENT *} must be owned by the current caller * @return {int} return 0 if ok or return error value @@ -38,7 +38,7 @@ FIBER_API void acl_fiber_cond_free(ACL_FIBER_COND *cond); FIBER_API int acl_fiber_cond_wait(ACL_FIBER_COND *cond, ACL_FIBER_EVENT *event); /** - * wait for cond event to be signaled with the specified timeout + * Wait for cond event to be signaled with the specified timeout * @param cond {ACL_FIBER_COND *} * @return {int} return 0 if ok or return error value, when timedout ETIMEDOUT * will be returned @@ -47,7 +47,7 @@ FIBER_API int acl_fiber_cond_timedwait(ACL_FIBER_COND *cond, ACL_FIBER_EVENT *event, int delay_ms); /** - * signle the cond which will wakeup one waiter for the cond to be signaled + * Signal the cond which will wakeup one waiter for the cond to be signaled * @param cond {ACL_FIBER_COND *} * @return {int} return 0 if ok or return error value */ diff --git a/lib_fiber/c/include/fiber/fiber_define.h b/lib_fiber/c/include/fiber/fiber_define.h index 08e8cac6f..a7d6ee454 100644 --- a/lib_fiber/c/include/fiber/fiber_define.h +++ b/lib_fiber/c/include/fiber/fiber_define.h @@ -95,7 +95,7 @@ typedef int socket_t; #endif /** - * the fiber struct type definition + * The fiber struct type definition */ typedef struct ACL_FIBER ACL_FIBER; diff --git a/lib_fiber/c/include/fiber/fiber_event.h b/lib_fiber/c/include/fiber/fiber_event.h index 8e0e0e872..25cbe0647 100644 --- a/lib_fiber/c/include/fiber/fiber_event.h +++ b/lib_fiber/c/include/fiber/fiber_event.h @@ -10,13 +10,13 @@ extern "C" { /* fiber_event.c */ /** - * fiber event mutex object based on IO event, which is thread safety. That's + * Fiber event mutex object based on IO event, which is thread safety. That's * to say one event object can used in different threads */ typedef struct ACL_FIBER_EVENT ACL_FIBER_EVENT; /** - * when the fiber_event is used in multiple threads for sync, if there're + * When the fiber_event is used in multiple threads for sync, if there're * many threads, the flag FIBER_FLAG_USE_MUTEX should be set to avoid internal * thundering herd which maybe happen by using atomic; if the threads' number * is less than one hundred, the flag FIBER_FLAG_USE_MUTEX needn't be set @@ -24,40 +24,40 @@ typedef struct ACL_FIBER_EVENT ACL_FIBER_EVENT; #define FIBER_FLAG_USE_MUTEX (1 << 0) /** - * if this flag is set, msg_fatal will be used other msg_error when error + * If this flag is set, msg_fatal will be used other msg_error when error * happened, this flag is optional for users */ #define FIBER_FLAG_USE_FATAL (1 << 1) /** - * create fiber event mutex which can be used in fibers mode or threads mode + * Create fiber event mutex which can be used in fibers mode or threads mode * @param flag {unsigned} define as FIBER_FLAG_XXX above * @return {ACL_FIBER_EVENT *} */ FIBER_API ACL_FIBER_EVENT *acl_fiber_event_create(unsigned flag); /** - * free event mutex returned by acl_fiber_event_create + * Free event mutex returned by acl_fiber_event_create * @param {ACL_FIBER_EVENT *} */ FIBER_API void acl_fiber_event_free(ACL_FIBER_EVENT *event); /** - * wait for event can be available + * Wait for event can be available * @param {ACL_FIBER_EVENT *} * @return {int} 0 returned if successful, or -1 if error happened */ FIBER_API int acl_fiber_event_wait(ACL_FIBER_EVENT *event); /** - * try to wait for event can be available + * Try to wait for event can be available * @param {ACL_FIBER_EVENT *} * @return {int} 0 returned if successful, or -1 if the event been locked */ FIBER_API int acl_fiber_event_trywait(ACL_FIBER_EVENT *event); /** - * the event's owner notify the waiters that the event mutex can be available, + * The event's owner notify the waiters that the event mutex can be available, * and the waiter will get the event mutex * @param {ACL_FIBER_EVENT *} * @return {int} 0 returned if successful, or -1 if error happened diff --git a/lib_fiber/c/include/fiber/fiber_lock.h b/lib_fiber/c/include/fiber/fiber_lock.h index 4c69cd7b3..dd6884bfb 100644 --- a/lib_fiber/c/include/fiber/fiber_lock.h +++ b/lib_fiber/c/include/fiber/fiber_lock.h @@ -7,40 +7,40 @@ extern "C" { #endif -/* fiber locking */ +/* Fiber locking */ /** - * fiber mutex, thread unsafety, one fiber mutex can only be used in the + * Fiber mutex, thread unsafety, one fiber mutex can only be used in the * same thread, otherwise the result is unpredictable */ typedef struct ACL_FIBER_MUTEX ACL_FIBER_MUTEX; /** - * fiber read/write mutex, thread unsafety, can only be used in the sampe thread + * Fiber read/write mutex, thread unsafety, can only be used in the sampe thread */ typedef struct ACL_FIBER_RWLOCK ACL_FIBER_RWLOCK; /** - * create one fiber mutex, can only be used in the same thread + * Create one fiber mutex, can only be used in the same thread * @return {ACL_FIBER_MUTEX*} fiber mutex returned */ FIBER_API ACL_FIBER_MUTEX* acl_fiber_mutex_create(void); /** - * free fiber mutex created by acl_fiber_mutex_create + * Free fiber mutex created by acl_fiber_mutex_create * @param l {ACL_FIBER_MUTEX*} created by acl_fiber_mutex_create */ FIBER_API void acl_fiber_mutex_free(ACL_FIBER_MUTEX* l); /** - * lock the specified fiber mutex, return immediately when locked, or will + * Lock the specified fiber mutex, return immediately when locked, or will * wait until the mutex can be used * @param l {ACL_FIBER_MUTEX*} created by acl_fiber_mutex_create */ FIBER_API void acl_fiber_mutex_lock(ACL_FIBER_MUTEX* l); /** - * try lock the specified fiber mutex, return immediately no matter the mutex + * Try lock the specified fiber mutex, return immediately no matter the mutex * can be locked. * @param l {ACL_FIBER_MUTEX*} created by acl_fiber_mutex_create * @return {int} 0 returned when locking successfully, -1 when locking failed @@ -48,7 +48,7 @@ FIBER_API void acl_fiber_mutex_lock(ACL_FIBER_MUTEX* l); FIBER_API int acl_fiber_mutex_trylock(ACL_FIBER_MUTEX* l); /** - * the fiber mutex be unlock by its owner fiber, fatal will happen when others + * The fiber mutex be unlock by its owner fiber, fatal will happen when others * release the fiber mutex * @param l {ACL_FIBER_MUTEX*} created by acl_fiber_mutex_create */ @@ -57,19 +57,19 @@ FIBER_API void acl_fiber_mutex_unlock(ACL_FIBER_MUTEX* l); /****************************************************************************/ /** - * create one fiber rwlock, can only be operated in the sampe thread + * Create one fiber rwlock, can only be operated in the sampe thread * @return {ACL_FIBER_RWLOCK*} */ FIBER_API ACL_FIBER_RWLOCK* acl_fiber_rwlock_create(void); /** - * free rw mutex created by acl_fiber_rwlock_create + * Free rw mutex created by acl_fiber_rwlock_create * @param l {ACL_FIBER_RWLOCK*} created by acl_fiber_rwlock_create */ FIBER_API void acl_fiber_rwlock_free(ACL_FIBER_RWLOCK* l); /** - * lock the rwlock, if there is no any write locking on it, the + * Lock the rwlock, if there is no any write locking on it, the * function will return immediately; otherwise, the caller will wait for all * write locking be released. Read lock on it will successful when returning * @param l {ACL_FIBER_RWLOCK*} created by acl_fiber_rwlock_create @@ -77,7 +77,7 @@ FIBER_API void acl_fiber_rwlock_free(ACL_FIBER_RWLOCK* l); FIBER_API void acl_fiber_rwlock_rlock(ACL_FIBER_RWLOCK* l); /** - * try to locking the Readonly lock, return immediately no matter locking + * Try to locking the Readonly lock, return immediately no matter locking * is successful. * @param l {ACL_FIBER_RWLOCK*} crated by acl_fiber_rwlock_create * @retur {int} 1 returned when successfully locked, or 0 returned if locking @@ -86,13 +86,13 @@ FIBER_API void acl_fiber_rwlock_rlock(ACL_FIBER_RWLOCK* l); FIBER_API int acl_fiber_rwlock_tryrlock(ACL_FIBER_RWLOCK* l); /** - * lock the rwlock in Write Lock mode, return until no any one locking it + * Lock the rwlock in Write Lock mode, return until no any one locking it * @param l {ACL_FIBER_RWLOCK*} created by acl_fiber_rwlock_create */ FIBER_API void acl_fiber_rwlock_wlock(ACL_FIBER_RWLOCK* l); /** - * try to lock the rwlock in Write Lock mode. return immediately no matter + * Try to lock the rwlock in Write Lock mode. return immediately no matter * locking is successful. * @param l {ACL_FIBER_RWLOCK*} created by acl_fiber_rwlock_create * @return {int} 1 returned when locking successfully, or 0 returned when @@ -101,13 +101,13 @@ FIBER_API void acl_fiber_rwlock_wlock(ACL_FIBER_RWLOCK* l); FIBER_API int acl_fiber_rwlock_trywlock(ACL_FIBER_RWLOCK* l); /** - * the rwlock's Read-Lock owner unlock the rwlock + * The rwlock's Read-Lock owner unlock the rwlock * @param l {ACL_FIBER_RWLOCK*} crated by acl_fiber_rwlock_create */ FIBER_API void acl_fiber_rwlock_runlock(ACL_FIBER_RWLOCK* l); /** - * the rwlock's Write-Lock owner unlock the rwlock + * The rwlock's Write-Lock owner unlock the rwlock * @param l {ACL_FIBER_RWLOCK*} created by acl_fiber_rwlock_create */ FIBER_API void acl_fiber_rwlock_wunlock(ACL_FIBER_RWLOCK* l); diff --git a/lib_fiber/c/include/fiber/fiber_sem.h b/lib_fiber/c/include/fiber/fiber_sem.h index a79116bb4..ae4842a0a 100644 --- a/lib_fiber/c/include/fiber/fiber_sem.h +++ b/lib_fiber/c/include/fiber/fiber_sem.h @@ -7,26 +7,26 @@ extern "C" { #endif -/* fiber semaphore, thread unsafety, one semaphore can only be used in one +/* Fiber semaphore, thread unsafety, one semaphore can only be used in one * thread, if used in different threads, result is unpredictable */ typedef struct ACL_FIBER_SEM ACL_FIBER_SEM; /** - * create one fiber semaphore, and binding it with the current thread + * Create one fiber semaphore, and binding it with the current thread * @param num {int} the initial value of the semaphore, must >= 0 * @return {ACL_FIBER_SEM *} */ FIBER_API ACL_FIBER_SEM* acl_fiber_sem_create(int num); /** - * free fiber semaphore + * Free fiber semaphore * @param {ACL_FIBER_SEM *} */ FIBER_API void acl_fiber_sem_free(ACL_FIBER_SEM* sem); /** - * get the thread binding the specificed fiber sem + * Get the thread binding the specificed fiber sem * @param sem {ACL_FIBER_SEM*} created by acl_fiber_sem_create * @return {unsigned long} thread ID of the thread binding the semaphore */ @@ -35,7 +35,7 @@ FIBER_API unsigned long acl_fiber_sem_get_tid(ACL_FIBER_SEM* sem); #endif /** - * set the thread ID the semaphore belongs to, changing the owner of the fiber + * Set the thread ID the semaphore belongs to, changing the owner of the fiber * semaphore, when this function was called, the value of the semaphore must * be zero, otherwise fatal will happen. * @param sem {ACL_FIBER_SEM*} created by acl_fiber_sem_create @@ -44,7 +44,7 @@ FIBER_API unsigned long acl_fiber_sem_get_tid(ACL_FIBER_SEM* sem); FIBER_API void acl_fiber_sem_set_tid(ACL_FIBER_SEM* sem, unsigned long tid); /** - * wait for semaphore until > 0, semaphore will be -1 when returned + * Wait for semaphore until > 0, semaphore will be -1 when returned * @param sem {ACL_FIBER_SEM *} created by acl_fiber_sem_create * @return {int} the semaphore value returned, if the caller's thread isn't * same as the semaphore owner's thread, -1 will be returned @@ -52,7 +52,7 @@ FIBER_API void acl_fiber_sem_set_tid(ACL_FIBER_SEM* sem, unsigned long tid); FIBER_API int acl_fiber_sem_wait(ACL_FIBER_SEM* sem); /** - * try to wait semaphore until > 0, if semaphore is 0, -1 returned immediately, + * Try to wait semaphore until > 0, if semaphore is 0, -1 returned immediately, * otherwise semaphore will be decreased 1 and the semaphore's value is returned * @param sem {ACL_FIBER_SEM *} created by acl_fiber_sem_create * @return {int} value(>=0) returned when waiting ok, otherwise -1 will be @@ -62,7 +62,7 @@ FIBER_API int acl_fiber_sem_wait(ACL_FIBER_SEM* sem); FIBER_API int acl_fiber_sem_trywait(ACL_FIBER_SEM* sem); /** - * add 1 to the semaphore, if there are other fibers waiting for semaphore, + * Add 1 to the semaphore, if there are other fibers waiting for semaphore, * one waiter will be wakeuped * @param sem {ACL_FIBER_SEM *} created by acl_fiber_sem_create * @return {int} the current semaphore value returned, -1 returned if the @@ -71,7 +71,7 @@ FIBER_API int acl_fiber_sem_trywait(ACL_FIBER_SEM* sem); FIBER_API int acl_fiber_sem_post(ACL_FIBER_SEM* sem); /** - * get the specificed semaphore's value + * Get the specificed semaphore's value * @param sem {ACL_FIBER_SEM*} created by acl_fiber_sem_create * @retur {int} current semaphore's value returned */ diff --git a/lib_fiber/c/src/common/msg.c b/lib_fiber/c/src/common/msg.c index 44fde8c55..01baa8f09 100644 --- a/lib_fiber/c/src/common/msg.c +++ b/lib_fiber/c/src/common/msg.c @@ -49,19 +49,23 @@ void msg_info(const char *fmt,...) va_start (ap, fmt); if (__pre_write_fn) { - __pre_write_fn(__pre_write_ctx, fmt, ap); + va_list ap_tmp; + va_copy(ap_tmp, ap); + __pre_write_fn(__pre_write_ctx, fmt, ap_tmp); + } + + if (__stdout_enable) { + va_list ap_tmp; + va_copy(ap_tmp, ap); + printf("msg_info->pid(%d), ", GETPID()); + vprintf(fmt, ap_tmp); + printf("\r\n"); } if (__write_fn != NULL) { __write_fn(__msg_ctx, fmt, ap); } - if (__stdout_enable) { - printf("msg_info->pid(%d), ", GETPID()); - vprintf(fmt, ap); - printf("\r\n"); - } - va_end (ap); } @@ -72,18 +76,22 @@ void msg_warn(const char *fmt,...) va_start (ap, fmt); if (__pre_write_fn) { - __pre_write_fn(__pre_write_ctx, fmt, ap); + va_list ap_tmp; + va_copy(ap_tmp, ap); + __pre_write_fn(__pre_write_ctx, fmt, ap_tmp); + } + + if (__stdout_enable) { + va_list ap_tmp; + va_copy(ap_tmp, ap); + printf("msg_warn->pid(%d), ", GETPID()); + vprintf(fmt, ap_tmp); + printf("\r\n"); } if (__write_fn != NULL) { __write_fn(__msg_ctx, fmt, ap); } - - if (__stdout_enable) { - printf("msg_warn->pid(%d), ", GETPID()); - vprintf(fmt, ap); - printf("\r\n"); - } va_end (ap); } @@ -95,18 +103,22 @@ void msg_error(const char *fmt,...) va_start (ap, fmt); if (__pre_write_fn) { - __pre_write_fn(__pre_write_ctx, fmt, ap); + va_list ap_tmp; + va_copy(ap_tmp, ap); + __pre_write_fn(__pre_write_ctx, fmt, ap_tmp); + } + + if (__stdout_enable) { + va_list ap_tmp; + va_copy(ap_tmp, ap); + printf("msg_error->pid(%d), ", GETPID()); + vprintf(fmt, ap_tmp); + printf("\r\n"); } if (__write_fn != NULL) { __write_fn(__msg_ctx, fmt, ap); } - - if (__stdout_enable) { - printf("msg_error->pid(%d), ", GETPID()); - vprintf(fmt, ap); - printf("\r\n"); - } va_end (ap); } @@ -118,19 +130,23 @@ void msg_fatal(const char *fmt,...) va_start (ap, fmt); if (__pre_write_fn) { - __pre_write_fn(__pre_write_ctx, fmt, ap); + va_list ap_tmp; + va_copy(ap_tmp, ap); + __pre_write_fn(__pre_write_ctx, fmt, ap_tmp); + } + + if (__stdout_enable) { + va_list ap_tmp; + va_copy(ap_tmp, ap); + printf("msg_fatal->pid(%d), ", GETPID()); + printf("fatal:"); + vprintf(fmt, ap_tmp); + printf("\r\n"); } if (__write_fn != NULL) { __write_fn(__msg_ctx, fmt, ap); } - - if (__stdout_enable) { - printf("msg_fatal->pid(%d), ", GETPID()); - printf("fatal:"); - vprintf(fmt, ap); - printf("\r\n"); - } va_end (ap); abort(); diff --git a/lib_fiber/c/src/dns/dns.c.bak b/lib_fiber/c/src/dns/dns.c.bak deleted file mode 100644 index 274131b53..000000000 --- a/lib_fiber/c/src/dns/dns.c.bak +++ /dev/null @@ -1,10129 +0,0 @@ -/* ========================================================================== - * dns.c - Recursive, Reentrant DNS Resolver. - * -------------------------------------------------------------------------- - * Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the - * following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN - * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * ========================================================================== - */ -#include "stdafx.h" - -#ifdef SYS_UNIX - -#include "fiber/libfiber.h" -#include "common.h" - -#if !defined(__FreeBSD__) && !defined(__sun) -#ifndef _XOPEN_SOURCE -#define _XOPEN_SOURCE 600 -#endif - -#undef _DEFAULT_SOURCE -#define _DEFAULT_SOURCE - -#undef _BSD_SOURCE -#define _BSD_SOURCE - -#undef _DARWIN_C_SOURCE -#define _DARWIN_C_SOURCE - -#undef _NETBSD_SOURCE -#define _NETBSD_SOURCE -#endif - -#include /* INT_MAX */ -#include /* offsetof() */ -#ifdef _WIN32 -#define uint32_t unsigned int -#else -#include /* uint32_t */ -#endif -#include /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */ -#include /* FILE fopen(3) fclose(3) getc(3) rewind(3) */ -#include /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */ -#include /* strcasecmp(3) strncasecmp(3) */ -#include /* isspace(3) isdigit(3) */ -#include /* time_t time(2) difftime(3) */ -#include /* SIGPIPE sigemptyset(3) sigaddset(3) sigpending(2) sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) */ -#include /* errno EINVAL ENOENT */ -#undef NDEBUG -#include /* assert(3) */ - -#if _WIN32 -#ifndef FD_SETSIZE -#define FD_SETSIZE 256 -#endif -#include -#include -#else -#include /* FD_SETSIZE socklen_t */ -#include /* FD_ZERO FD_SET fd_set select(2) */ -#include /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */ -#if defined(AF_UNIX) -#include /* struct sockaddr_un */ -#endif -#include /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */ -#include /* _POSIX_THREADS gethostname(3) close(2) */ -#include /* POLLIN POLLOUT */ -#include /* struct sockaddr_in struct sockaddr_in6 */ -#include /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */ -#include /* struct addrinfo */ -#endif - -#include "dns.h" - - -/* - * C O M P I L E R V E R S I O N & F E A T U R E D E T E C T I O N - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) -#define DNS_GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && DNS_GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= DNS_GNUC_2VER((M), (m), (p))) - -#define DNS_MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p)) -#define DNS_MSC_PREREQ(M, m, p) (_MSC_VER_FULL > 0 && _MSC_VER_FULL >= DNS_MSC_2VER((M), (m), (p))) - -#define DNS_SUNPRO_PREREQ(M, m, p) (__SUNPRO_C > 0 && __SUNPRO_C >= 0x ## M ## m ## p) - -#if defined __has_builtin -#define dns_has_builtin(x) __has_builtin(x) -#else -#define dns_has_builtin(x) 0 -#endif - -#if defined __has_extension -#define dns_has_extension(x) __has_extension(x) -#else -#define dns_has_extension(x) 0 -#endif - -#ifndef HAVE___ASSUME -#define HAVE___ASSUME DNS_MSC_PREREQ(8,0,0) -#endif - -#ifndef HAVE___BUILTIN_TYPES_COMPATIBLE_P -#define HAVE___BUILTIN_TYPES_COMPATIBLE_P (DNS_GNUC_PREREQ(3,1,1) || __clang__) -#endif - -#ifndef HAVE___BUILTIN_UNREACHABLE -#define HAVE___BUILTIN_UNREACHABLE (DNS_GNUC_PREREQ(4,5,0) || dns_has_builtin(__builtin_unreachable)) -#endif - -#ifndef HAVE_PRAGMA_MESSAGE -#define HAVE_PRAGMA_MESSAGE (DNS_GNUC_PREREQ(4,4,0) || __clang__ || _MSC_VER) -#endif - - -/* - * C O M P I L E R A N N O T A T I O N S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#if __GNUC__ -#define DNS_NOTUSED __attribute__((unused)) -#define DNS_NORETURN __attribute__((noreturn)) -#else -#define DNS_NOTUSED -#define DNS_NORETURN -#endif - -#if __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#elif DNS_GNUC_PREREQ(4,6,0) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif - - -/* - * S T A N D A R D M A C R O S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#if HAVE___BUILTIN_TYPES_COMPATIBLE_P -#define dns_same_type(a, b, def) __builtin_types_compatible_p(__typeof__ (a), __typeof__ (b)) -#else -#define dns_same_type(a, b, def) (def) -#endif -#define dns_isarray(a) (!dns_same_type((a), (&(a)[0]), 0)) -/* NB: "_" field silences Sun Studio "zero-sized struct/union" error diagnostic */ -#define dns_inline_assert(cond) ((void)(sizeof (struct { int:-!(cond); int _; }))) - -#if HAVE___ASSUME -#define dns_assume(cond) __assume(cond) -#elif HAVE___BUILTIN_UNREACHABLE -#define dns_assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) -#else -#define dns_assume(cond) do { (void)(cond); } while (0) -#endif - -#ifndef lengthof -#define lengthof(a) (dns_inline_assert(dns_isarray(a)), (sizeof (a) / sizeof (a)[0])) -#endif - -#ifndef endof -#define endof(a) (dns_inline_assert(dns_isarray(a)), &(a)[lengthof((a))]) -#endif - - -/* - * M I S C E L L A N E O U S C O M P A T - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#if _WIN32 || _WIN64 -#define PRIuZ "Iu" -#else -#define PRIuZ "zu" -#endif - -#ifndef DNS_THREAD_SAFE -#if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0 -#define DNS_THREAD_SAFE 1 -#else -#define DNS_THREAD_SAFE 0 -#endif -#endif - -#ifndef HAVE__STATIC_ASSERT -#define HAVE__STATIC_ASSERT \ - (dns_has_extension(c_static_assert) || DNS_GNUC_PREREQ(4,6,0) || \ - __C11FEATURES__ || __STDC_VERSION__ >= 201112L) -#endif - -#ifndef HAVE_STATIC_ASSERT -#if (defined static_assert) && \ - (!DNS_GNUC_PREREQ(0,0,0) || DNS_GNUC_PREREQ(4,6,0)) /* glibc doesn't check GCC version */ -#define HAVE_STATIC_ASSERT 1 -#else -#define HAVE_STATIC_ASSERT 0 -#endif -#endif - -#if HAVE_STATIC_ASSERT -#define dns_static_assert(cond, msg) static_assert(cond, msg) -#elif HAVE__STATIC_ASSERT -#define dns_static_assert(cond, msg) _Static_assert(cond, msg) -#else -#define dns_static_assert(cond, msg) extern char DNS_PP_XPASTE(dns_assert_, __LINE__)[sizeof (int[1 - 2*!(cond)])] -#endif - - -/* - * D E B U G M A C R O S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -int *dns_debug_p(void) { - static int debug; - - return &debug; -} /* dns_debug_p() */ - -#if DNS_DEBUG - -#undef DNS_DEBUG -#define DNS_DEBUG dns_debug - -#define DNS_SAY_(fmt, ...) \ - do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0) -#define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n") -#define DNS_HAI DNS_SAY("HAI") - -#define DNS_SHOW_(P, fmt, ...) do { \ - if (DNS_DEBUG > 1) { \ - fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \ - fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \ - dns_p_dump((P), stderr); \ - fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \ - } \ -} while (0) - -#define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "") - -#else /* !DNS_DEBUG */ - -#undef DNS_DEBUG -#define DNS_DEBUG 0 - -#define DNS_SAY(...) -#define DNS_HAI -#define DNS_SHOW(...) - -#endif /* DNS_DEBUG */ - -#define DNS_CARP(...) DNS_SAY(__VA_ARGS__) - - -/* - * V E R S I O N R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -const char *dns_vendor(void) { - return DNS_VENDOR; -} /* dns_vendor() */ - - -int dns_v_rel(void) { - return DNS_V_REL; -} /* dns_v_rel() */ - - -int dns_v_abi(void) { - return DNS_V_ABI; -} /* dns_v_abi() */ - - -int dns_v_api(void) { - return DNS_V_API; -} /* dns_v_api() */ - - -/* - * E R R O R R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#if _WIN32 - -#define DNS_EINTR WSAEINTR -#define DNS_EINPROGRESS WSAEINPROGRESS -#define DNS_EISCONN WSAEISCONN -#define DNS_EWOULDBLOCK WSAEWOULDBLOCK -#define DNS_EALREADY WSAEALREADY -#define DNS_EAGAIN EAGAIN -#define DNS_ETIMEDOUT WSAETIMEDOUT - -#define dns_syerr() ((int)GetLastError()) -#define dns_soerr() ((int)WSAGetLastError()) - -#else - -#define DNS_EINTR EINTR -#define DNS_EINPROGRESS EINPROGRESS -#define DNS_EISCONN EISCONN -#define DNS_EWOULDBLOCK EWOULDBLOCK -#define DNS_EALREADY EALREADY -#define DNS_EAGAIN EAGAIN -#define DNS_ETIMEDOUT ETIMEDOUT - -#define dns_syerr() errno -#define dns_soerr() errno - -#endif - - -const char *dns_strerror(int error) { - switch (error) { - case DNS_ENOBUFS: - return "DNS packet buffer too small"; - case DNS_EILLEGAL: - return "Illegal DNS RR name or data"; - case DNS_EORDER: - return "Attempt to push RR out of section order"; - case DNS_ESECTION: - return "Invalid section specified"; - case DNS_EUNKNOWN: - return "Unknown DNS error"; - case DNS_EADDRESS: - return "Invalid textual address form"; - case DNS_ENOQUERY: - return "Bad execution state (missing query packet)"; - case DNS_ENOANSWER: - return "Bad execution state (missing answer packet)"; - case DNS_EFETCHED: - return "Answer already fetched"; - case DNS_ESERVICE: - return "The service passed was not recognized for the specified socket type"; - case DNS_ENONAME: - return "The name does not resolve for the supplied parameters"; - case DNS_EFAIL: - return "A non-recoverable error occurred when attempting to resolve the name"; - default: - return strerror(error); - } /* switch() */ -} /* dns_strerror() */ - - -/* - * A T O M I C R O U T I N E S - * - * Use GCC's __atomic built-ins if possible. Unlike the __sync built-ins, we - * can use the preprocessor to detect API and, more importantly, ISA - * support. We want to avoid linking headaches where the API depends on an - * external library if the ISA (e.g. i386) doesn't support lockless - * operation. - * - * TODO: Support C11's atomic API. Although that may require some finesse - * with how we define some public types, such as dns_atomic_t and struct - * dns_resolv_conf. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef HAVE___ATOMIC_FETCH_ADD -#ifdef __ATOMIC_RELAXED -#define HAVE___ATOMIC_FETCH_ADD 1 -#else -#define HAVE___ATOMIC_FETCH_ADD 0 -#endif -#endif - -#ifndef HAVE___ATOMIC_FETCH_SUB -#define HAVE___ATOMIC_FETCH_SUB HAVE___ATOMIC_FETCH_ADD -#endif - -#ifndef DNS_ATOMIC_FETCH_ADD -#if HAVE___ATOMIC_FETCH_ADD && __GCC_ATOMIC_LONG_LOCK_FREE == 2 -#define DNS_ATOMIC_FETCH_ADD(i) __atomic_fetch_add((i), 1, __ATOMIC_RELAXED) -#else -#pragma message("no atomic_fetch_add available") -#define DNS_ATOMIC_FETCH_ADD(i) ((*(i))++) -#endif -#endif - -#ifndef DNS_ATOMIC_FETCH_SUB -#if HAVE___ATOMIC_FETCH_SUB && __GCC_ATOMIC_LONG_LOCK_FREE == 2 -#define DNS_ATOMIC_FETCH_SUB(i) __atomic_fetch_sub((i), 1, __ATOMIC_RELAXED) -#else -#pragma message("no atomic_fetch_sub available") -#define DNS_ATOMIC_FETCH_SUB(i) ((*(i))--) -#endif -#endif - -static inline unsigned dns_atomic_fetch_add(dns_atomic_t *i) { - return DNS_ATOMIC_FETCH_ADD(i); -} /* dns_atomic_fetch_add() */ - - -static inline unsigned dns_atomic_fetch_sub(dns_atomic_t *i) { - return DNS_ATOMIC_FETCH_SUB(i); -} /* dns_atomic_fetch_sub() */ - - -/* - * C R Y P T O R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* - * P R N G - */ - -#ifndef DNS_RANDOM -#if defined(HAVE_ARC4RANDOM) \ - || defined(__OpenBSD__) \ - || defined(__FreeBSD__) \ - || defined(__NetBSD__) \ - || defined(__APPLE__) -#define DNS_RANDOM arc4random -#elif __linux -#define DNS_RANDOM random -#else -#define DNS_RANDOM rand -#endif -#endif - -#define DNS_RANDOM_arc4random 1 -#define DNS_RANDOM_random 2 -#define DNS_RANDOM_rand 3 -#define DNS_RANDOM_RAND_bytes 4 - -#define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM)) - -#if DNS_RANDOM_OPENSSL -#include -#endif - -static unsigned dns_random_(void) { -#if DNS_RANDOM_OPENSSL - unsigned r; - _Bool ok; - - ok = (1 == RAND_bytes((unsigned char *)&r, sizeof r)); - assert(ok && "1 == RAND_bytes()"); - - return r; -#else - return DNS_RANDOM(); -#endif -} /* dns_random_() */ - -dns_random_f **dns_random_p(void) { - static dns_random_f *random_f = &dns_random_; - - return &random_f; -} /* dns_random_p() */ - - -/* - * P E R M U T A T I O N G E N E R A T O R - */ - -#define DNS_K_TEA_KEY_SIZE 16 -#define DNS_K_TEA_BLOCK_SIZE 8 -#define DNS_K_TEA_CYCLES 32 -#define DNS_K_TEA_MAGIC 0x9E3779B9U - -struct dns_k_tea { - uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; - unsigned cycles; -}; /* struct dns_k_tea */ - - -static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) { - memcpy(tea->key, key, sizeof tea->key); - - tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES; -} /* dns_k_tea_init() */ - - -static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) { - uint32_t y, z, sum, n; - - y = v[0]; - z = v[1]; - sum = 0; - - for (n = 0; n < tea->cycles; n++) { - sum += DNS_K_TEA_MAGIC; - y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]); - z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]); - } - - w[0] = y; - w[1] = z; - - return /* void */; -} /* dns_k_tea_encrypt() */ - - -/* - * Permutation generator, based on a Luby-Rackoff Feistel construction. - * - * Specifically, this is a generic balanced Feistel block cipher using TEA - * (another block cipher) as the pseudo-random function, F. At best it's as - * strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or - * perhaps Bernstein's Salsa20 core; I am naively trying to keep things - * simple. - * - * The generator can create a permutation of any set of numbers, as long as - * the size of the set is an even power of 2. This limitation arises either - * out of an inherent property of balanced Feistel constructions, or by my - * own ignorance. I'll tackle an unbalanced construction after I wrap my - * head around Schneier and Kelsey's paper. - * - * CAVEAT EMPTOR. IANAC. - */ -#define DNS_K_PERMUTOR_ROUNDS 8 - -struct dns_k_permutor { - unsigned stepi, length, limit; - unsigned shift, mask, rounds; - - struct dns_k_tea tea; -}; /* struct dns_k_permutor */ - - -static inline unsigned dns_k_permutor_powof(unsigned n) { - unsigned m, i = 0; - - for (m = 1; m < n; m <<= 1, i++) - ;; - - return i; -} /* dns_k_permutor_powof() */ - -static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) { - uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; - unsigned width, i; - - p->stepi = 0; - - p->length = (high - low) + 1; - p->limit = high; - - width = dns_k_permutor_powof(p->length); - width += width % 2; - - p->shift = width / 2; - p->mask = (1U << p->shift) - 1; - p->rounds = DNS_K_PERMUTOR_ROUNDS; - - for (i = 0; i < lengthof(key); i++) - key[i] = dns_random(); - - dns_k_tea_init(&p->tea, key, 0); - - return /* void */; -} /* dns_k_permutor_init() */ - - -static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) { - uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)]; - - memset(in, '\0', sizeof in); - - in[0] = k; - in[1] = x; - - dns_k_tea_encrypt(&p->tea, in, out); - - return p->mask & out[0]; -} /* dns_k_permutor_F() */ - - -static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) { - unsigned l[2], r[2]; - unsigned i; - - i = 0; - l[i] = p->mask & (n >> p->shift); - r[i] = p->mask & (n >> 0); - - do { - l[(i + 1) % 2] = r[i % 2]; - r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]); - - i++; - } while (i < p->rounds - 1); - - return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); -} /* dns_k_permutor_E() */ - - -DNS_NOTUSED static unsigned dns_k_permutor_D(struct dns_k_permutor *p, unsigned n) { - unsigned l[2], r[2]; - unsigned i; - - i = p->rounds - 1; - l[i % 2] = p->mask & (n >> p->shift); - r[i % 2] = p->mask & (n >> 0); - - do { - i--; - - r[i % 2] = l[(i + 1) % 2]; - l[i % 2] = r[(i + 1) % 2] ^ dns_k_permutor_F(p, i, l[(i + 1) % 2]); - } while (i > 0); - - return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); -} /* dns_k_permutor_D() */ - - -static unsigned dns_k_permutor_step(struct dns_k_permutor *p) { - unsigned n; - - do { - n = dns_k_permutor_E(p, p->stepi++); - } while (n >= p->length); - - return n + (p->limit + 1 - p->length); -} /* dns_k_permutor_step() */ - - -/* - * Simple permutation box. Useful for shuffling rrsets from an iterator. - * Uses AES s-box to provide good diffusion. - * - * Seems to pass muster under runs test. - * - * $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done - * $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }' - * library(lawstat) - * runs.test(scan(file="/tmp/out")) - * EOF - */ -static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) { - static const unsigned char sbox[256] = - { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, - 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, - 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, - 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, - 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, - 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, - 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, - 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, - 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, - 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, - 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, - 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, - 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, - 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, - 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, - 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, - 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; - unsigned char a, b; - unsigned i; - - a = 0xff & (n >> 0); - b = 0xff & (n >> 8); - - for (i = 0; i < 4; i++) { - a ^= 0xff & s; - a = sbox[a] ^ b; - b = sbox[b] ^ a; - s >>= 8; - } - - return ((0xff00 & (a << 8)) | (0x00ff & (b << 0))); -} /* dns_k_shuffle16() */ - -/* - * S T A T E M A C H I N E R O U T I N E S - * - * Application code should define DNS_SM_RESTORE and DNS_SM_SAVE, and the - * local variable pc. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_SM_ENTER \ - do { \ - static const int pc0 = __LINE__; \ - DNS_SM_RESTORE; \ - switch (pc0 + pc) { \ - case __LINE__: (void)0 - -#define DNS_SM_SAVE_AND_DO(do_statement) \ - do { \ - pc = __LINE__ - pc0; \ - DNS_SM_SAVE; \ - do_statement; \ - case __LINE__: (void)0; \ - } while (0) - -#define DNS_SM_YIELD(rv) \ - DNS_SM_SAVE_AND_DO(return (rv)) - -#define DNS_SM_EXIT \ - do { goto leave; } while (0) - -#define DNS_SM_LEAVE \ - leave: (void)0; \ - DNS_SM_SAVE_AND_DO(break); \ - } \ - } while (0) - -/* - * U T I L I T Y R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_MAXINTERVAL 300 - -struct dns_clock { - time_t sample, elapsed; -}; /* struct dns_clock */ - -static void dns_begin(struct dns_clock *clk) { - clk->sample = time(0); - clk->elapsed = 0; -} /* dns_begin() */ - -static time_t dns_elapsed(struct dns_clock *clk) { - time_t curtime; - - if ((time_t)-1 == time(&curtime)) - return clk->elapsed; - - if (curtime > clk->sample) - clk->elapsed += (time_t)DNS_PP_MIN(difftime(curtime, clk->sample), DNS_MAXINTERVAL); - - clk->sample = curtime; - - return clk->elapsed; -} /* dns_elapsed() */ - - -DNS_NOTUSED static size_t dns_strnlen(const char *src, size_t m) { - size_t n = 0; - - while (*src++ && n < m) - ++n; - - return n; -} /* dns_strnlen() */ - - -DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, size_t max) { - size_t len = dns_strnlen(src, max), n; - - if (lim > 0) { - n = DNS_PP_MIN(lim - 1, len); - memcpy(dst, src, n); - dst[n] = '\0'; - } - - return len; -} /* dns_strnlcpy() */ - - -#if (defined AF_UNIX && !defined _WIN32) -#define DNS_HAVE_SOCKADDR_UN 1 -#else -#define DNS_HAVE_SOCKADDR_UN 0 -#endif - -static size_t dns_af_len(int af) { - static const size_t table[AF_MAX] = { - [AF_INET6] = sizeof (struct sockaddr_in6), - [AF_INET] = sizeof (struct sockaddr_in), -#if DNS_HAVE_SOCKADDR_UN - [AF_UNIX] = sizeof (struct sockaddr_un), -#endif - }; - - return table[af]; -} /* dns_af_len() */ - -#define c_dns_sa_family(sa) (((const struct sockaddr *)(sa))->sa_family) -#define dns_sa_family(sa) (((struct sockaddr *)(sa))->sa_family) - -#define dns_sa_len(sa) dns_af_len(c_dns_sa_family(sa)) - - -#define DNS_SA_NOPORT &dns_sa_noport -static unsigned short dns_sa_noport; - -static unsigned short *dns_sa_port(int af, void *sa) { - switch (af) { - case AF_INET6: - return &((struct sockaddr_in6 *)sa)->sin6_port; - case AF_INET: - return &((struct sockaddr_in *)sa)->sin_port; - default: - return DNS_SA_NOPORT; - } -} /* dns_sa_port() */ - - -static void *dns_sa_addr(int af, void *sa, socklen_t *size) { - switch (af) { - case AF_INET6: { - struct in6_addr *in6 = &((struct sockaddr_in6 *)sa)->sin6_addr; - - if (size) - *size = sizeof *in6; - - return in6; - } - case AF_INET: { - struct in_addr *in = &((struct sockaddr_in *)sa)->sin_addr; - - if (size) - *size = sizeof *in; - - return in; - } - default: - if (size) - *size = 0; - - return 0; - } -} /* dns_sa_addr() */ - - -#if DNS_HAVE_SOCKADDR_UN -#define DNS_SUNPATHMAX (sizeof ((struct sockaddr_un *)0)->sun_path) -#endif - -DNS_NOTUSED static void *dns_sa_path(void *sa, socklen_t *size) { - switch (dns_sa_family(sa)) { -#if DNS_HAVE_SOCKADDR_UN - case AF_UNIX: { - char *path = ((struct sockaddr_un *)sa)->sun_path; - - if (size) - *size = dns_strnlen(path, DNS_SUNPATHMAX); - - return path; - } -#endif - default: - if (size) - *size = 0; - - return NULL; - } -} /* dns_sa_path() */ - - -static int dns_sa_cmp(void *a, void *b) { - int cmp, af; - - if ((cmp = dns_sa_family(a) - dns_sa_family(b))) - return cmp; - - switch ((af = dns_sa_family(a))) { - case AF_INET: { - const struct in_addr *a4, *b4; - - if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) - return cmp; - - a4 = dns_sa_addr(af, a, NULL); - b4 = dns_sa_addr(af, b, NULL); - - if (ntohl(a4->s_addr) < ntohl(b4->s_addr)) - return -1; - if (ntohl(a4->s_addr) > ntohl(b4->s_addr)) - return 1; - - return 0; - } - case AF_INET6: { - const struct in6_addr *a6, *b6; - size_t i; - - if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) - return cmp; - - a6 = dns_sa_addr(af, a, NULL); - b6 = dns_sa_addr(af, b, NULL); - - /* XXX: do we need to use in6_clearscope()? */ - for (i = 0; i < sizeof a6->s6_addr; i++) { - if ((cmp = a6->s6_addr[i] - b6->s6_addr[i])) - return cmp; - } - - return 0; - } -#if DNS_HAVE_SOCKADDR_UN - case AF_UNIX: { - char a_path[DNS_SUNPATHMAX + 1], b_path[sizeof a_path]; - - dns_strnlcpy(a_path, sizeof a_path, dns_sa_path(a, NULL), DNS_SUNPATHMAX); - dns_strnlcpy(b_path, sizeof b_path, dns_sa_path(b, NULL), DNS_SUNPATHMAX); - - return strcmp(a_path, b_path); - } -#endif - default: - return -1; - } -} /* dns_sa_cmp() */ - - -#if _WIN32 -static int dns_inet_pton(int af, const void *src, void *dst) { - union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; - - u.sin.sin_family = af; - - if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u })) - return -1; - - switch (af) { - case AF_INET6: - *(struct in6_addr *)dst = u.sin6.sin6_addr; - - return 1; - case AF_INET: - *(struct in_addr *)dst = u.sin.sin_addr; - - return 1; - default: - return 0; - } -} /* dns_inet_pton() */ - -static const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) { - union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; - - /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */ - memset(&u, 0, sizeof u); - - u.sin.sin_family = af; - - switch (af) { - case AF_INET6: - u.sin6.sin6_addr = *(struct in6_addr *)src; - break; - case AF_INET: - u.sin.sin_addr = *(struct in_addr *)src; - - break; - default: - return 0; - } - - if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim)) - return 0; - - return dst; -} /* dns_inet_ntop() */ -#else -#define dns_inet_pton(...) inet_pton(__VA_ARGS__) -#define dns_inet_ntop(...) inet_ntop(__VA_ARGS__) -#endif - - -static dns_error_t dns_pton(int af, const void *src, void *dst) { - switch (dns_inet_pton(af, src, dst)) { - case 1: - return 0; - case -1: - return dns_soerr(); - default: - return DNS_EADDRESS; - } -} /* dns_pton() */ - - -static dns_error_t dns_ntop(int af, const void *src, void *dst, unsigned long lim) { - return (dns_inet_ntop(af, src, dst, lim))? 0 : dns_soerr(); -} /* dns_ntop() */ - - -size_t dns_strlcpy(char *dst, const char *src, size_t lim) { - char *d = dst; - char *e = &dst[lim]; - const char *s = src; - - if (d < e) { - do { - if ('\0' == (*d++ = *s++)) - return s - src - 1; - } while (d < e); - - d[-1] = '\0'; - } - - while (*s++ != '\0') - ;; - - return s - src - 1; -} /* dns_strlcpy() */ - - -size_t dns_strlcat(char *dst, const char *src, size_t lim) { - char *d = memchr(dst, '\0', lim); - char *e = &dst[lim]; - const char *s = src; - const char *p; - - if (d && d < e) { - do { - if ('\0' == (*d++ = *s++)) - return d - dst - 1; - } while (d < e); - - d[-1] = '\0'; - } - - p = s; - - while (*s++ != '\0') - ;; - - return lim + (s - p - 1); -} /* dns_strlcat() */ - - -#if _WIN32 - -static char *dns_strsep(char **sp, const char *delim) { - char *p; - - if (!(p = *sp)) - return 0; - - *sp += strcspn(p, delim); - - if (**sp != '\0') { - **sp = '\0'; - ++*sp; - } else - *sp = NULL; - - return p; -} /* dns_strsep() */ - -#else -#define dns_strsep(...) strsep(__VA_ARGS__) -#endif - - -#if _WIN32 -#define strcasecmp(...) _stricmp(__VA_ARGS__) -#define strncasecmp(...) _strnicmp(__VA_ARGS__) -#endif - - -static inline _Bool dns_isalpha(unsigned char c) { - return isalpha(c); -} /* dns_isalpha() */ - -static inline _Bool dns_isdigit(unsigned char c) { - return isdigit(c); -} /* dns_isdigit() */ - -static inline _Bool dns_isalnum(unsigned char c) { - return isalnum(c); -} /* dns_isalnum() */ - -static inline _Bool dns_isspace(unsigned char c) { - return isspace(c); -} /* dns_isspace() */ - -/* changed by zsx--2017.12.20 */ -#if defined(_WIN32) || defined(_WIN64) -static int dns_poll(int fd, short events, int timeout) { - fd_set rset, wset; - struct timeval tv, *tp; - - if (!events) - return 0; - - assert(fd >= 0 && (unsigned)fd < FD_SETSIZE); - - FD_ZERO(&rset); - FD_ZERO(&wset); - - if (events & DNS_POLLIN) - FD_SET(fd, &rset); - - if (events & DNS_POLLOUT) - FD_SET(fd, &wset); - - if (timeout > 0) { - tv.tv_sec = timeout; - tv.tv_usec = 0; - tp = &tv; - } else { - tp = NULL; - } - select(fd + 1, &rset, &wset, 0, tp); - - return 0; -} -#else -static int dns_poll(int fd, short events, int timeout) { - struct pollfd fds; - - if (!events) { - acl_fiber_set_error(EINVAL); - return EINVAL; - } - - fds.fd = fd; - fds.events = 0; /* POLLHUP | POLLERR */ - if (events & DNS_POLLIN) - fds.events |= POLLIN; - if (events & DNS_POLLOUT) - fds.events |= POLLOUT; - for (;;) { - switch (poll(&fds, 1, timeout)) { - case -1: - if (acl_fiber_last_error() == EINTR) - continue; - return EEXIST; - case 0: - return ETIMEDOUT; - default: - if ((fds.revents & POLLIN)) - return 0; - return EEXIST; - } - } -} /* dns_poll() */ -#endif - -/* add by zsx--2017.12.20 */ -#ifdef SYS_WIN -static int read_wait(int fd, int timeout) { - struct timeval tv; - struct timeval *tp; - fd_set rset, xset; - int err; - - FD_ZERO(&rset); - FD_SET(fd, &rset); - FD_ZERO(&xset); - FD_SET(fd, &xset); - - if (timeout > 0) { - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - tp = &tv; - } else { - tp = NULL; - } - -TAG_AGAIN: - switch (select(1, &rset, (fd_set *) 0, &xset, tp)) { - case -1: - err = acl_fiber_last_error(); - if (err == EINTR || err == WSAEINPROGRESS - || err == WSAEWOULDBLOCK) { - - goto TAG_AGAIN; - } - return -1; - case 0: - acl_fiber_set_error(ETIMEDOUT); - return 0; - default: - return 1; - } -} -#else -static int read_wait(int fd, int timeout) { - struct pollfd fds; - int err; - - fds.fd = fd; - //fds.events = POLLHUP | POLLERR; - fds.events = POLLIN; - - for (;;) { - switch (poll(&fds, 1, timeout)) { - case -1: - err = acl_fiber_last_error(); - if (err == EINTR) - continue; - acl_fiber_set_error(EEXIST); - return -1; - case 0: - acl_fiber_set_error(ETIMEDOUT); - return 0; - default: - if ((fds.revents & POLLIN)) - return 1; - acl_fiber_set_error(EEXIST); - return -1; - } - } -} -#endif - -static int __timeout = 5000; - -static int read_wait0(int fd) { - return read_wait(fd, __timeout); -} - -void set_read_timeout(int timeo) { - if (timeo > 0) - __timeout = timeo * 1000; -} - -int get_read_timeout(void) { - return __timeout / 1000; -} - -// end add by zsx - - -#if !_WIN32 -DNS_NOTUSED static int dns_sigmask(int how, const sigset_t *set, sigset_t *oset) { -#if DNS_THREAD_SAFE - return pthread_sigmask(how, set, oset); -#else - return (0 == sigprocmask(how, set, oset))? 0 : errno; -#endif -} /* dns_sigmask() */ -#endif - - -static long dns_send(int fd, const void *src, size_t lim, int flags) { -#if _WIN32 || !defined SIGPIPE || defined SO_NOSIGPIPE - return send(fd, src, lim, flags); -#elif defined MSG_NOSIGNAL - return send(fd, src, lim, flags|MSG_NOSIGNAL); -#elif _POSIX_REALTIME_SIGNALS > 0 /* require sigtimedwait */ - /* - * SIGPIPE handling similar to the approach described in - * http://krokisplace.blogspot.com/2010/02/suppressing-sigpipe-in-library.html - */ - sigset_t pending, blocked, piped; - long count; - int saved, error; - - sigemptyset(&pending); - sigpending(&pending); - - if (!sigismember(&pending, SIGPIPE)) { - sigemptyset(&piped); - sigaddset(&piped, SIGPIPE); - sigemptyset(&blocked); - - if ((error = dns_sigmask(SIG_BLOCK, &piped, &blocked))) - goto error; - } - - count = send(fd, src, lim, flags); - - if (!sigismember(&pending, SIGPIPE)) { - saved = errno; - - if (count == -1 && errno == EPIPE) { - while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR) - ;; - } - - if ((error = dns_sigmask(SIG_SETMASK, &blocked, NULL))) - goto error; - - errno = saved; - } - - return count; -error: - errno = error; - - return -1; -#else -#error "unable to suppress SIGPIPE" - return send(fd, src, lim, flags); -#endif -} /* dns_send() */ - - -#define DNS_FOPEN_STDFLAGS "rwabt+" - -static dns_error_t dns_fopen_addflag(char *dst, const char *src, size_t lim, int fc) { - char *p = dst, *pe = dst + lim; - - /* copy standard flags */ - while (*src && strchr(DNS_FOPEN_STDFLAGS, *src)) { - if (!(p < pe)) - return ENOMEM; - *p++ = *src++; - } - - /* append flag to standard flags */ - if (!(p < pe)) - return ENOMEM; - *p++ = fc; - - /* copy remaining mode string, including '\0' */ - do { - if (!(p < pe)) - return ENOMEM; - } while ((*p++ = *src++)); - - return 0; -} /* dns_fopen_addflag() */ - -static FILE *dns_fopen(const char *path, const char *mode, dns_error_t *_error) { - FILE *fp; - char mode_cloexec[32]; - int error; - - assert(path && mode && *mode); - if (!*path) { - error = EINVAL; - goto error; - } - -#if _WIN32 || _WIN64 - if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'N'))) - goto error; - if (!(fp = fopen(path, mode_cloexec))) - goto syerr; -#else - if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'e'))) - goto error; - if (!(fp = fopen(path, mode_cloexec))) { - if (errno != EINVAL) - goto syerr; - if (!(fp = fopen(path, mode))) - goto syerr; - } -#endif - - return fp; -syerr: - error = dns_syerr(); -error: - *_error = error; - - return NULL; -} /* dns_fopen() */ - - -/* - * F I X E D - S I Z E D B U F F E R R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_B_INIT(src, n) { \ - (unsigned char *)(src), \ - (unsigned char *)(src), \ - (unsigned char *)(src) + (n), \ - DNS_EUNKNOWN, 0 \ -} - -#define DNS_B_FROM(src, n) DNS_B_INIT((src), (n)) -#define DNS_B_INTO(src, n) DNS_B_INIT((src), (n)) - -struct dns_buf { - unsigned char *base; - unsigned char *p; - const unsigned char *pe; - dns_error_t error; - size_t overflow; -}; /* struct dns_buf */ - -static inline size_t -dns_b_tell(struct dns_buf *b) -{ - return b->p - b->base; -} - -static inline dns_error_t -dns_b_setoverflow(struct dns_buf *b, size_t n, dns_error_t error) -{ - b->overflow += n; - return b->error = error; -} - -DNS_NOTUSED static struct dns_buf * -dns_b_into(struct dns_buf *b, void *src, size_t n) -{ - *b = (struct dns_buf)DNS_B_INTO(src, n); - - return b; -} - -static dns_error_t -dns_b_putc(struct dns_buf *b, unsigned char uc) -{ - if (!(b->p < b->pe)) - return dns_b_setoverflow(b, 1, DNS_ENOBUFS); - - *b->p++ = uc; - - return 0; -} - -static dns_error_t -dns_b_pputc(struct dns_buf *b, unsigned char uc, size_t p) -{ - size_t pe = b->pe - b->base; - if (pe <= p) - return dns_b_setoverflow(b, p - pe + 1, DNS_ENOBUFS); - - *((unsigned char *)b->base + p) = uc; - - return 0; -} - -static inline dns_error_t -dns_b_put16(struct dns_buf *b, uint16_t u) -{ - return dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); -} - -static inline dns_error_t -dns_b_pput16(struct dns_buf *b, uint16_t u, size_t p) -{ - if (dns_b_pputc(b, u >> 8, p) || dns_b_pputc(b, u >> 0, p + 1)) - return b->error; - - return 0; -} - -DNS_NOTUSED static inline dns_error_t -dns_b_put32(struct dns_buf *b, uint32_t u) -{ - return dns_b_putc(b, u >> 24), dns_b_putc(b, u >> 16), - dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); -} - -static dns_error_t -dns_b_put(struct dns_buf *b, const void *src, size_t len) -{ - size_t n = DNS_PP_MIN((size_t)(b->pe - b->p), len); - - memcpy(b->p, src, n); - b->p += n; - - if (n < len) - return dns_b_setoverflow(b, len - n, DNS_ENOBUFS); - - return 0; -} - -static dns_error_t -dns_b_puts(struct dns_buf *b, const void *src) -{ - return dns_b_put(b, src, strlen(src)); -} - -DNS_NOTUSED static inline dns_error_t -dns_b_fmtju(struct dns_buf *b, const uintmax_t u, const unsigned width) -{ - size_t digits, padding, overflow; - uintmax_t r; - unsigned char *tp, *te, tc; - - digits = 0; - r = u; - do { - digits++; - r /= 10; - } while (r); - - padding = width - DNS_PP_MIN(digits, width); - overflow = (digits + padding) - DNS_PP_MIN((size_t)(b->pe - b->p), (digits + padding)); - - while (padding--) { - dns_b_putc(b, '0'); - } - - digits = 0; - tp = b->p; - r = u; - do { - if (overflow < ++digits) - dns_b_putc(b, '0' + (r % 10)); - r /= 10; - } while (r); - - te = b->p; - while (tp < te) { - tc = *--te; - *te = *tp; - *tp++ = tc; - } - - return b->error; -} - -static void -dns_b_popc(struct dns_buf *b) -{ - if (b->overflow && !--b->overflow) - b->error = 0; - if (b->p > b->base) - b->p--; -} - -static inline const char * -dns_b_tolstring(struct dns_buf *b, size_t *n) -{ - if (b->p < b->pe) { - *b->p = '\0'; - *n = b->p - b->base; - - return (const char *)b->base; - } else if (b->p > b->base) { - if (b->p[-1] != '\0') { - dns_b_setoverflow(b, 1, DNS_ENOBUFS); - b->p[-1] = '\0'; - } - *n = &b->p[-1] - b->base; - - return (const char *)b->base; - } else { - *n = 0; - - return ""; - } -} - -static inline const char * -dns_b_tostring(struct dns_buf *b) -{ - size_t n; - return dns_b_tolstring(b, &n); -} - -static inline size_t -dns_b_strlen(struct dns_buf *b) -{ - size_t n; - dns_b_tolstring(b, &n); - return n; -} - -static inline size_t -dns_b_strllen(struct dns_buf *b) -{ - size_t n = dns_b_strlen(b); - return n + b->overflow; -} - -DNS_NOTUSED static const struct dns_buf * -dns_b_from(struct dns_buf *b, void *src, size_t n) -{ - *(struct dns_buf *)b = (struct dns_buf)DNS_B_FROM(src, n); - - return b; -} - -static inline int -dns_b_getc(struct dns_buf *_b, int eof) -{ - struct dns_buf *b = (struct dns_buf *)_b; - - if (!(b->p < b->pe)) - return dns_b_setoverflow(b, 1, DNS_EILLEGAL), eof; - - return *b->p++; -} - -static inline intmax_t -dns_b_get16(struct dns_buf *b, intmax_t eof) -{ - intmax_t n; - - n = (dns_b_getc(b, 0) << 8); - n |= (dns_b_getc(b, 0) << 0); - - return (!b->overflow)? n : eof; -} - -DNS_NOTUSED static inline intmax_t -dns_b_get32(struct dns_buf *b, intmax_t eof) -{ - intmax_t n; - - n = (dns_b_get16(b, 0) << 16); - n |= (dns_b_get16(b, 0) << 0); - - return (!b->overflow)? n : eof; -} - -static inline dns_error_t -dns_b_move(struct dns_buf *dst, struct dns_buf *_src, size_t n) -{ - struct dns_buf *src = (struct dns_buf *)_src; - size_t src_n = DNS_PP_MIN((size_t)(src->pe - src->p), n); - size_t src_r = n - src_n; - - dns_b_put(dst, src->p, src_n); - src->p += src_n; - - if (src_r) - return dns_b_setoverflow(src, src_r, DNS_EILLEGAL); - - return dst->error; -} - - -/* - * P A C K E T R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -unsigned dns_p_count(struct dns_packet *P, enum dns_section section) { - unsigned count; - - switch (section) { - case DNS_S_QD: - return ntohs(DNS_HEADER(P)->qdcount); - case DNS_S_AN: - return ntohs(DNS_HEADER(P)->ancount); - case DNS_S_NS: - return ntohs(DNS_HEADER(P)->nscount); - case DNS_S_AR: - return ntohs(DNS_HEADER(P)->arcount); - default: - count = 0; - - if (section & DNS_S_QD) - count += ntohs(DNS_HEADER(P)->qdcount); - if (section & DNS_S_AN) - count += ntohs(DNS_HEADER(P)->ancount); - if (section & DNS_S_NS) - count += ntohs(DNS_HEADER(P)->nscount); - if (section & DNS_S_AR) - count += ntohs(DNS_HEADER(P)->arcount); - - return count; - } -} /* dns_p_count() */ - - -struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) { - if (!P) - return 0; - - assert(size >= offsetof(struct dns_packet, data) + 12); - - memset(P, 0, sizeof *P); - P->size = size - offsetof(struct dns_packet, data); - P->end = 12; - - memset(P->data, '\0', 12); - - return P; -} /* dns_p_init() */ - - -static struct dns_packet *dns_p_reset(struct dns_packet *P) { - return dns_p_init(P, offsetof(struct dns_packet, data) + P->size); -} /* dns_p_reset() */ - - -static unsigned short dns_p_qend(struct dns_packet *P) { - unsigned short qend = 12; - unsigned i, count = dns_p_count(P, DNS_S_QD); - - for (i = 0; i < count && qend < P->end; i++) { - if (P->end == (qend = dns_d_skip(qend, P))) - goto invalid; - - if (P->end - qend < 4) - goto invalid; - - qend += 4; - } - - return DNS_PP_MIN(qend, P->end); -invalid: - return P->end; -} /* dns_p_qend() */ - - -struct dns_packet *dns_p_make(size_t len, int *error) { - struct dns_packet *P; - size_t size = dns_p_calcsize(len); - - if (!(P = dns_p_init(malloc(size), size))) - *error = dns_syerr(); - - return P; -} /* dns_p_make() */ - - -static void dns_p_free(struct dns_packet *P) { - free(P); -} /* dns_p_free() */ - - -/* convience routine to free any existing packet before storing new packet */ -static struct dns_packet *dns_p_setptr(struct dns_packet **dst, struct dns_packet *src) { - dns_p_free(*dst); - - *dst = src; - - return src; -} /* dns_p_setptr() */ - - -static struct dns_packet *dns_p_movptr(struct dns_packet **dst, struct dns_packet **src) { - dns_p_setptr(dst, *src); - - *src = NULL; - - return *dst; -} /* dns_p_movptr() */ - - -int dns_p_grow(struct dns_packet **P) { - struct dns_packet *tmp; - size_t size; - int error; - - if (!*P) { - if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error))) - return error; - - return 0; - } - - size = dns_p_sizeof(*P); - size |= size >> 1; - size |= size >> 2; - size |= size >> 4; - size |= size >> 8; - size++; - - if (size > 65536) - return DNS_ENOBUFS; - - if (!(tmp = realloc(*P, dns_p_calcsize(size)))) - return dns_syerr(); - - tmp->size = size; - *P = tmp; - - return 0; -} /* dns_p_grow() */ - - -struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) { - if (!P) - return 0; - - P->end = DNS_PP_MIN(P->size, P0->end); - - memcpy(P->data, P0->data, P->end); - - return P; -} /* dns_p_copy() */ - - -struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) { - size_t bufsiz = DNS_PP_MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0)); - struct dns_packet *M; - enum dns_section section; - struct dns_rr rr, mr; - int error, copy; - - if (!A && B) { - A = B; - Amask = Bmask; - B = 0; - } - -merge: - if (!(M = dns_p_make(bufsiz, &error))) - goto error; - - for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) { - if (A && (section & Amask)) { - dns_rr_foreach(&rr, A, .section = section) { - if ((error = dns_rr_copy(M, &rr, A))) - goto error; - } - } - - if (B && (section & Bmask)) { - dns_rr_foreach(&rr, B, .section = section) { - copy = 1; - - dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) { - if (!(copy = dns_rr_cmp(&rr, B, &mr, M))) - break; - } - - if (copy && (error = dns_rr_copy(M, &rr, B))) - goto error; - } - } - } - - return M; -error: - dns_p_setptr(&M, NULL); - - if (error == DNS_ENOBUFS && bufsiz < 65535) { - bufsiz = DNS_PP_MIN(65535, bufsiz * 2); - - goto merge; - } - - *error_ = error; - - return 0; -} /* dns_p_merge() */ - - -static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t); - -void dns_p_dictadd(struct dns_packet *P, unsigned short dn) { - unsigned short lp, lptr, i; - - lp = dn; - - while (lp < P->end) { - if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) { - lptr = ((0x3f & P->data[lp + 0]) << 8) - | ((0xff & P->data[lp + 1]) << 0); - - for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { - if (P->dict[i] == lptr) { - P->dict[i] = dn; - - return; - } - } - } - - lp = dns_l_skip(lp, P->data, P->end); - } - - for (i = 0; i < lengthof(P->dict); i++) { - if (!P->dict[i]) { - P->dict[i] = dn; - - break; - } - } -} /* dns_p_dictadd() */ - - -int dns_p_push(struct dns_packet *P, enum dns_section section, void *dn, - size_t dnlen, enum dns_type type, enum dns_class class, - unsigned ttl, void *any) { - - unsigned n; - size_t end = P->end; - int error; - - if ((error = dns_d_push(P, dn, dnlen))) - goto error; - - if (P->size - P->end < 4) - goto nobufs; - - P->data[P->end++] = 0xff & (type >> 8); - P->data[P->end++] = 0xff & (type >> 0); - - P->data[P->end++] = 0xff & (class >> 8); - P->data[P->end++] = 0xff & (class >> 0); - - if (section == DNS_S_QD) - goto update; - - if (P->size - P->end < 6) - goto nobufs; - - if (type != DNS_T_OPT) - ttl = DNS_PP_MIN(ttl, 0x7fffffffU); - P->data[P->end++] = ttl >> 24; - P->data[P->end++] = ttl >> 16; - P->data[P->end++] = ttl >> 8; - P->data[P->end++] = ttl >> 0; - - if ((error = dns_any_push(P, (union dns_any *)any, type))) - goto error; - -update: - switch (section) { - case DNS_S_QD: - if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR)) - goto order; - - if (!P->memo.qd.base && (error = dns_p_study(P))) - goto error; - - n = ntohs(DNS_HEADER(P)->qdcount) + 1; - DNS_HEADER(P)->qdcount = htons(n); - - P->memo.qd.end = P->end; - P->memo.an.base = P->end; - P->memo.an.end = P->end; - P->memo.ns.base = P->end; - P->memo.ns.end = P->end; - P->memo.ar.base = P->end; - P->memo.ar.end = P->end; - - break; - case DNS_S_AN: - if (dns_p_count(P, DNS_S_NS|DNS_S_AR)) - goto order; - - if (!P->memo.an.base && (error = dns_p_study(P))) - goto error; - - n = ntohs(DNS_HEADER(P)->ancount) + 1; - DNS_HEADER(P)->ancount = htons(n); - - P->memo.an.end = P->end; - P->memo.ns.base = P->end; - P->memo.ns.end = P->end; - P->memo.ar.base = P->end; - P->memo.ar.end = P->end; - - break; - case DNS_S_NS: - if (dns_p_count(P, DNS_S_AR)) - goto order; - - if (!P->memo.ns.base && (error = dns_p_study(P))) - goto error; - - n = ntohs(DNS_HEADER(P)->nscount) + 1; - DNS_HEADER(P)->nscount = htons(n); - - P->memo.ns.end = P->end; - P->memo.ar.base = P->end; - P->memo.ar.end = P->end; - - break; - case DNS_S_AR: - if (!P->memo.ar.base && (error = dns_p_study(P))) - goto error; - - n = ntohs(DNS_HEADER(P)->arcount) + 1; - DNS_HEADER(P)->arcount = htons(n); - - P->memo.ar.end = P->end; - - if (type == DNS_T_OPT && !P->memo.opt.p) { - P->memo.opt.p = end; - P->memo.opt.maxudp = class; - P->memo.opt.ttl = ttl; - } - - break; - default: - error = DNS_ESECTION; - - goto error; - } /* switch() */ - - return 0; -nobufs: - error = DNS_ENOBUFS; - - goto error; -order: - error = DNS_EORDER; - - goto error; -error: - P->end = end; - - return error; -} /* dns_p_push() */ - - -static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) { - enum dns_section section; - struct dns_rr rr; - int error; - union dns_any any; - char pretty[sizeof any * 2]; - size_t len; - - fputs(";; [HEADER]\n", fp); - fprintf(fp, ";; qid : %d\n", ntohs(DNS_HEADER(P)->qid)); - fprintf(fp, ";; qr : %s(%d)\n", (DNS_HEADER(P)->qr)? "RESPONSE" : "QUERY", DNS_HEADER(P)->qr); - fprintf(fp, ";; opcode : %s(%d)\n", dns_stropcode(DNS_HEADER(P)->opcode), DNS_HEADER(P)->opcode); - fprintf(fp, ";; aa : %s(%d)\n", (DNS_HEADER(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", DNS_HEADER(P)->aa); - fprintf(fp, ";; tc : %s(%d)\n", (DNS_HEADER(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", DNS_HEADER(P)->tc); - fprintf(fp, ";; rd : %s(%d)\n", (DNS_HEADER(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", DNS_HEADER(P)->rd); - fprintf(fp, ";; ra : %s(%d)\n", (DNS_HEADER(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", DNS_HEADER(P)->ra); - fprintf(fp, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); - - section = 0; - - while (dns_rr_grep(&rr, 1, I, P, &error)) { - if (section != rr.section) - fprintf(fp, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); - - if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) - fprintf(fp, "%s\n", pretty); - - section = rr.section; - } -} /* dns_p_dump3() */ - - -void dns_p_dump(struct dns_packet *P, FILE *fp) { - dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp); -} /* dns_p_dump() */ - - -static void dns_s_unstudy(struct dns_s_memo *m) - { m->base = 0; m->end = 0; } - -static void dns_m_unstudy(struct dns_p_memo *m) { - dns_s_unstudy(&m->qd); - dns_s_unstudy(&m->an); - dns_s_unstudy(&m->ns); - dns_s_unstudy(&m->ar); - m->opt.p = 0; - m->opt.maxudp = 0; - m->opt.ttl = 0; -} /* dns_m_unstudy() */ - -static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned short base, struct dns_packet *P) { - unsigned short count, rp; - - count = dns_p_count(P, section); - - for (rp = base; count && rp < P->end; count--) - rp = dns_rr_skip(rp, P); - - m->base = base; - m->end = rp; - - return 0; -} /* dns_s_study() */ - -static int dns_m_study(struct dns_p_memo *m, struct dns_packet *P) { - struct dns_rr rr; - int error; - - if ((error = dns_s_study(&m->qd, DNS_S_QD, 12, P))) - goto error; - if ((error = dns_s_study(&m->an, DNS_S_AN, m->qd.end, P))) - goto error; - if ((error = dns_s_study(&m->ns, DNS_S_NS, m->an.end, P))) - goto error; - if ((error = dns_s_study(&m->ar, DNS_S_AR, m->ns.end, P))) - goto error; - - m->opt.p = 0; - m->opt.maxudp = 0; - m->opt.ttl = 0; - dns_rr_foreach(&rr, P, .type = DNS_T_OPT, .section = DNS_S_AR) { - m->opt.p = rr.dn.p; - m->opt.maxudp = rr.class; - m->opt.ttl = rr.ttl; - break; - } - - return 0; -error: - dns_m_unstudy(m); - - return error; -} /* dns_m_study() */ - -int dns_p_study(struct dns_packet *P) { - return dns_m_study(&P->memo, P); -} /* dns_p_study() */ - - -enum dns_rcode dns_p_rcode(struct dns_packet *P) { - return 0xfff & ((P->memo.opt.ttl >> 20) | DNS_HEADER(P)->rcode); -} /* dns_p_rcode() */ - - -/* - * Q U E R Y P A C K E T R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_Q_RD 0x1 /* recursion desired */ -#define DNS_Q_EDNS0 0x2 /* include OPT RR */ - -static dns_error_t -dns_q_make2(struct dns_packet **_Q, char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass, int qflags) -{ - struct dns_packet *Q = NULL; - int error; - - if (dns_p_movptr(&Q, _Q)) { - dns_p_reset(Q); - } else if (!(Q = dns_p_make(DNS_P_QBUFSIZ, &error))) { - goto error; - } - - if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, qtype, qclass, 0, 0))) - goto error; - - DNS_HEADER(Q)->rd = !!(qflags & DNS_Q_RD); - - if (qflags & DNS_Q_EDNS0) { - struct dns_opt opt = DNS_OPT_INIT(&opt); - - opt.version = 0; /* RFC 6891 version */ - opt.maxudp = 4096; - - if ((error = dns_p_push(Q, DNS_S_AR, ".", 1, DNS_T_OPT, dns_opt_class(&opt), dns_opt_ttl(&opt), &opt))) - goto error; - } - - *_Q = Q; - - return 0; -error: - dns_p_free(Q); - - return error; -} - -static dns_error_t -dns_q_make(struct dns_packet **Q, char *qname, enum dns_type qtype, enum dns_class qclass, int qflags) -{ - return dns_q_make2(Q, qname, strlen(qname), qtype, qclass, qflags); -} - -static dns_error_t -dns_q_remake(struct dns_packet **Q, int qflags) -{ - char qname[DNS_D_MAXNAME + 1]; - size_t qlen; - struct dns_rr rr; - int error; - - assert(Q && *Q); - if ((error = dns_rr_parse(&rr, 12, *Q))) - return error; - if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, *Q, &error))) - return error; - if (qlen >= sizeof qname) - return DNS_EILLEGAL; - return dns_q_make2(Q, qname, qlen, rr.type, rr.class, qflags); -} - -/* - * D O M A I N N A M E R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DNS_D_MAXPTRS -#define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */ -#endif - -static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) { - unsigned short len; - unsigned nptrs = 0; - -retry: - if (src >= end) - goto invalid; - - switch (0x03 & (data[src] >> 6)) { - case 0x00: - len = (0x3f & (data[src++])); - - if (end - src < len) - goto invalid; - - if (lim > 0) { - memcpy(dst, &data[src], DNS_PP_MIN(lim, len)); - - dst[DNS_PP_MIN(lim - 1, len)] = '\0'; - } - - *nxt = src + len; - - return len; - case 0x01: - goto invalid; - case 0x02: - goto invalid; - case 0x03: - if (++nptrs > DNS_D_MAXPTRS) - goto invalid; - - if (end - src < 2) - goto invalid; - - src = ((0x3f & data[src + 0]) << 8) - | ((0xff & data[src + 1]) << 0); - - goto retry; - } /* switch() */ - - /* NOT REACHED */ -invalid: - *nxt = end; - - return 0; -} /* dns_l_expand() */ - - -static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) { - unsigned short len; - - if (src >= end) - goto invalid; - - switch (0x03 & (data[src] >> 6)) { - case 0x00: - len = (0x3f & (data[src++])); - - if (end - src < len) - goto invalid; - - return (len)? src + len : end; - case 0x01: - goto invalid; - case 0x02: - goto invalid; - case 0x03: - return end; - } /* switch() */ - - /* NOT REACHED */ -invalid: - return end; -} /* dns_l_skip() */ - - -static _Bool dns_d_isanchored(const void *_src, size_t len) { - const unsigned char *src = _src; - return len > 0 && src[len - 1] == '.'; -} /* dns_d_isanchored() */ - - -static size_t dns_d_ndots(const void *_src, size_t len) { - const unsigned char *p = _src, *pe = p + len; - size_t ndots = 0; - - while ((p = memchr(p, '.', pe - p))) { - ndots++; - p++; - } - - return ndots; -} /* dns_d_ndots() */ - - -static size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) { - unsigned char *dst = dst_; - const unsigned char *src = src_; - size_t dp = 0, sp = 0; - int lc; - - /* trim any leading dot(s) */ - while (sp < len && src[sp] == '.') - sp++; - - for (lc = 0; sp < len; lc = src[sp++]) { - /* trim extra dot(s) */ - if (src[sp] == '.' && lc == '.') - continue; - - if (dp < lim) - dst[dp] = src[sp]; - - dp++; - } - - if ((flags & DNS_D_ANCHOR) && lc != '.') { - if (dp < lim) - dst[dp] = '.'; - - dp++; - } - - if (lim > 0) - dst[DNS_PP_MIN(dp, lim - 1)] = '\0'; - - return dp; -} /* dns_d_trim() */ - - -char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) { - if (flags & DNS_D_TRIM) { - dns_d_trim(dst, lim, src, len, flags); - } if (flags & DNS_D_ANCHOR) { - dns_d_anchor(dst, lim, src, len); - } else { - memmove(dst, src, DNS_PP_MIN(lim, len)); - - if (lim > 0) - ((char *)dst)[DNS_PP_MIN(len, lim - 1)] = '\0'; - } - - return dst; -} /* dns_d_init() */ - - -size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) { - if (len == 0) - return 0; - - memmove(dst, src, DNS_PP_MIN(lim, len)); - - if (((const char *)src)[len - 1] != '.') { - if (len < lim) - ((char *)dst)[len] = '.'; - len++; - } - - if (lim > 0) - ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; - - return len; -} /* dns_d_anchor() */ - - -size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) { - const char *dot; - - /* XXX: Skip any leading dot. Handles cleaving root ".". */ - if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1))) - return 0; - - len -= dot - (const char *)src; - - /* XXX: Unless root, skip the label's trailing dot. */ - if (len > 1) { - src = ++dot; - len--; - } else - src = dot; - - memmove(dst, src, DNS_PP_MIN(lim, len)); - - if (lim > 0) - ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; - - return len; -} /* dns_d_cleave() */ - - -size_t dns_d_comp(void *dst_, size_t lim, void *src_, size_t len, struct dns_packet *P, int *error) { - struct { unsigned char *b; size_t p, x; } dst, src; - unsigned char ch = '.'; - - dst.b = dst_; - dst.p = 0; - dst.x = 1; - - src.b = (unsigned char *)src_; - src.p = 0; - src.x = 0; - - while (src.x < len) { - ch = src.b[src.x]; - - if (ch == '.') { - if (dst.p < lim) - dst.b[dst.p] = (0x3f & (src.x - src.p)); - - dst.p = dst.x++; - src.p = ++src.x; - } else { - if (dst.x < lim) - dst.b[dst.x] = ch; - - dst.x++; - src.x++; - } - } /* while() */ - - if (src.x > src.p) { - if (dst.p < lim) - dst.b[dst.p] = (0x3f & (src.x - src.p)); - - dst.p = dst.x; - } - - if (dst.p > 1) { - if (dst.p < lim) - dst.b[dst.p] = 0x00; - - dst.p++; - } - -#if 1 - if (dst.p < lim) { - struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b; - unsigned i; - - a.p = 0; - - while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) { - for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { - b.p = P->dict[i]; - - while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) { - a.y = a.x; - b.y = b.x; - - while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) { - a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim); - b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end); - } - - if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) { - dst.b[a.p++] = 0xc0 - | (0x3f & (b.p >> 8)); - dst.b[a.p++] = (0xff & (b.p >> 0)); - - /* silence static analyzers */ - dns_assume(a.p > 0); - - return a.p; - } - - b.p = b.x; - } /* while() */ - } /* for() */ - - a.p = a.x; - } /* while() */ - } /* if () */ -#endif - - if (!dst.p) - *error = DNS_EILLEGAL; - - return dst.p; -} /* dns_d_comp() */ - - -unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) { - unsigned short len; - - while (src < P->end) { - switch (0x03 & (P->data[src] >> 6)) { - case 0x00: /* FOLLOWS */ - len = (0x3f & P->data[src++]); - - if (0 == len) { -/* success ==> */ return src; - } else if (P->end - src > len) { - src += len; - - break; - } else - goto invalid; - - /* NOT REACHED */ - case 0x01: /* RESERVED */ - goto invalid; - case 0x02: /* RESERVED */ - goto invalid; - case 0x03: /* POINTER */ - if (P->end - src < 2) - goto invalid; - - src += 2; - -/* success ==> */ return src; - } /* switch() */ - } /* while() */ - -invalid: - return P->end; -} /* dns_d_skip() */ - - -#include - -size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) { - size_t dstp = 0; - unsigned nptrs = 0; - unsigned char len; - - while (src < P->end) { - switch ((0x03 & (P->data[src] >> 6))) { - case 0x00: /* FOLLOWS */ - len = (0x3f & P->data[src]); - - if (0 == len) { - if (dstp == 0) { - if (dstp < lim) - ((unsigned char *)dst)[dstp] = '.'; - - dstp++; - } - - /* NUL terminate */ - if (lim > 0) - ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; - -/* success ==> */ return dstp; - } - - src++; - - if (P->end - src < len) - goto toolong; - - if (dstp < lim) - memcpy(&((unsigned char *)dst)[dstp], &P->data[src], DNS_PP_MIN(len, lim - dstp)); - - src += len; - dstp += len; - - if (dstp < lim) - ((unsigned char *)dst)[dstp] = '.'; - - dstp++; - - nptrs = 0; - - continue; - case 0x01: /* RESERVED */ - goto reserved; - case 0x02: /* RESERVED */ - goto reserved; - case 0x03: /* POINTER */ - if (++nptrs > DNS_D_MAXPTRS) - goto toolong; - - if (P->end - src < 2) - goto toolong; - - src = ((0x3f & P->data[src + 0]) << 8) - | ((0xff & P->data[src + 1]) << 0); - - continue; - } /* switch() */ - } /* while() */ - -toolong: - *error = DNS_EILLEGAL; - - if (lim > 0) - ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; - - return 0; -reserved: - *error = DNS_EILLEGAL; - - if (lim > 0) - ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; - - return 0; -} /* dns_d_expand() */ - - -int dns_d_push(struct dns_packet *P, void *dn, size_t len) { - size_t lim = P->size - P->end; - unsigned dp = P->end; - int error = DNS_EILLEGAL; /* silence compiler */ - - len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error); - - if (len == 0) - return error; - if (len > lim) - return DNS_ENOBUFS; - - P->end += len; - - dns_p_dictadd(P, dp); - - return 0; -} /* dns_d_push() */ - - -size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) { - char host[DNS_D_MAXNAME + 1]; - struct dns_rr_i i; - struct dns_rr rr; - unsigned depth; - int error; - - if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len)) - { error = ENAMETOOLONG; goto error; } - - for (depth = 0; depth < 7; depth++) { - dns_rr_i_init(memset(&i, 0, sizeof i), P); - - i.section = DNS_S_ALL & ~DNS_S_QD; - i.name = host; - i.type = DNS_T_CNAME; - - if (!dns_rr_grep(&rr, 1, &i, P, &error)) - break; - - if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P))) - goto error; - } - - return dns_strlcpy(dst, host, lim); -error: - *error_ = error; - - return 0; -} /* dns_d_cname() */ - - -/* - * R E S O U R C E R E C O R D R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) { - unsigned char dn[DNS_D_MAXNAME + 1]; - union dns_any any; - size_t len; - int error; - - if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error))) - return error; - else if (len >= sizeof dn) - return DNS_EILLEGAL; - - if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q))) - return error; - - return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any); -} /* dns_rr_copy() */ - - -int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) { - unsigned short p = src; - - if (src >= P->end) - goto invalid; - - rr->dn.p = p; - rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p; - - if (P->end - p < 4) - goto invalid; - - rr->type = ((0xff & P->data[p + 0]) << 8) - | ((0xff & P->data[p + 1]) << 0); - - rr->class = ((0xff & P->data[p + 2]) << 8) - | ((0xff & P->data[p + 3]) << 0); - - p += 4; - - if (src < dns_p_qend(P)) { - rr->section = DNS_S_QUESTION; - - rr->ttl = 0; - rr->rd.p = 0; - rr->rd.len = 0; - - return 0; - } - - if (P->end - p < 4) - goto invalid; - - rr->ttl = ((0xff & P->data[p + 0]) << 24) - | ((0xff & P->data[p + 1]) << 16) - | ((0xff & P->data[p + 2]) << 8) - | ((0xff & P->data[p + 3]) << 0); - if (rr->type != DNS_T_OPT) - rr->ttl = DNS_PP_MIN(rr->ttl, 0x7fffffffU); - - p += 4; - - if (P->end - p < 2) - goto invalid; - - rr->rd.len = ((0xff & P->data[p + 0]) << 8) - | ((0xff & P->data[p + 1]) << 0); - rr->rd.p = p + 2; - - p += 2; - - if (P->end - p < rr->rd.len) - goto invalid; - - return 0; -invalid: - return DNS_EILLEGAL; -} /* dns_rr_parse() */ - - -static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) { - unsigned short rp, rdlen; - - rp = dns_d_skip(src, P); - - if (P->end - rp < 4) - return P->end - src; - - rp += 4; /* TYPE, CLASS */ - - if (rp <= dns_p_qend(P)) - return rp - src; - - if (P->end - rp < 6) - return P->end - src; - - rp += 6; /* TTL, RDLEN */ - - rdlen = ((0xff & P->data[rp - 2]) << 8) - | ((0xff & P->data[rp - 1]) << 0); - - if (P->end - rp < rdlen) - return P->end - src; - - rp += rdlen; - - return rp - src; -} /* dns_rr_len() */ - - -unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) { - return src + dns_rr_len(src, P); -} /* dns_rr_skip() */ - - -static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) { - enum dns_section section; - unsigned count, index; - unsigned short rp; - - if (src >= P->memo.qd.base && src < P->memo.qd.end) - return DNS_S_QD; - if (src >= P->memo.an.base && src < P->memo.an.end) - return DNS_S_AN; - if (src >= P->memo.ns.base && src < P->memo.ns.end) - return DNS_S_NS; - if (src >= P->memo.ar.base && src < P->memo.ar.end) - return DNS_S_AR; - - /* NOTE: Possibly bad memoization. Try it the hard-way. */ - - for (rp = 12, index = 0; rp < src && rp < P->end; index++) - rp = dns_rr_skip(rp, P); - - section = DNS_S_QD; - count = dns_p_count(P, section); - - while (index >= count && section <= DNS_S_AR) { - section <<= 1; - count += dns_p_count(P, section); - } - - return DNS_S_ALL & section; -} /* dns_rr_section() */ - - -static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) { - struct dns_rr rr; - int error; - - if ((error = dns_rr_parse(&rr, src, P))) - return 0; - - return rr.type; -} /* dns_rr_type() */ - - -int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) { - char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1]; - union dns_any any0, any1; - int cmp, error; - size_t len; - - if ((cmp = r0->type - r1->type)) - return cmp; - - if ((cmp = r0->class - r1->class)) - return cmp; - - /* - * FIXME: Do label-by-label comparison to handle illegally long names? - */ - - if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error)) - || len >= sizeof host0) - return -1; - - if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error)) - || len >= sizeof host1) - return 1; - - if ((cmp = strcasecmp(host0, host1))) - return cmp; - - if (DNS_S_QD & (r0->section | r1->section)) { - if (r0->section == r1->section) - return 0; - - return (r0->section == DNS_S_QD)? -1 : 1; - } - - if ((error = dns_any_parse(&any0, r0, P0))) - return -1; - - if ((error = dns_any_parse(&any1, r1, P1))) - return 1; - - return dns_any_cmp(&any0, r0->type, &any1, r1->type); -} /* dns_rr_cmp() */ - - -static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) { - struct dns_rr rr1; - - dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) { - if (0 == dns_rr_cmp(rr0, P0, &rr1, P1)) - return 1; - } - - return 0; -} /* dns_rr_exists() */ - - -static unsigned short dns_rr_offset(struct dns_rr *rr) { - return rr->dn.p; -} /* dns_rr_offset() */ - - -static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) { - if (i->section && !(rr->section & i->section)) - return 0; - - if (i->type && rr->type != i->type && i->type != DNS_T_ALL) - return 0; - - if (i->class && rr->class != i->class && i->class != DNS_C_ANY) - return 0; - - if (i->name) { - char dn[DNS_D_MAXNAME + 1]; - size_t len; - int error; - - if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error)) - || len >= sizeof dn) - return 0; - - if (0 != strcasecmp(dn, i->name)) - return 0; - } - - if (i->data && i->type && rr->section > DNS_S_QD) { - union dns_any rd; - int error; - - if ((error = dns_any_parse(&rd, rr, P))) - return 0; - - if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type)) - return 0; - } - - return 1; -} /* dns_rr_i_match() */ - - -static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) { - unsigned short rp; - struct dns_rr r0, rr; - int error; - - if ((i->section & DNS_S_QD) && P->memo.qd.base) - rp = P->memo.qd.base; - else if ((i->section & DNS_S_AN) && P->memo.an.base) - rp = P->memo.an.base; - else if ((i->section & DNS_S_NS) && P->memo.ns.base) - rp = P->memo.ns.base; - else if ((i->section & DNS_S_AR) && P->memo.ar.base) - rp = P->memo.ar.base; - else - rp = 12; - - for (; rp < P->end; rp = dns_rr_skip(rp, P)) { - if ((error = dns_rr_parse(&rr, rp, P))) - continue; - - rr.section = dns_rr_section(rp, P); - - if (!dns_rr_i_match(&rr, i, P)) - continue; - - r0 = rr; - - goto lower; - } - - return P->end; -lower: - if (i->sort == &dns_rr_i_packet) - return dns_rr_offset(&r0); - - while ((rp = dns_rr_skip(rp, P)) < P->end) { - if ((error = dns_rr_parse(&rr, rp, P))) - continue; - - rr.section = dns_rr_section(rp, P); - - if (!dns_rr_i_match(&rr, i, P)) - continue; - - if (i->sort(&rr, &r0, i, P) < 0) - r0 = rr; - } - - return dns_rr_offset(&r0); -} /* dns_rr_i_start() */ - - -static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) { - struct dns_rr r0, r1, rr; - int error; - - if ((error = dns_rr_parse(&r0, rp, P))) - return P->end; - - r0.section = dns_rr_section(rp, P); - - rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12; - - for (; rp < P->end; rp = dns_rr_skip(rp, P)) { - if ((error = dns_rr_parse(&rr, rp, P))) - continue; - - rr.section = dns_rr_section(rp, P); - - if (!dns_rr_i_match(&rr, i, P)) - continue; - - if (i->sort(&rr, &r0, i, P) <= 0) - continue; - - r1 = rr; - - goto lower; - } - - return P->end; -lower: - if (i->sort == &dns_rr_i_packet) - return dns_rr_offset(&r1); - - while ((rp = dns_rr_skip(rp, P)) < P->end) { - if ((error = dns_rr_parse(&rr, rp, P))) - continue; - - rr.section = dns_rr_section(rp, P); - - if (!dns_rr_i_match(&rr, i, P)) - continue; - - if (i->sort(&rr, &r0, i, P) <= 0) - continue; - - if (i->sort(&rr, &r1, i, P) >= 0) - continue; - - r1 = rr; - } - - return dns_rr_offset(&r1); -} /* dns_rr_i_skip() */ - - -int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { - (void)i; - (void)P; - - return (int)a->dn.p - (int)b->dn.p; -} /* dns_rr_i_packet() */ - - -int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { - int cmp; - - (void)i; - - if ((cmp = a->section - b->section)) - return cmp; - - if (a->type != b->type) - return (int)a->dn.p - (int)b->dn.p; - - return dns_rr_cmp(a, P, b, P); -} /* dns_rr_i_order() */ - - -int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { - int cmp; - - (void)i; - (void)P; - - while (!i->state.regs[0]) - i->state.regs[0] = dns_random(); - - if ((cmp = a->section - b->section)) - return cmp; - - return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]); -} /* dns_rr_i_shuffle() */ - - -struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P) { - static const struct dns_rr_i i_initializer; - - (void)P; - - i->state = i_initializer.state; - i->saved = i->state; - - return i; -} /* dns_rr_i_init() */ - - -unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) { - unsigned count = 0; - int error; - - switch (i->state.exec) { - case 0: - if (!i->sort) - i->sort = &dns_rr_i_packet; - - i->state.next = dns_rr_i_start(i, P); - i->state.exec++; - - /* FALL THROUGH */ - case 1: - while (count < lim && i->state.next < P->end) { - if ((error = dns_rr_parse(rr, i->state.next, P))) - goto error; - - rr->section = dns_rr_section(i->state.next, P); - - rr++; - count++; - i->state.count++; - - i->state.next = dns_rr_i_skip(i->state.next, i, P); - } /* while() */ - - break; - } /* switch() */ - - return count; -error: - *error_ = error; - - return count; -} /* dns_rr_grep() */ - - -size_t dns_rr_print(void *_dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *_error) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - union dns_any any; - size_t n; - int error; - - if (rr->section == DNS_S_QD) - dns_b_putc(&dst, ';'); - - if (!(n = dns_d_expand(any.ns.host, sizeof any.ns.host, rr->dn.p, P, &error))) - goto error; - dns_b_put(&dst, any.ns.host, DNS_PP_MIN(n, sizeof any.ns.host - 1)); - - if (rr->section != DNS_S_QD) { - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, rr->ttl, 0); - } - - dns_b_putc(&dst, ' '); - dns_b_puts(&dst, dns_strclass(rr->class)); - dns_b_putc(&dst, ' '); - dns_b_puts(&dst, dns_strtype(rr->type)); - - if (rr->section == DNS_S_QD) - goto epilog; - - dns_b_putc(&dst, ' '); - - if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P))) - goto error; - - n = dns_any_print(dst.p, dst.pe - dst.p, &any, rr->type); - dst.p += DNS_PP_MIN(n, (size_t)(dst.pe - dst.p)); -epilog: - return dns_b_strllen(&dst); -error: - *_error = error; - - return 0; -} /* dns_rr_print() */ - - -int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) { - unsigned long addr; - - if (rr->rd.len != 4) - return DNS_EILLEGAL; - - addr = ((0xffU & P->data[rr->rd.p + 0]) << 24) - | ((0xffU & P->data[rr->rd.p + 1]) << 16) - | ((0xffU & P->data[rr->rd.p + 2]) << 8) - | ((0xffU & P->data[rr->rd.p + 3]) << 0); - - a->addr.s_addr = htonl(addr); - - return 0; -} /* dns_a_parse() */ - - -int dns_a_push(struct dns_packet *P, struct dns_a *a) { - unsigned long addr; - - if (P->size - P->end < 6) - return DNS_ENOBUFS; - - P->data[P->end++] = 0x00; - P->data[P->end++] = 0x04; - - addr = ntohl(a->addr.s_addr); - - P->data[P->end++] = 0xffU & (addr >> 24); - P->data[P->end++] = 0xffU & (addr >> 16); - P->data[P->end++] = 0xffU & (addr >> 8); - P->data[P->end++] = 0xffU & (addr >> 0); - - return 0; -} /* dns_a_push() */ - - -size_t dns_a_arpa(void *_dst, size_t lim, const struct dns_a *a) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - unsigned long octets = ntohl(a->addr.s_addr); - unsigned i; - - for (i = 0; i < 4; i++) { - dns_b_fmtju(&dst, 0xff & octets, 0); - dns_b_putc(&dst, '.'); - octets >>= 8; - } - - dns_b_puts(&dst, "in-addr.arpa."); - - return dns_b_strllen(&dst); -} /* dns_a_arpa() */ - - -int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) { - if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr)) - return -1; - if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr)) - return 1; - - return 0; -} /* dns_a_cmp() */ - - -size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) { - char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0"; - - dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr); - - return dns_strlcpy(dst, addr, lim); -} /* dns_a_print() */ - - -int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) { - if (rr->rd.len != sizeof aaaa->addr.s6_addr) - return DNS_EILLEGAL; - - memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr); - - return 0; -} /* dns_aaaa_parse() */ - - -int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) { - if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr) - return DNS_ENOBUFS; - - P->data[P->end++] = 0x00; - P->data[P->end++] = 0x10; - - memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr); - - P->end += sizeof aaaa->addr.s6_addr; - - return 0; -} /* dns_aaaa_push() */ - - -int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) { - unsigned i; - int cmp; - - for (i = 0; i < lengthof(a->addr.s6_addr); i++) { - if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i]))) - return cmp; - } - - return 0; -} /* dns_aaaa_cmp() */ - - -size_t dns_aaaa_arpa(void *_dst, size_t lim, const struct dns_aaaa *aaaa) { - static const unsigned char hex[16] = "0123456789abcdef"; - struct dns_buf dst = DNS_B_INTO(_dst, lim); - unsigned nyble; - int i, j; - - for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) { - nyble = aaaa->addr.s6_addr[i]; - - for (j = 0; j < 2; j++) { - dns_b_putc(&dst, hex[0x0f & nyble]); - dns_b_putc(&dst, '.'); - nyble >>= 4; - } - } - - dns_b_puts(&dst, "ip6.arpa."); - - return dns_b_strllen(&dst); -} /* dns_aaaa_arpa() */ - - -size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) { - char addr[INET6_ADDRSTRLEN + 1] = "::"; - - dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr); - - return dns_strlcpy(dst, addr, lim); -} /* dns_aaaa_print() */ - - -int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) { - size_t len; - int error; - - if (rr->rd.len < 3) - return DNS_EILLEGAL; - - mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8)) - | (0x00ff & (P->data[rr->rd.p + 1] << 0)); - - if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error))) - return error; - else if (len >= sizeof mx->host) - return DNS_EILLEGAL; - - return 0; -} /* dns_mx_parse() */ - - -int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) { - size_t end, len; - int error; - - if (P->size - P->end < 5) - return DNS_ENOBUFS; - - end = P->end; - P->end += 2; - - P->data[P->end++] = 0xff & (mx->preference >> 8); - P->data[P->end++] = 0xff & (mx->preference >> 0); - - if ((error = dns_d_push(P, mx->host, strlen(mx->host)))) - goto error; - - len = P->end - end - 2; - - P->data[end + 0] = 0xff & (len >> 8); - P->data[end + 1] = 0xff & (len >> 0); - - return 0; -error: - P->end = end; - - return error; -} /* dns_mx_push() */ - - -int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) { - int cmp; - - if ((cmp = a->preference - b->preference)) - return cmp; - - return strcasecmp(a->host, b->host); -} /* dns_mx_cmp() */ - - -size_t dns_mx_print(void *_dst, size_t lim, struct dns_mx *mx) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - - dns_b_fmtju(&dst, mx->preference, 0); - dns_b_putc(&dst, ' '); - dns_b_puts(&dst, mx->host); - - return dns_b_strllen(&dst); -} /* dns_mx_print() */ - - -size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) { - return dns_strlcpy(dst, mx->host, lim); -} /* dns_mx_cname() */ - - -int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) { - size_t len; - int error; - - if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error))) - return error; - else if (len >= sizeof ns->host) - return DNS_EILLEGAL; - - return 0; -} /* dns_ns_parse() */ - - -int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) { - size_t end, len; - int error; - - if (P->size - P->end < 3) - return DNS_ENOBUFS; - - end = P->end; - P->end += 2; - - if ((error = dns_d_push(P, ns->host, strlen(ns->host)))) - goto error; - - len = P->end - end - 2; - - P->data[end + 0] = 0xff & (len >> 8); - P->data[end + 1] = 0xff & (len >> 0); - - return 0; -error: - P->end = end; - - return error; -} /* dns_ns_push() */ - - -int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) { - return strcasecmp(a->host, b->host); -} /* dns_ns_cmp() */ - - -size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) { - return dns_strlcpy(dst, ns->host, lim); -} /* dns_ns_print() */ - - -size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) { - return dns_strlcpy(dst, ns->host, lim); -} /* dns_ns_cname() */ - - -int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) { - return dns_ns_parse((struct dns_ns *)cname, rr, P); -} /* dns_cname_parse() */ - - -int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) { - return dns_ns_push(P, (struct dns_ns *)cname); -} /* dns_cname_push() */ - - -int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) { - return strcasecmp(a->host, b->host); -} /* dns_cname_cmp() */ - - -size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) { - return dns_ns_print(dst, lim, (struct dns_ns *)cname); -} /* dns_cname_print() */ - - -size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) { - return dns_strlcpy(dst, cname->host, lim); -} /* dns_cname_cname() */ - - -int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) { - struct { void *dst; size_t lim; } dn[] = - { { soa->mname, sizeof soa->mname }, - { soa->rname, sizeof soa->rname } }; - unsigned *ts[] = - { &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum }; - unsigned short rp; - unsigned i, j, n; - int error; - - /* MNAME / RNAME */ - if ((rp = rr->rd.p) >= P->end) - return DNS_EILLEGAL; - - for (i = 0; i < lengthof(dn); i++) { - if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error))) - return error; - else if (n >= dn[i].lim) - return DNS_EILLEGAL; - - if ((rp = dns_d_skip(rp, P)) >= P->end) - return DNS_EILLEGAL; - } - - /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ - for (i = 0; i < lengthof(ts); i++) { - for (j = 0; j < 4; j++, rp++) { - if (rp >= P->end) - return DNS_EILLEGAL; - - *ts[i] <<= 8; - *ts[i] |= (0xff & P->data[rp]); - } - } - - return 0; -} /* dns_soa_parse() */ - - -int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) { - void *dn[] = { soa->mname, soa->rname }; - unsigned ts[] = { (0xffffffff & soa->serial), - (0x7fffffff & soa->refresh), - (0x7fffffff & soa->retry), - (0x7fffffff & soa->expire), - (0xffffffff & soa->minimum) }; - unsigned i, j; - size_t end, len; - int error; - - end = P->end; - - if ((P->end += 2) >= P->size) - goto toolong; - - /* MNAME / RNAME */ - for (i = 0; i < lengthof(dn); i++) { - if ((error = dns_d_push(P, dn[i], strlen(dn[i])))) - goto error; - } - - /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ - for (i = 0; i < lengthof(ts); i++) { - if ((P->end += 4) >= P->size) - goto toolong; - - for (j = 1; j <= 4; j++) { - P->data[P->end - j] = (0xff & ts[i]); - ts[i] >>= 8; - } - } - - len = P->end - end - 2; - P->data[end + 0] = (0xff & (len >> 8)); - P->data[end + 1] = (0xff & (len >> 0)); - - return 0; -toolong: - error = DNS_ENOBUFS; - - /* FALL THROUGH */ -error: - P->end = end; - - return error; -} /* dns_soa_push() */ - - -int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) { - int cmp; - - if ((cmp = strcasecmp(a->mname, b->mname))) - return cmp; - - if ((cmp = strcasecmp(a->rname, b->rname))) - return cmp; - - if (a->serial > b->serial) - return -1; - else if (a->serial < b->serial) - return 1; - - if (a->refresh > b->refresh) - return -1; - else if (a->refresh < b->refresh) - return 1; - - if (a->retry > b->retry) - return -1; - else if (a->retry < b->retry) - return 1; - - if (a->expire > b->expire) - return -1; - else if (a->expire < b->expire) - return 1; - - if (a->minimum > b->minimum) - return -1; - else if (a->minimum < b->minimum) - return 1; - - return 0; -} /* dns_soa_cmp() */ - - -size_t dns_soa_print(void *_dst, size_t lim, struct dns_soa *soa) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - - dns_b_puts(&dst, soa->mname); - dns_b_putc(&dst, ' '); - dns_b_puts(&dst, soa->rname); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, soa->serial, 0); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, soa->refresh, 0); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, soa->retry, 0); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, soa->expire, 0); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, soa->minimum, 0); - - return dns_b_strllen(&dst); -} /* dns_soa_print() */ - - -int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) { - unsigned short rp; - unsigned i; - size_t n; - int error; - - memset(srv, '\0', sizeof *srv); - - rp = rr->rd.p; - - if (rr->rd.len < 7) - return DNS_EILLEGAL; - - for (i = 0; i < 2; i++, rp++) { - srv->priority <<= 8; - srv->priority |= (0xff & P->data[rp]); - } - - for (i = 0; i < 2; i++, rp++) { - srv->weight <<= 8; - srv->weight |= (0xff & P->data[rp]); - } - - for (i = 0; i < 2; i++, rp++) { - srv->port <<= 8; - srv->port |= (0xff & P->data[rp]); - } - - if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error))) - return error; - else if (n >= sizeof srv->target) - return DNS_EILLEGAL; - - return 0; -} /* dns_srv_parse() */ - - -int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) { - size_t end, len; - int error; - - end = P->end; - - if (P->size - P->end < 2) - goto toolong; - - P->end += 2; - - if (P->size - P->end < 6) - goto toolong; - - P->data[P->end++] = 0xff & (srv->priority >> 8); - P->data[P->end++] = 0xff & (srv->priority >> 0); - - P->data[P->end++] = 0xff & (srv->weight >> 8); - P->data[P->end++] = 0xff & (srv->weight >> 0); - - P->data[P->end++] = 0xff & (srv->port >> 8); - P->data[P->end++] = 0xff & (srv->port >> 0); - - if (0 == (len = dns_d_comp(&P->data[P->end], P->size - P->end, srv->target, strlen(srv->target), P, &error))) - goto error; - else if (P->size - P->end < len) - goto toolong; - - P->end += len; - - if (P->end > 65535) - goto toolong; - - len = P->end - end - 2; - - P->data[end + 0] = 0xff & (len >> 8); - P->data[end + 1] = 0xff & (len >> 0); - - return 0; -toolong: - error = DNS_ENOBUFS; - - /* FALL THROUGH */ -error: - P->end = end; - - return error; -} /* dns_srv_push() */ - - -int dns_srv_cmp(const struct dns_srv *a, const struct dns_srv *b) { - int cmp; - - if ((cmp = a->priority - b->priority)) - return cmp; - - /* - * FIXME: We need some sort of random seed to implement the dynamic - * weighting required by RFC 2782. - */ - if ((cmp = a->weight - b->weight)) - return cmp; - - if ((cmp = a->port - b->port)) - return cmp; - - return strcasecmp(a->target, b->target); -} /* dns_srv_cmp() */ - - -size_t dns_srv_print(void *_dst, size_t lim, struct dns_srv *srv) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - - dns_b_fmtju(&dst, srv->priority, 0); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, srv->weight, 0); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, srv->port, 0); - dns_b_putc(&dst, ' '); - dns_b_puts(&dst, srv->target); - - return dns_b_strllen(&dst); -} /* dns_srv_print() */ - - -size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) { - return dns_strlcpy(dst, srv->target, lim); -} /* dns_srv_cname() */ - - -unsigned int dns_opt_ttl(const struct dns_opt *opt) { - unsigned int ttl = 0; - - ttl |= (0xffU & opt->rcode) << 24; - ttl |= (0xffU & opt->version) << 16; - ttl |= (0xffffU & opt->flags) << 0; - - return ttl; -} /* dns_opt_ttl() */ - - -unsigned short dns_opt_class(const struct dns_opt *opt) { - return opt->maxudp; -} /* dns_opt_class() */ - - -struct dns_opt *dns_opt_init(struct dns_opt *opt, size_t size) { - assert(size >= offsetof(struct dns_opt, data)); - - opt->size = size - offsetof(struct dns_opt, data); - opt->len = 0; - - opt->rcode = 0; - opt->version = 0; - opt->maxudp = 0; - - return opt; -} /* dns_opt_init() */ - - -static union dns_any *dns_opt_initany(union dns_any *any, size_t size) { - return dns_opt_init(&any->opt, size), any; -} /* dns_opt_initany() */ - - -int dns_opt_parse(struct dns_opt *opt, struct dns_rr *rr, struct dns_packet *P) { - struct dns_buf src = DNS_B_FROM(&P->data[rr->rd.p], rr->rd.len); - struct dns_buf dst = DNS_B_INTO(opt->data, opt->size); - int error; - - opt->rcode = 0xfff & ((rr->ttl >> 20) | DNS_HEADER(P)->rcode); - opt->version = 0xff & (rr->ttl >> 16); - opt->flags = 0xffff & rr->ttl; - opt->maxudp = 0xffff & rr->class; - - while (src.p < src.pe) { - int code, len; - - if (-1 == (code = dns_b_get16(&src, -1))) - return src.error; - if (-1 == (len = dns_b_get16(&src, -1))) - return src.error; - - switch (code) { - default: - dns_b_put16(&dst, code); - dns_b_put16(&dst, len); - if ((error = dns_b_move(&dst, &src, len))) - return error; - break; - } - } - - return 0; -} /* dns_opt_parse() */ - - -int dns_opt_push(struct dns_packet *P, struct dns_opt *opt) { - struct dns_buf src = DNS_B_FROM(opt->data, opt->len); - struct dns_buf dst = DNS_B_INTO(&P->data[P->end], (P->size - P->end)); - int error; - - /* rdata length (see below) */ - if ((error = dns_b_put16(&dst, 0))) - goto error; - - /* ... push known options here */ - - /* push opaque option data */ - if ((error = dns_b_move(&dst, &src, (size_t)(src.pe - src.p)))) - goto error; - - /* rdata length */ - if ((error = dns_b_pput16(&dst, dns_b_tell(&dst) - 2, 0))) - goto error; - -#if !DNS_DEBUG_OPT_FORMERR - P->end += dns_b_tell(&dst); -#endif - - return 0; -error: - return error; -} /* dns_opt_push() */ - - -int dns_opt_cmp(const struct dns_opt *a, const struct dns_opt *b) { - (void)a; - (void)b; - - return -1; -} /* dns_opt_cmp() */ - - -size_t dns_opt_print(void *_dst, size_t lim, struct dns_opt *opt) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - size_t p; - - dns_b_putc(&dst, '"'); - - for (p = 0; p < opt->len; p++) { - dns_b_putc(&dst, '\\'); - dns_b_fmtju(&dst, opt->data[p], 3); - } - - dns_b_putc(&dst, '"'); - - return dns_b_strllen(&dst); -} /* dns_opt_print() */ - - -int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) { - return dns_ns_parse((struct dns_ns *)ptr, rr, P); -} /* dns_ptr_parse() */ - - -int dns_ptr_push(struct dns_packet *P, struct dns_ptr *ptr) { - return dns_ns_push(P, (struct dns_ns *)ptr); -} /* dns_ptr_push() */ - - -size_t dns_ptr_qname(void *dst, size_t lim, int af, void *addr) { - switch (af) { - case AF_INET6: - return dns_aaaa_arpa(dst, lim, addr); - case AF_INET: - return dns_a_arpa(dst, lim, addr); - default: { - struct dns_a a; - a.addr.s_addr = INADDR_NONE; - return dns_a_arpa(dst, lim, &a); - } - } -} /* dns_ptr_qname() */ - - -int dns_ptr_cmp(const struct dns_ptr *a, const struct dns_ptr *b) { - return strcasecmp(a->host, b->host); -} /* dns_ptr_cmp() */ - - -size_t dns_ptr_print(void *dst, size_t lim, struct dns_ptr *ptr) { - return dns_ns_print(dst, lim, (struct dns_ns *)ptr); -} /* dns_ptr_print() */ - - -size_t dns_ptr_cname(void *dst, size_t lim, struct dns_ptr *ptr) { - return dns_strlcpy(dst, ptr->host, lim); -} /* dns_ptr_cname() */ - - -int dns_sshfp_parse(struct dns_sshfp *fp, struct dns_rr *rr, struct dns_packet *P) { - unsigned p = rr->rd.p, pe = rr->rd.p + rr->rd.len; - - if (pe - p < 2) - return DNS_EILLEGAL; - - fp->algo = P->data[p++]; - fp->type = P->data[p++]; - - switch (fp->type) { - case DNS_SSHFP_SHA1: - if (pe - p < sizeof fp->digest.sha1) - return DNS_EILLEGAL; - - memcpy(fp->digest.sha1, &P->data[p], sizeof fp->digest.sha1); - - break; - default: - break; - } /* switch() */ - - return 0; -} /* dns_sshfp_parse() */ - - -int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) { - unsigned p = P->end, pe = P->size, n; - - if (pe - p < 4) - return DNS_ENOBUFS; - - p += 2; - P->data[p++] = 0xff & fp->algo; - P->data[p++] = 0xff & fp->type; - - switch (fp->type) { - case DNS_SSHFP_SHA1: - if (pe - p < sizeof fp->digest.sha1) - return DNS_ENOBUFS; - - memcpy(&P->data[p], fp->digest.sha1, sizeof fp->digest.sha1); - p += sizeof fp->digest.sha1; - - break; - default: - return DNS_EILLEGAL; - } /* switch() */ - - n = p - P->end - 2; - P->data[P->end++] = 0xff & (n >> 8); - P->data[P->end++] = 0xff & (n >> 0); - P->end = p; - - return 0; -} /* dns_sshfp_push() */ - - -int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) { - int cmp; - - if ((cmp = a->algo - b->algo) || (cmp = a->type - b->type)) - return cmp; - - switch (a->type) { - case DNS_SSHFP_SHA1: - return memcmp(a->digest.sha1, b->digest.sha1, sizeof a->digest.sha1); - default: - return 0; - } /* switch() */ - - /* NOT REACHED */ -} /* dns_sshfp_cmp() */ - - -size_t dns_sshfp_print(void *_dst, size_t lim, struct dns_sshfp *fp) { - static const unsigned char hex[16] = "0123456789abcdef"; - struct dns_buf dst = DNS_B_INTO(_dst, lim); - size_t i; - - dns_b_fmtju(&dst, fp->algo, 0); - dns_b_putc(&dst, ' '); - dns_b_fmtju(&dst, fp->type, 0); - dns_b_putc(&dst, ' '); - - switch (fp->type) { - case DNS_SSHFP_SHA1: - for (i = 0; i < sizeof fp->digest.sha1; i++) { - dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 4)]); - dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 0)]); - } - - break; - default: - dns_b_putc(&dst, '0'); - - break; - } /* switch() */ - - return dns_b_strllen(&dst); -} /* dns_sshfp_print() */ - - -struct dns_txt *dns_txt_init(struct dns_txt *txt, size_t size) { - assert(size > offsetof(struct dns_txt, data)); - - txt->size = size - offsetof(struct dns_txt, data); - txt->len = 0; - - return txt; -} /* dns_txt_init() */ - - -static union dns_any *dns_txt_initany(union dns_any *any, size_t size) { - /* NB: union dns_any is already initialized as struct dns_txt */ - (void)size; - return any; -} /* dns_txt_initany() */ - - -int dns_txt_parse(struct dns_txt *txt, struct dns_rr *rr, struct dns_packet *P) { - struct { unsigned char *b; size_t p, end; } dst, src; - unsigned n; - - dst.b = txt->data; - dst.p = 0; - dst.end = txt->size; - - src.b = P->data; - src.p = rr->rd.p; - src.end = src.p + rr->rd.len; - - while (src.p < src.end) { - n = 0xff & P->data[src.p++]; - - if (src.end - src.p < n || dst.end - dst.p < n) - return DNS_EILLEGAL; - - memcpy(&dst.b[dst.p], &src.b[src.p], n); - - dst.p += n; - src.p += n; - } - - txt->len = dst.p; - - return 0; -} /* dns_txt_parse() */ - - -int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) { - struct { unsigned char *b; size_t p, end; } dst, src; - unsigned n; - - dst.b = P->data; - dst.p = P->end; - dst.end = P->size; - - src.b = txt->data; - src.p = 0; - src.end = txt->len; - - if (dst.end - dst.p < 2) - return DNS_ENOBUFS; - - n = txt->len + ((txt->len + 254) / 255); - - dst.b[dst.p++] = 0xff & (n >> 8); - dst.b[dst.p++] = 0xff & (n >> 0); - - while (src.p < src.end) { - n = DNS_PP_MIN(255, src.end - src.p); - - if (dst.p >= dst.end) - return DNS_ENOBUFS; - - dst.b[dst.p++] = n; - - if (dst.end - dst.p < n) - return DNS_ENOBUFS; - - memcpy(&dst.b[dst.p], &src.b[src.p], n); - - dst.p += n; - src.p += n; - } - - P->end = dst.p; - - return 0; -} /* dns_txt_push() */ - - -int dns_txt_cmp(const struct dns_txt *a, const struct dns_txt *b) { - (void)a; - (void)b; - - return -1; -} /* dns_txt_cmp() */ - - -size_t dns_txt_print(void *_dst, size_t lim, struct dns_txt *txt) { - struct dns_buf src = DNS_B_FROM(txt->data, txt->len); - struct dns_buf dst = DNS_B_INTO(_dst, lim); - unsigned i; - - if (src.p < src.pe) { - do { - dns_b_putc(&dst, '"'); - - for (i = 0; i < 256 && src.p < src.pe; i++, src.p++) { - if (*src.p < 32 || *src.p > 126 || *src.p == '"' || *src.p == '\\') { - dns_b_putc(&dst, '\\'); - dns_b_fmtju(&dst, *src.p, 3); - } else { - dns_b_putc(&dst, *src.p); - } - } - - dns_b_putc(&dst, '"'); - dns_b_putc(&dst, ' '); - } while (src.p < src.pe); - - dns_b_popc(&dst); - } else { - dns_b_putc(&dst, '"'); - dns_b_putc(&dst, '"'); - } - - return dns_b_strllen(&dst); -} /* dns_txt_print() */ - - -static const struct dns_rrtype { - enum dns_type type; - const char *name; - union dns_any *(*init)(union dns_any *, size_t); - int (*parse)(); - int (*push)(); - int (*cmp)(); - size_t (*print)(); - size_t (*cname)(); -} dns_rrtypes[] = { - { DNS_T_A, "A", 0, &dns_a_parse, &dns_a_push, &dns_a_cmp, &dns_a_print, 0, }, - { DNS_T_AAAA, "AAAA", 0, &dns_aaaa_parse, &dns_aaaa_push, &dns_aaaa_cmp, &dns_aaaa_print, 0, }, - { DNS_T_MX, "MX", 0, &dns_mx_parse, &dns_mx_push, &dns_mx_cmp, &dns_mx_print, &dns_mx_cname, }, - { DNS_T_NS, "NS", 0, &dns_ns_parse, &dns_ns_push, &dns_ns_cmp, &dns_ns_print, &dns_ns_cname, }, - { DNS_T_CNAME, "CNAME", 0, &dns_cname_parse, &dns_cname_push, &dns_cname_cmp, &dns_cname_print, &dns_cname_cname, }, - { DNS_T_SOA, "SOA", 0, &dns_soa_parse, &dns_soa_push, &dns_soa_cmp, &dns_soa_print, 0, }, - { DNS_T_SRV, "SRV", 0, &dns_srv_parse, &dns_srv_push, &dns_srv_cmp, &dns_srv_print, &dns_srv_cname, }, - { DNS_T_OPT, "OPT", &dns_opt_initany, &dns_opt_parse, &dns_opt_push, &dns_opt_cmp, &dns_opt_print, 0, }, - { DNS_T_PTR, "PTR", 0, &dns_ptr_parse, &dns_ptr_push, &dns_ptr_cmp, &dns_ptr_print, &dns_ptr_cname, }, - { DNS_T_TXT, "TXT", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, - { DNS_T_SPF, "SPF", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, - { DNS_T_SSHFP, "SSHFP", 0, &dns_sshfp_parse, &dns_sshfp_push, &dns_sshfp_cmp, &dns_sshfp_print, 0, }, - { DNS_T_AXFR, "AXFR", 0, 0, 0, 0, 0, 0, }, -}; /* dns_rrtypes[] */ - -static const struct dns_rrtype *dns_rrtype(enum dns_type type) { - const struct dns_rrtype *t; - - for (t = dns_rrtypes; t < endof(dns_rrtypes); t++) { - if (t->type == type && t->parse) { - return t; - } - } - - return NULL; -} /* dns_rrtype() */ - - -union dns_any *dns_any_init(union dns_any *any, size_t size) { - dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); - return (union dns_any *)dns_txt_init(&any->rdata, size); -} /* dns_any_init() */ - - -static size_t dns_any_sizeof(union dns_any *any) { - dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); - return offsetof(struct dns_txt, data) + any->rdata.size; -} /* dns_any_sizeof() */ - -static union dns_any *dns_any_reinit(union dns_any *any, const struct dns_rrtype *t) { - return (t->init)? t->init(any, dns_any_sizeof(any)) : any; -} /* dns_any_reinit() */ - -int dns_any_parse(union dns_any *any, struct dns_rr *rr, struct dns_packet *P) { - const struct dns_rrtype *t; - - if ((t = dns_rrtype(rr->type))) - return t->parse(dns_any_reinit(any, t), rr, P); - - if (rr->rd.len > any->rdata.size) - return DNS_EILLEGAL; - - memcpy(any->rdata.data, &P->data[rr->rd.p], rr->rd.len); - any->rdata.len = rr->rd.len; - - return 0; -} /* dns_any_parse() */ - - -int dns_any_push(struct dns_packet *P, union dns_any *any, enum dns_type type) { - const struct dns_rrtype *t; - - if ((t = dns_rrtype(type))) - return t->push(P, any); - - if (P->size - P->end < any->rdata.len + 2) - return DNS_ENOBUFS; - - P->data[P->end++] = 0xff & (any->rdata.len >> 8); - P->data[P->end++] = 0xff & (any->rdata.len >> 0); - - memcpy(&P->data[P->end], any->rdata.data, any->rdata.len); - P->end += any->rdata.len; - - return 0; -} /* dns_any_push() */ - - -int dns_any_cmp(const union dns_any *a, enum dns_type x, const union dns_any *b, enum dns_type y) { - const struct dns_rrtype *t; - int cmp; - - if ((cmp = x - y)) - return cmp; - - if ((t = dns_rrtype(x))) - return t->cmp(a, b); - - return -1; -} /* dns_any_cmp() */ - - -size_t dns_any_print(void *_dst, size_t lim, union dns_any *any, enum dns_type type) { - const struct dns_rrtype *t; - struct dns_buf src, dst; - - if ((t = dns_rrtype(type))) - return t->print(_dst, lim, any); - - dns_b_from(&src, any->rdata.data, any->rdata.len); - dns_b_into(&dst, _dst, lim); - - dns_b_putc(&dst, '"'); - - while (src.p < src.pe) { - dns_b_putc(&dst, '\\'); - dns_b_fmtju(&dst, *src.p++, 3); - } - - dns_b_putc(&dst, '"'); - - return dns_b_strllen(&dst); -} /* dns_any_print() */ - - -size_t dns_any_cname(void *dst, size_t lim, union dns_any *any, enum dns_type type) { - const struct dns_rrtype *t; - - if ((t = dns_rrtype(type)) && t->cname) - return t->cname(dst, lim, any); - - return 0; -} /* dns_any_cname() */ - - -/* - * H O S T S R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_hosts { - struct dns_hosts_entry { - char host[DNS_D_MAXNAME + 1]; - char arpa[73 + 1]; - - int af; - - union { - struct in_addr a4; - struct in6_addr a6; - } addr; - - _Bool alias; - - struct dns_hosts_entry *next; - } *head, **tail; - - dns_atomic_t refcount; -}; /* struct dns_hosts */ - - -struct dns_hosts *dns_hosts_open(int *error) { - static const struct dns_hosts hosts_initializer = { .refcount = 1 }; - struct dns_hosts *hosts; - - if (!(hosts = malloc(sizeof *hosts))) - goto syerr; - - *hosts = hosts_initializer; - - hosts->tail = &hosts->head; - - return hosts; -syerr: - *error = dns_syerr(); - - free(hosts); - - return 0; -} /* dns_hosts_open() */ - - -void dns_hosts_close(struct dns_hosts *hosts) { - struct dns_hosts_entry *ent, *xnt; - - if (!hosts || 1 != dns_hosts_release(hosts)) - return; - - for (ent = hosts->head; ent; ent = xnt) { - xnt = ent->next; - - free(ent); - } - - free(hosts); - - return; -} /* dns_hosts_close() */ - - -dns_refcount_t dns_hosts_acquire(struct dns_hosts *hosts) { - return dns_atomic_fetch_add(&hosts->refcount); -} /* dns_hosts_acquire() */ - - -dns_refcount_t dns_hosts_release(struct dns_hosts *hosts) { - return dns_atomic_fetch_sub(&hosts->refcount); -} /* dns_hosts_release() */ - - -struct dns_hosts *dns_hosts_mortal(struct dns_hosts *hosts) { - if (hosts) - dns_hosts_release(hosts); - - return hosts; -} /* dns_hosts_mortal() */ - - -struct dns_hosts *dns_hosts_local(int *error_) { - struct dns_hosts *hosts; - int error; - - if (!(hosts = dns_hosts_open(&error))) - goto error; - - if ((error = dns_hosts_loadpath(hosts, "/etc/hosts"))) - goto error; - - return hosts; -error: - *error_ = error; - - dns_hosts_close(hosts); - - return 0; -} /* dns_hosts_local() */ - - -#define dns_hosts_issep(ch) (dns_isspace(ch)) -#define dns_hosts_iscom(ch) ((ch) == '#' || (ch) == ';') - -int dns_hosts_loadfile(struct dns_hosts *hosts, FILE *fp) { - struct dns_hosts_entry ent; - char word[DNS_PP_MAX(INET6_ADDRSTRLEN, DNS_D_MAXNAME) + 1]; - unsigned wp, wc, skip; - int ch, error; - - rewind(fp); - - do { - memset(&ent, '\0', sizeof ent); - wc = 0; - skip = 0; - - do { - memset(word, '\0', sizeof word); - wp = 0; - - while (EOF != (ch = fgetc(fp)) && ch != '\n') { - skip |= !!dns_hosts_iscom(ch); - - if (skip) - continue; - - if (dns_hosts_issep(ch)) - break; - - if (wp < sizeof word - 1) - word[wp] = ch; - wp++; - } - - if (!wp) - continue; - - wc++; - - switch (wc) { - case 0: - break; - case 1: - ent.af = (strchr(word, ':'))? AF_INET6 : AF_INET; - skip = (1 != dns_inet_pton(ent.af, word, &ent.addr)); - - break; - default: - if (!wp) - break; - - dns_d_anchor(ent.host, sizeof ent.host, word, wp); - - if ((error = dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, (wc > 2)))) - return error; - - break; - } /* switch() */ - } while (ch != EOF && ch != '\n'); - } while (ch != EOF); - - return 0; -} /* dns_hosts_loadfile() */ - - -int dns_hosts_loadpath(struct dns_hosts *hosts, const char *path) { - FILE *fp; - int error; - - if (!(fp = dns_fopen(path, "rt", &error))) - return error; - - error = dns_hosts_loadfile(hosts, fp); - - fclose(fp); - - return error; -} /* dns_hosts_loadpath() */ - - -int dns_hosts_dump(struct dns_hosts *hosts, FILE *fp) { - struct dns_hosts_entry *ent, *xnt; - char addr[INET6_ADDRSTRLEN + 1]; - unsigned i; - - for (ent = hosts->head; ent; ent = xnt) { - xnt = ent->next; - - dns_inet_ntop(ent->af, &ent->addr, addr, sizeof addr); - - fputs(addr, fp); - - for (i = strlen(addr); i < INET_ADDRSTRLEN; i++) - fputc(' ', fp); - - fputc(' ', fp); - - fputs(ent->host, fp); - fputc('\n', fp); - } - - return 0; -} /* dns_hosts_dump() */ - - -int dns_hosts_insert(struct dns_hosts *hosts, int af, const void *addr, const void *host, _Bool alias) { - struct dns_hosts_entry *ent; - int error; - - if (!(ent = malloc(sizeof *ent))) - goto syerr; - - dns_d_anchor(ent->host, sizeof ent->host, host, strlen(host)); - - switch ((ent->af = af)) { - case AF_INET6: - memcpy(&ent->addr.a6, addr, sizeof ent->addr.a6); - - dns_aaaa_arpa(ent->arpa, sizeof ent->arpa, addr); - - break; - case AF_INET: - memcpy(&ent->addr.a4, addr, sizeof ent->addr.a4); - - dns_a_arpa(ent->arpa, sizeof ent->arpa, addr); - - break; - default: - error = EINVAL; - - goto error; - } /* switch() */ - - ent->alias = alias; - - ent->next = 0; - *hosts->tail = ent; - hosts->tail = &ent->next; - - return 0; -syerr: - error = dns_syerr(); -error: - free(ent); - - return error; -} /* dns_hosts_insert() */ - - -struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) { - struct dns_packet *P = dns_p_new(512); - struct dns_packet *A = 0; - struct dns_rr rr; - struct dns_hosts_entry *ent; - int error, af; - char qname[DNS_D_MAXNAME + 1]; - size_t qlen; - - if ((error = dns_rr_parse(&rr, 12, Q))) - goto error; - - if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, Q, &error))) - goto error; - else if (qlen >= sizeof qname) - goto toolong; - - if ((error = dns_p_push(P, DNS_S_QD, qname, qlen, rr.type, rr.class, 0, 0))) - goto error; - - switch (rr.type) { - case DNS_T_PTR: - for (ent = hosts->head; ent; ent = ent->next) { - if (ent->alias || 0 != strcasecmp(qname, ent->arpa)) - continue; - - if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, ent->host))) - goto error; - } - - break; - case DNS_T_AAAA: - af = AF_INET6; - - goto loop; - case DNS_T_A: - af = AF_INET; - -loop: for (ent = hosts->head; ent; ent = ent->next) { - if (ent->af != af || 0 != strcasecmp(qname, ent->host)) - continue; - - if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, &ent->addr))) - goto error; - } - - break; - default: - break; - } /* switch() */ - - - if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) - goto error; - - return A; -toolong: - error = DNS_EILLEGAL; -error: - *error_ = error; - - dns_p_free(A); - - return 0; -} /* dns_hosts_query() */ - - -/* - * R E S O L V . C O N F R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_resolv_conf *dns_resconf_open(int *error) { - static const struct dns_resolv_conf resconf_initializer = { - .lookup = "bf", - .family = { AF_INET, AF_INET6 }, - .options = { .ndots = 1, .timeout = 5, .attempts = 2, .tcp = DNS_RESCONF_TCP_ENABLE, }, - .iface = { .ss_family = AF_INET }, - }; - struct dns_resolv_conf *resconf; - struct sockaddr_in *sin; - size_t len; - - if (!(resconf = malloc(sizeof *resconf))) - goto syerr; - - *resconf = resconf_initializer; - - sin = (struct sockaddr_in *)&resconf->nameserver[0]; - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = INADDR_ANY; - sin->sin_port = htons(53); -#if defined(SA_LEN) - sin->sin_len = sizeof *sin; -#endif - - if (0 != gethostname(resconf->search[0], sizeof resconf->search[0])) - goto syerr; - - len = strlen(resconf->search[0]); - len = dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], len); - len = dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], len); - if (1 == len) /* gethostname() returned a string without any label */ - resconf->search[0][0] = '\0'; - - dns_resconf_acquire(resconf); - - return resconf; -syerr: - *error = dns_syerr(); - - free(resconf); - - return 0; -} /* dns_resconf_open() */ - - -void dns_resconf_close(struct dns_resolv_conf *resconf) { - if (!resconf || 1 != dns_resconf_release(resconf)) - return /* void */; - - free(resconf); -} /* dns_resconf_close() */ - - -dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *resconf) { - return dns_atomic_fetch_add(&resconf->_.refcount); -} /* dns_resconf_acquire() */ - - -dns_refcount_t dns_resconf_release(struct dns_resolv_conf *resconf) { - return dns_atomic_fetch_sub(&resconf->_.refcount); -} /* dns_resconf_release() */ - - -struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *resconf) { - if (resconf) - dns_resconf_release(resconf); - - return resconf; -} /* dns_resconf_mortal() */ - - -struct dns_resolv_conf *dns_resconf_local(int *error_) { - struct dns_resolv_conf *resconf; - int error; - - if (!(resconf = dns_resconf_open(&error))) - goto error; - - if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf"))) { - /* - * NOTE: Both the glibc and BIND9 resolvers ignore a missing - * /etc/resolv.conf, defaulting to a nameserver of - * 127.0.0.1. See also dns_hints_insert_resconf, and the - * default initialization of nameserver[0] in - * dns_resconf_open. - */ - if (error != ENOENT) - goto error; - } - - if ((error = dns_nssconf_loadpath(resconf, "/etc/nsswitch.conf"))) { - if (error != ENOENT) - goto error; - } - - return resconf; -error: - *error_ = error; - - dns_resconf_close(resconf); - - return 0; -} /* dns_resconf_local() */ - - -struct dns_resolv_conf *dns_resconf_root(int *error) { - struct dns_resolv_conf *resconf; - - if ((resconf = dns_resconf_local(error))) - resconf->options.recurse = 1; - - return resconf; -} /* dns_resconf_root() */ - - -static time_t dns_resconf_timeout(const struct dns_resolv_conf *resconf) { - return (time_t)DNS_PP_MIN(INT_MAX, resconf->options.timeout); -} /* dns_resconf_timeout() */ - - -enum dns_resconf_keyword { - DNS_RESCONF_NAMESERVER, - DNS_RESCONF_DOMAIN, - DNS_RESCONF_SEARCH, - DNS_RESCONF_LOOKUP, - DNS_RESCONF_FILE, - DNS_RESCONF_BIND, - DNS_RESCONF_CACHE, - DNS_RESCONF_FAMILY, - DNS_RESCONF_INET4, - DNS_RESCONF_INET6, - DNS_RESCONF_OPTIONS, - DNS_RESCONF_EDNS0, - DNS_RESCONF_NDOTS, - DNS_RESCONF_TIMEOUT, - DNS_RESCONF_ATTEMPTS, - DNS_RESCONF_ROTATE, - DNS_RESCONF_RECURSE, - DNS_RESCONF_SMART, - DNS_RESCONF_TCP, - DNS_RESCONF_TCPx, - DNS_RESCONF_INTERFACE, - DNS_RESCONF_ZERO, - DNS_RESCONF_ONE, - DNS_RESCONF_ENABLE, - DNS_RESCONF_ONLY, - DNS_RESCONF_DISABLE, -}; /* enum dns_resconf_keyword */ - -static enum dns_resconf_keyword dns_resconf_keyword(const char *word) { - static const char *words[] = { - [DNS_RESCONF_NAMESERVER] = "nameserver", - [DNS_RESCONF_DOMAIN] = "domain", - [DNS_RESCONF_SEARCH] = "search", - [DNS_RESCONF_LOOKUP] = "lookup", - [DNS_RESCONF_FILE] = "file", - [DNS_RESCONF_BIND] = "bind", - [DNS_RESCONF_CACHE] = "cache", - [DNS_RESCONF_FAMILY] = "family", - [DNS_RESCONF_INET4] = "inet4", - [DNS_RESCONF_INET6] = "inet6", - [DNS_RESCONF_OPTIONS] = "options", - [DNS_RESCONF_EDNS0] = "edns0", - [DNS_RESCONF_ROTATE] = "rotate", - [DNS_RESCONF_RECURSE] = "recurse", - [DNS_RESCONF_SMART] = "smart", - [DNS_RESCONF_TCP] = "tcp", - [DNS_RESCONF_INTERFACE] = "interface", - [DNS_RESCONF_ZERO] = "0", - [DNS_RESCONF_ONE] = "1", - [DNS_RESCONF_ENABLE] = "enable", - [DNS_RESCONF_ONLY] = "only", - [DNS_RESCONF_DISABLE] = "disable", - }; - unsigned i; - - for (i = 0; i < lengthof(words); i++) { - if (words[i] && 0 == strcasecmp(words[i], word)) - return i; - } - - if (0 == strncasecmp(word, "ndots:", sizeof "ndots:" - 1)) - return DNS_RESCONF_NDOTS; - - if (0 == strncasecmp(word, "timeout:", sizeof "timeout:" - 1)) - return DNS_RESCONF_TIMEOUT; - - if (0 == strncasecmp(word, "attempts:", sizeof "attempts:" - 1)) - return DNS_RESCONF_ATTEMPTS; - - if (0 == strncasecmp(word, "tcp:", sizeof "tcp:" - 1)) - return DNS_RESCONF_TCPx; - - return -1; -} /* dns_resconf_keyword() */ - - -/** OpenBSD-style "[1.2.3.4]:53" nameserver syntax */ -int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) { - struct { char buf[128], *p; } addr = { "", addr.buf }; - unsigned short port = 0; - int ch, af = AF_INET, error; - - while ((ch = *src++)) { - switch (ch) { - case ' ': - /* FALL THROUGH */ - case '\t': - break; - case '[': - break; - case ']': - while ((ch = *src++)) { - if (dns_isdigit(ch)) { - port *= 10; - port += ch - '0'; - } - } - - goto inet; - case ':': - af = AF_INET6; - - /* FALL THROUGH */ - default: - if (addr.p < endof(addr.buf) - 1) - *addr.p++ = ch; - - break; - } /* switch() */ - } /* while() */ -inet: - - if ((error = dns_pton(af, addr.buf, dns_sa_addr(af, ss, NULL)))) - return error; - - port = (!port)? 53 : port; - *dns_sa_port(af, ss) = htons(port); - dns_sa_family(ss) = af; - - return 0; -} /* dns_resconf_pton() */ - -#define dns_resconf_issep(ch) (dns_isspace(ch) || (ch) == ',') -#define dns_resconf_iscom(ch) ((ch) == '#' || (ch) == ';') - -int dns_resconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { - unsigned sa_count = 0; - char words[6][DNS_D_MAXNAME + 1]; - unsigned wp, wc, i, j, n; - int ch, error; - - rewind(fp); - - do { - memset(words, '\0', sizeof words); - wp = 0; - wc = 0; - - while (EOF != (ch = getc(fp)) && ch != '\n') { - if (dns_resconf_issep(ch)) { - if (wp > 0) { - wp = 0; - - if (++wc >= lengthof(words)) - goto skip; - } - } else if (dns_resconf_iscom(ch)) { -skip: - do { - ch = getc(fp); - } while (ch != EOF && ch != '\n'); - - break; - } else if (wp < sizeof words[wc] - 1) { - words[wc][wp++] = ch; - } else { - wp = 0; /* drop word */ - goto skip; - } - } - - if (wp > 0) - wc++; - - if (wc < 2) - continue; - - switch (dns_resconf_keyword(words[0])) { - case DNS_RESCONF_NAMESERVER: - if (sa_count >= lengthof(resconf->nameserver)) - continue; - - if ((error = dns_resconf_pton(&resconf->nameserver[sa_count], words[1]))) - continue; - - sa_count++; - - break; - case DNS_RESCONF_DOMAIN: - case DNS_RESCONF_SEARCH: - memset(resconf->search, '\0', sizeof resconf->search); - - for (i = 1, j = 0; i < wc && j < lengthof(resconf->search); i++, j++) - dns_d_anchor(resconf->search[j], sizeof resconf->search[j], words[i], strlen(words[i])); - - break; - case DNS_RESCONF_LOOKUP: - for (i = 1, j = 0; i < wc && j < lengthof(resconf->lookup); i++) { - switch (dns_resconf_keyword(words[i])) { - case DNS_RESCONF_FILE: - resconf->lookup[j++] = 'f'; - - break; - case DNS_RESCONF_BIND: - resconf->lookup[j++] = 'b'; - - break; - case DNS_RESCONF_CACHE: - resconf->lookup[j++] = 'c'; - - break; - default: - break; - } /* switch() */ - } /* for() */ - - break; - case DNS_RESCONF_FAMILY: - for (i = 1, j = 0; i < wc && j < lengthof(resconf->family); i++) { - switch (dns_resconf_keyword(words[i])) { - case DNS_RESCONF_INET4: - resconf->family[j++] = AF_INET; - - break; - case DNS_RESCONF_INET6: - resconf->family[j++] = AF_INET6; - - break; - default: - break; - } - } - - break; - case DNS_RESCONF_OPTIONS: - for (i = 1; i < wc; i++) { - switch (dns_resconf_keyword(words[i])) { - case DNS_RESCONF_EDNS0: - resconf->options.edns0 = 1; - - break; - case DNS_RESCONF_NDOTS: - for (j = sizeof "ndots:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { - n *= 10; - n += words[i][j] - '0'; - } /* for() */ - - resconf->options.ndots = n; - - break; - case DNS_RESCONF_TIMEOUT: - for (j = sizeof "timeout:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { - n *= 10; - n += words[i][j] - '0'; - } /* for() */ - - resconf->options.timeout = n; - - break; - case DNS_RESCONF_ATTEMPTS: - for (j = sizeof "attempts:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { - n *= 10; - n += words[i][j] - '0'; - } /* for() */ - - resconf->options.attempts = n; - - break; - case DNS_RESCONF_ROTATE: - resconf->options.rotate = 1; - - break; - case DNS_RESCONF_RECURSE: - resconf->options.recurse = 1; - - break; - case DNS_RESCONF_SMART: - resconf->options.smart = 1; - - break; - case DNS_RESCONF_TCP: - resconf->options.tcp = DNS_RESCONF_TCP_ONLY; - - break; - case DNS_RESCONF_TCPx: - switch (dns_resconf_keyword(&words[i][sizeof "tcp:" - 1])) { - case DNS_RESCONF_ENABLE: - resconf->options.tcp = DNS_RESCONF_TCP_ENABLE; - - break; - case DNS_RESCONF_ONE: - case DNS_RESCONF_ONLY: - resconf->options.tcp = DNS_RESCONF_TCP_ONLY; - - break; - case DNS_RESCONF_ZERO: - case DNS_RESCONF_DISABLE: - resconf->options.tcp = DNS_RESCONF_TCP_DISABLE; - - break; - default: - break; - } /* switch() */ - - break; - default: - break; - } /* switch() */ - } /* for() */ - - break; - case DNS_RESCONF_INTERFACE: - for (i = 0, n = 0; dns_isdigit(words[2][i]); i++) { - n *= 10; - n += words[2][i] - '0'; - } - - dns_resconf_setiface(resconf, words[1], n); - - break; - default: - break; - } /* switch() */ - } while (ch != EOF); - - return 0; -} /* dns_resconf_loadfile() */ - - -int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { - FILE *fp; - int error; - - if (!(fp = dns_fopen(path, "rt", &error))) - return error; - - error = dns_resconf_loadfile(resconf, fp); - - fclose(fp); - - return error; -} /* dns_resconf_loadpath() */ - - -struct dns_anyconf { - char *token[16]; - unsigned count; - char buffer[1024], *tp, *cp; -}; /* struct dns_anyconf */ - - -static void dns_anyconf_reset(struct dns_anyconf *cf) { - cf->count = 0; - cf->tp = cf->cp = cf->buffer; -} /* dns_anyconf_reset() */ - - -static int dns_anyconf_push(struct dns_anyconf *cf) { - if (!(cf->cp < endof(cf->buffer) && cf->count < lengthof(cf->token))) - return ENOMEM; - - *cf->cp++ = '\0'; - cf->token[cf->count++] = cf->tp; - cf->tp = cf->cp; - - return 0; -} /* dns_anyconf_push() */ - - -static void dns_anyconf_pop(struct dns_anyconf *cf) { - if (cf->count > 0) { - --cf->count; - cf->tp = cf->cp = cf->token[cf->count]; - cf->token[cf->count] = 0; - } -} /* dns_anyconf_pop() */ - - -static int dns_anyconf_addc(struct dns_anyconf *cf, int ch) { - if (!(cf->cp < endof(cf->buffer))) - return ENOMEM; - - *cf->cp++ = ch; - - return 0; -} /* dns_anyconf_addc() */ - - -static _Bool dns_anyconf_match(const char *pat, int mc) { - _Bool match; - int pc; - - if (*pat == '^') { - match = 0; - ++pat; - } else { - match = 1; - } - - while ((pc = *(const unsigned char *)pat++)) { - switch (pc) { - case '%': - if (!(pc = *(const unsigned char *)pat++)) - return !match; - - switch (pc) { - case 'a': - if (dns_isalpha(mc)) - return match; - break; - case 'd': - if (dns_isdigit(mc)) - return match; - break; - case 'w': - if (dns_isalnum(mc)) - return match; - break; - case 's': - if (dns_isspace(mc)) - return match; - break; - default: - if (mc == pc) - return match; - break; - } /* switch() */ - - break; - default: - if (mc == pc) - return match; - break; - } /* switch() */ - } /* while() */ - - return !match; -} /* dns_anyconf_match() */ - - -static int dns_anyconf_peek(FILE *fp) { - int ch; - ch = getc(fp); - ungetc(ch, fp); - return ch; -} /* dns_anyconf_peek() */ - - -static size_t dns_anyconf_skip(const char *pat, FILE *fp) { - size_t count = 0; - int ch; - - while (EOF != (ch = getc(fp))) { - if (dns_anyconf_match(pat, ch)) { - count++; - continue; - } - - ungetc(ch, fp); - - break; - } - - return count; -} /* dns_anyconf_skip() */ - - -static size_t dns_anyconf_scan(struct dns_anyconf *cf, const char *pat, FILE *fp, int *error) { - size_t len; - int ch; - - while (EOF != (ch = getc(fp))) { - if (dns_anyconf_match(pat, ch)) { - if ((*error = dns_anyconf_addc(cf, ch))) - return 0; - - continue; - } else { - ungetc(ch, fp); - - break; - } - } - - if ((len = cf->cp - cf->tp)) { - if ((*error = dns_anyconf_push(cf))) - return 0; - - return len; - } else { - *error = 0; - - return 0; - } -} /* dns_anyconf_scan() */ - - -DNS_NOTUSED static void dns_anyconf_dump(struct dns_anyconf *cf, FILE *fp) { - unsigned i; - - fprintf(fp, "tokens:"); - - for (i = 0; i < cf->count; i++) { - fprintf(fp, " %s", cf->token[i]); - } - - fputc('\n', fp); -} /* dns_anyconf_dump() */ - - -enum dns_nssconf_keyword { - DNS_NSSCONF_INVALID = 0, - DNS_NSSCONF_HOSTS = 1, - DNS_NSSCONF_SUCCESS, - DNS_NSSCONF_NOTFOUND, - DNS_NSSCONF_UNAVAIL, - DNS_NSSCONF_TRYAGAIN, - DNS_NSSCONF_CONTINUE, - DNS_NSSCONF_RETURN, - DNS_NSSCONF_FILES, - DNS_NSSCONF_DNS, - DNS_NSSCONF_MDNS, - - DNS_NSSCONF_LAST, -}; /* enum dns_nssconf_keyword */ - -static enum dns_nssconf_keyword dns_nssconf_keyword(const char *word) { - static const char *list[] = { - [DNS_NSSCONF_HOSTS] = "hosts", - [DNS_NSSCONF_SUCCESS] = "success", - [DNS_NSSCONF_NOTFOUND] = "notfound", - [DNS_NSSCONF_UNAVAIL] = "unavail", - [DNS_NSSCONF_TRYAGAIN] = "tryagain", - [DNS_NSSCONF_CONTINUE] = "continue", - [DNS_NSSCONF_RETURN] = "return", - [DNS_NSSCONF_FILES] = "files", - [DNS_NSSCONF_DNS] = "dns", - [DNS_NSSCONF_MDNS] = "mdns", - }; - unsigned i; - - for (i = 1; i < lengthof(list); i++) { - if (list[i] && 0 == strcasecmp(list[i], word)) - return i; - } - - return DNS_NSSCONF_INVALID; -} /* dns_nssconf_keyword() */ - - -static enum dns_nssconf_keyword dns_nssconf_c2k(int ch) { - static const char map[] = { - ['S'] = DNS_NSSCONF_SUCCESS, - ['N'] = DNS_NSSCONF_NOTFOUND, - ['U'] = DNS_NSSCONF_UNAVAIL, - ['T'] = DNS_NSSCONF_TRYAGAIN, - ['C'] = DNS_NSSCONF_CONTINUE, - ['R'] = DNS_NSSCONF_RETURN, - ['f'] = DNS_NSSCONF_FILES, - ['F'] = DNS_NSSCONF_FILES, - ['d'] = DNS_NSSCONF_DNS, - ['D'] = DNS_NSSCONF_DNS, - ['b'] = DNS_NSSCONF_DNS, - ['B'] = DNS_NSSCONF_DNS, - ['m'] = DNS_NSSCONF_MDNS, - ['M'] = DNS_NSSCONF_MDNS, - }; - - return (ch >= 0 && ch < (int)lengthof(map))? map[ch] : DNS_NSSCONF_INVALID; -} /* dns_nssconf_c2k() */ - - -DNS_PRAGMA_PUSH -DNS_PRAGMA_QUIET - -static int dns_nssconf_k2c(int k) { - static const char map[DNS_NSSCONF_LAST] = { - [DNS_NSSCONF_SUCCESS] = 'S', - [DNS_NSSCONF_NOTFOUND] = 'N', - [DNS_NSSCONF_UNAVAIL] = 'U', - [DNS_NSSCONF_TRYAGAIN] = 'T', - [DNS_NSSCONF_CONTINUE] = 'C', - [DNS_NSSCONF_RETURN] = 'R', - [DNS_NSSCONF_FILES] = 'f', - [DNS_NSSCONF_DNS] = 'b', - [DNS_NSSCONF_MDNS] = 'm', - }; - - return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : '?') : '?'; -} /* dns_nssconf_k2c() */ - -static const char *dns_nssconf_k2s(int k) { - static const char *const map[DNS_NSSCONF_LAST] = { - [DNS_NSSCONF_SUCCESS] = "SUCCESS", - [DNS_NSSCONF_NOTFOUND] = "NOTFOUND", - [DNS_NSSCONF_UNAVAIL] = "UNAVAIL", - [DNS_NSSCONF_TRYAGAIN] = "TRYAGAIN", - [DNS_NSSCONF_CONTINUE] = "continue", - [DNS_NSSCONF_RETURN] = "return", - [DNS_NSSCONF_FILES] = "files", - [DNS_NSSCONF_DNS] = "dns", - [DNS_NSSCONF_MDNS] = "mdns", - }; - - return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : "") : ""; -} /* dns_nssconf_k2s() */ - -DNS_PRAGMA_POP - - -int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { - enum dns_nssconf_keyword source, status, action; - char lookup[sizeof resconf->lookup] = "", *lp; - struct dns_anyconf cf; - size_t i; - int error; - - while (!feof(fp) && !ferror(fp)) { - dns_anyconf_reset(&cf); - - dns_anyconf_skip("%s", fp); - - if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) - goto nextent; - - if (DNS_NSSCONF_HOSTS != dns_nssconf_keyword(cf.token[0])) - goto nextent; - - dns_anyconf_pop(&cf); - - if (!dns_anyconf_skip(": \t", fp)) - goto nextent; - - *(lp = lookup) = '\0'; - - while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { - dns_anyconf_skip(" \t", fp); - - if ('[' == dns_anyconf_peek(fp)) { - dns_anyconf_skip("[! \t", fp); - - while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { - dns_anyconf_skip("= \t", fp); - if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) { - dns_anyconf_pop(&cf); /* discard status */ - dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */ - break; - } - dns_anyconf_skip(" \t", fp); - } - - dns_anyconf_skip("] \t", fp); - } - - if ((size_t)(endof(lookup) - lp) < cf.count + 1) /* +1 for '\0' */ - goto nextsrc; - - source = dns_nssconf_keyword(cf.token[0]); - - switch (source) { - case DNS_NSSCONF_DNS: - case DNS_NSSCONF_MDNS: - case DNS_NSSCONF_FILES: - *lp++ = dns_nssconf_k2c(source); - break; - default: - goto nextsrc; - } - - for (i = 1; i + 1 < cf.count; i += 2) { - status = dns_nssconf_keyword(cf.token[i]); - action = dns_nssconf_keyword(cf.token[i + 1]); - - switch (status) { - case DNS_NSSCONF_SUCCESS: - case DNS_NSSCONF_NOTFOUND: - case DNS_NSSCONF_UNAVAIL: - case DNS_NSSCONF_TRYAGAIN: - *lp++ = dns_nssconf_k2c(status); - break; - default: - continue; - } - - switch (action) { - case DNS_NSSCONF_CONTINUE: - case DNS_NSSCONF_RETURN: - break; - default: - action = (status == DNS_NSSCONF_SUCCESS) - ? DNS_NSSCONF_RETURN - : DNS_NSSCONF_CONTINUE; - break; - } - - *lp++ = dns_nssconf_k2c(action); - } -nextsrc: - *lp = '\0'; - dns_anyconf_reset(&cf); - } -nextent: - dns_anyconf_skip("^\n", fp); - } - - if (*lookup) - strncpy(resconf->lookup, lookup, sizeof resconf->lookup); - - return 0; -} /* dns_nssconf_loadfile() */ - - -int dns_nssconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { - FILE *fp; - int error; - - if (!(fp = dns_fopen(path, "rt", &error))) - return error; - - error = dns_nssconf_loadfile(resconf, fp); - - fclose(fp); - - return error; -} /* dns_nssconf_loadpath() */ - - -struct dns_nssconf_source { - enum dns_nssconf_keyword source, success, notfound, unavail, tryagain; -}; /* struct dns_nssconf_source */ - -typedef unsigned dns_nssconf_i; - -static inline int dns_nssconf_peek(const struct dns_resolv_conf *resconf, dns_nssconf_i state) { - return (state < lengthof(resconf->lookup) && resconf->lookup[state])? resconf->lookup[state] : 0; -} /* dns_nssconf_peek() */ - -static _Bool dns_nssconf_next(struct dns_nssconf_source *src, const struct dns_resolv_conf *resconf, dns_nssconf_i *state) { - int source, status, action; - - src->source = DNS_NSSCONF_INVALID; - src->success = DNS_NSSCONF_RETURN; - src->notfound = DNS_NSSCONF_CONTINUE; - src->unavail = DNS_NSSCONF_CONTINUE; - src->tryagain = DNS_NSSCONF_CONTINUE; - - while ((source = dns_nssconf_peek(resconf, *state))) { - source = dns_nssconf_c2k(source); - ++*state; - - switch (source) { - case DNS_NSSCONF_FILES: - case DNS_NSSCONF_DNS: - case DNS_NSSCONF_MDNS: - src->source = source; - break; - default: - continue; - } - - while ((status = dns_nssconf_peek(resconf, *state)) && (action = dns_nssconf_peek(resconf, *state + 1))) { - status = dns_nssconf_c2k(status); - action = dns_nssconf_c2k(action); - - switch (action) { - case DNS_NSSCONF_RETURN: - case DNS_NSSCONF_CONTINUE: - break; - default: - goto done; - } - - switch (status) { - case DNS_NSSCONF_SUCCESS: - src->success = action; - break; - case DNS_NSSCONF_NOTFOUND: - src->notfound = action; - break; - case DNS_NSSCONF_UNAVAIL: - src->unavail = action; - break; - case DNS_NSSCONF_TRYAGAIN: - src->tryagain = action; - break; - default: - goto done; - } - - *state += 2; - } - - break; - } -done: - return src->source != DNS_NSSCONF_INVALID; -} /* dns_nssconf_next() */ - - -static int dns_nssconf_dump_status(int status, int action, unsigned *count, FILE *fp) { - switch (status) { - case DNS_NSSCONF_SUCCESS: - if (action == DNS_NSSCONF_RETURN) - return 0; - break; - default: - if (action == DNS_NSSCONF_CONTINUE) - return 0; - break; - } - - fputc(' ', fp); - - if (!*count) - fputc('[', fp); - - fprintf(fp, "%s=%s", dns_nssconf_k2s(status), dns_nssconf_k2s(action)); - - ++*count; - - return 0; -} /* dns_nssconf_dump_status() */ - - -int dns_nssconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { - struct dns_nssconf_source src; - dns_nssconf_i i = 0; - - fputs("hosts:", fp); - - while (dns_nssconf_next(&src, resconf, &i)) { - unsigned n = 0; - - fprintf(fp, " %s", dns_nssconf_k2s(src.source)); - - dns_nssconf_dump_status(DNS_NSSCONF_SUCCESS, src.success, &n, fp); - dns_nssconf_dump_status(DNS_NSSCONF_NOTFOUND, src.notfound, &n, fp); - dns_nssconf_dump_status(DNS_NSSCONF_UNAVAIL, src.unavail, &n, fp); - dns_nssconf_dump_status(DNS_NSSCONF_TRYAGAIN, src.tryagain, &n, fp); - - if (n) - fputc(']', fp); - } - - fputc('\n', fp); - - return 0; -} /* dns_nssconf_dump() */ - - -int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) { - int af = (strchr(addr, ':'))? AF_INET6 : AF_INET; - int error; - - if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface, NULL)))) - return error; - - *dns_sa_port(af, &resconf->iface) = htons(port); - resconf->iface.ss_family = af; - - return 0; -} /* dns_resconf_setiface() */ - - -#define DNS_SM_RESTORE \ - do { \ - pc = 0xff & (*state >> 0); \ - srchi = 0xff & (*state >> 8); \ - ndots = 0xff & (*state >> 16); \ - } while (0) - -#define DNS_SM_SAVE \ - do { \ - *state = ((0xff & pc) << 0) \ - | ((0xff & srchi) << 8) \ - | ((0xff & ndots) << 16); \ - } while (0) - -size_t dns_resconf_search(void *dst, size_t lim, const void *qname, size_t qlen, struct dns_resolv_conf *resconf, dns_resconf_i_t *state) { - unsigned pc, srchi, ndots, len; - - DNS_SM_ENTER; - - /* if FQDN then return as-is and finish */ - if (dns_d_isanchored(qname, qlen)) { - len = dns_d_anchor(dst, lim, qname, qlen); - DNS_SM_YIELD(len); - DNS_SM_EXIT; - } - - ndots = dns_d_ndots(qname, qlen); - - if (ndots >= resconf->options.ndots) { - len = dns_d_anchor(dst, lim, qname, qlen); - DNS_SM_YIELD(len); - } - - while (srchi < lengthof(resconf->search) && resconf->search[srchi][0]) { - struct dns_buf buf = DNS_B_INTO(dst, lim); - const char *dn = resconf->search[srchi++]; - - dns_b_put(&buf, qname, qlen); - dns_b_putc(&buf, '.'); - dns_b_puts(&buf, dn); - if (!dns_d_isanchored(dn, strlen(dn))) - dns_b_putc(&buf, '.'); - len = dns_b_strllen(&buf); - DNS_SM_YIELD(len); - } - - if (ndots < resconf->options.ndots) { - len = dns_d_anchor(dst, lim, qname, qlen); - DNS_SM_YIELD(len); - } - - DNS_SM_LEAVE; - - return dns_strlcpy(dst, "", lim); -} /* dns_resconf_search() */ - -#undef DNS_SM_SAVE -#undef DNS_SM_RESTORE - - -int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { - unsigned i; - int af; - - for (i = 0; i < lengthof(resconf->nameserver) && (af = resconf->nameserver[i].ss_family) != AF_UNSPEC; i++) { - char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; - unsigned short port; - - dns_inet_ntop(af, dns_sa_addr(af, &resconf->nameserver[i], NULL), addr, sizeof addr); - port = ntohs(*dns_sa_port(af, &resconf->nameserver[i])); - - if (port == 53) - fprintf(fp, "nameserver %s\n", addr); - else - fprintf(fp, "nameserver [%s]:%hu\n", addr, port); - } - - - fprintf(fp, "search"); - - for (i = 0; i < lengthof(resconf->search) && resconf->search[i][0]; i++) - fprintf(fp, " %s", resconf->search[i]); - - fputc('\n', fp); - - - fputs("; ", fp); - dns_nssconf_dump(resconf, fp); - - fprintf(fp, "lookup"); - - for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) { - switch (resconf->lookup[i]) { - case 'b': - fprintf(fp, " bind"); break; - case 'f': - fprintf(fp, " file"); break; - case 'c': - fprintf(fp, " cache"); break; - } - } - - fputc('\n', fp); - - - fprintf(fp, "options ndots:%u timeout:%u attempts:%u", resconf->options.ndots, resconf->options.timeout, resconf->options.attempts); - - if (resconf->options.edns0) - fprintf(fp, " edns0"); - if (resconf->options.rotate) - fprintf(fp, " rotate"); - if (resconf->options.recurse) - fprintf(fp, " recurse"); - if (resconf->options.smart) - fprintf(fp, " smart"); - - switch (resconf->options.tcp) { - case DNS_RESCONF_TCP_ENABLE: - break; - case DNS_RESCONF_TCP_ONLY: - fprintf(fp, " tcp"); - break; - case DNS_RESCONF_TCP_DISABLE: - fprintf(fp, " tcp:disable"); - break; - } - - fputc('\n', fp); - - - if ((af = resconf->iface.ss_family) != AF_UNSPEC) { - char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; - - dns_inet_ntop(af, dns_sa_addr(af, &resconf->iface, NULL), addr, sizeof addr); - - fprintf(fp, "interface %s %hu\n", addr, ntohs(*dns_sa_port(af, &resconf->iface))); - } - - return 0; -} /* dns_resconf_dump() */ - - -/* - * H I N T S E R V E R R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_hints_soa { - unsigned char zone[DNS_D_MAXNAME + 1]; - - struct { - struct sockaddr_storage ss; - unsigned priority; - } addrs[16]; - - unsigned count; - - struct dns_hints_soa *next; -}; /* struct dns_hints_soa */ - - -struct dns_hints { - dns_atomic_t refcount; - - struct dns_hints_soa *head; -}; /* struct dns_hints */ - - -struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf, int *error) { - static const struct dns_hints H_initializer; - struct dns_hints *H; - - (void)resconf; - - if (!(H = malloc(sizeof *H))) - goto syerr; - - *H = H_initializer; - - dns_hints_acquire(H); - - return H; -syerr: - *error = dns_syerr(); - - free(H); - - return 0; -} /* dns_hints_open() */ - - -void dns_hints_close(struct dns_hints *H) { - struct dns_hints_soa *soa, *nxt; - - if (!H || 1 != dns_hints_release(H)) - return /* void */; - - for (soa = H->head; soa; soa = nxt) { - nxt = soa->next; - - free(soa); - } - - free(H); - - return /* void */; -} /* dns_hints_close() */ - - -dns_refcount_t dns_hints_acquire(struct dns_hints *H) { - return dns_atomic_fetch_add(&H->refcount); -} /* dns_hints_acquire() */ - - -dns_refcount_t dns_hints_release(struct dns_hints *H) { - return dns_atomic_fetch_sub(&H->refcount); -} /* dns_hints_release() */ - - -struct dns_hints *dns_hints_mortal(struct dns_hints *hints) { - if (hints) - dns_hints_release(hints); - - return hints; -} /* dns_hints_mortal() */ - - -struct dns_hints *dns_hints_local(struct dns_resolv_conf *resconf, int *error_) { - struct dns_hints *hints = 0; - int error; - - if (resconf) - dns_resconf_acquire(resconf); - else if (!(resconf = dns_resconf_local(&error))) - goto error; - - if (!(hints = dns_hints_open(resconf, &error))) - goto error; - - error = 0; - - if (0 == dns_hints_insert_resconf(hints, ".", resconf, &error) && error) - goto error; - - dns_resconf_close(resconf); - - return hints; -error: - *error_ = error; - - dns_resconf_close(resconf); - dns_hints_close(hints); - - return 0; -} /* dns_hints_local() */ - - -struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) { - static const struct { - int af; - char addr[INET6_ADDRSTRLEN]; - } root_hints[] = { - { AF_INET, "198.41.0.4" }, /* A.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:503:ba3e::2:30" }, /* A.ROOT-SERVERS.NET. */ - { AF_INET, "192.228.79.201" }, /* B.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:500:84::b" }, /* B.ROOT-SERVERS.NET. */ - { AF_INET, "192.33.4.12" }, /* C.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:500:2::c" }, /* C.ROOT-SERVERS.NET. */ - { AF_INET, "199.7.91.13" }, /* D.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:500:2d::d" }, /* D.ROOT-SERVERS.NET. */ - { AF_INET, "192.203.230.10" }, /* E.ROOT-SERVERS.NET. */ - { AF_INET, "192.5.5.241" }, /* F.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:500:2f::f" }, /* F.ROOT-SERVERS.NET. */ - { AF_INET, "192.112.36.4" }, /* G.ROOT-SERVERS.NET. */ - { AF_INET, "128.63.2.53" }, /* H.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:500:1::803f:235" }, /* H.ROOT-SERVERS.NET. */ - { AF_INET, "192.36.148.17" }, /* I.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:7FE::53" }, /* I.ROOT-SERVERS.NET. */ - { AF_INET, "192.58.128.30" }, /* J.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:503:c27::2:30" }, /* J.ROOT-SERVERS.NET. */ - { AF_INET, "193.0.14.129" }, /* K.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:7FD::1" }, /* K.ROOT-SERVERS.NET. */ - { AF_INET, "199.7.83.42" }, /* L.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:500:3::42" }, /* L.ROOT-SERVERS.NET. */ - { AF_INET, "202.12.27.33" }, /* M.ROOT-SERVERS.NET. */ - { AF_INET6, "2001:DC3::35" }, /* M.ROOT-SERVERS.NET. */ - }; - struct dns_hints *hints = 0; - struct sockaddr_storage ss; - unsigned i; - int error, af; - - if (!(hints = dns_hints_open(resconf, &error))) - goto error; - - for (i = 0; i < lengthof(root_hints); i++) { - af = root_hints[i].af; - - if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss, NULL)))) - goto error; - - *dns_sa_port(af, &ss) = htons(53); - ss.ss_family = af; - - if ((error = dns_hints_insert(hints, ".", (struct sockaddr *)&ss, 1))) - goto error; - } - - return hints; -error: - *error_ = error; - - dns_hints_close(hints); - - return 0; -} /* dns_hints_root() */ - - -static struct dns_hints_soa *dns_hints_fetch(struct dns_hints *H, const char *zone) { - struct dns_hints_soa *soa; - - for (soa = H->head; soa; soa = soa->next) { - if (0 == strcasecmp(zone, (char *)soa->zone)) - return soa; - } - - return 0; -} /* dns_hints_fetch() */ - - -int dns_hints_insert(struct dns_hints *H, const char *zone, const struct sockaddr *sa, unsigned priority) { - static const struct dns_hints_soa soa_initializer; - struct dns_hints_soa *soa; - unsigned i; - - if (!(soa = dns_hints_fetch(H, zone))) { - if (!(soa = malloc(sizeof *soa))) - return dns_syerr(); - *soa = soa_initializer; - dns_strlcpy((char *)soa->zone, zone, sizeof soa->zone); - - soa->next = H->head; - H->head = soa; - } - - i = soa->count % lengthof(soa->addrs); - - memcpy(&soa->addrs[i].ss, sa, dns_sa_len(sa)); - - soa->addrs[i].priority = DNS_PP_MAX(1, priority); - - if (soa->count < lengthof(soa->addrs)) - soa->count++; - - return 0; -} /* dns_hints_insert() */ - - -static _Bool dns_hints_isinaddr_any(void *sa) { - struct in_addr *addr; - - if (dns_sa_family(sa) != AF_INET) - return 0; - - addr = dns_sa_addr(AF_INET, sa, NULL); - return addr->s_addr == htonl(INADDR_ANY); -} - -unsigned dns_hints_insert_resconf(struct dns_hints *H, const char *zone, struct dns_resolv_conf *resconf, int *error_) { - unsigned i, n, p; - int error; - - for (i = 0, n = 0, p = 1; i < lengthof(resconf->nameserver) && resconf->nameserver[i].ss_family != AF_UNSPEC; i++, n++) { - union { struct sockaddr_in sin; } tmp; - struct sockaddr *ns; - - /* - * dns_resconf_open initializes nameserver[0] to INADDR_ANY. - * - * Traditionally the semantics of 0.0.0.0 meant the default - * interface, which evolved to mean the loopback interface. - * See comment block preceding resolv/res_init.c:res_init in - * glibc 2.23. As of 2.23, glibc no longer translates - * 0.0.0.0 despite the code comment, but it does default to - * 127.0.0.1 when no nameservers are present. - * - * BIND9 as of 9.10.3 still translates 0.0.0.0 to 127.0.0.1. - * See lib/lwres/lwconfig.c:lwres_create_addr and the - * convert_zero flag. 127.0.0.1 is also the default when no - * nameservers are present. - */ - if (dns_hints_isinaddr_any(&resconf->nameserver[i])) { - memcpy(&tmp.sin, &resconf->nameserver[i], sizeof tmp.sin); - tmp.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - ns = (struct sockaddr *)&tmp.sin; - } else { - ns = (struct sockaddr *)&resconf->nameserver[i]; - } - - if ((error = dns_hints_insert(H, zone, ns, p))) - goto error; - - p += !resconf->options.rotate; - } - - return n; -error: - *error_ = error; - - return n; -} /* dns_hints_insert_resconf() */ - - -static int dns_hints_i_cmp(unsigned a, unsigned b, struct dns_hints_i *i, struct dns_hints_soa *soa) { - int cmp; - - if ((cmp = soa->addrs[a].priority - soa->addrs[b].priority)) - return cmp; - - return dns_k_shuffle16(a, i->state.seed) - dns_k_shuffle16(b, i->state.seed); -} /* dns_hints_i_cmp() */ - - -static unsigned dns_hints_i_start(struct dns_hints_i *i, struct dns_hints_soa *soa) { - unsigned p0, p; - - p0 = 0; - - for (p = 1; p < soa->count; p++) { - if (dns_hints_i_cmp(p, p0, i, soa) < 0) - p0 = p; - } - - return p0; -} /* dns_hints_i_start() */ - - -static unsigned dns_hints_i_skip(unsigned p0, struct dns_hints_i *i, struct dns_hints_soa *soa) { - unsigned pZ, p; - - for (pZ = 0; pZ < soa->count; pZ++) { - if (dns_hints_i_cmp(pZ, p0, i, soa) > 0) - goto cont; - } - - return soa->count; -cont: - for (p = pZ + 1; p < soa->count; p++) { - if (dns_hints_i_cmp(p, p0, i, soa) <= 0) - continue; - - if (dns_hints_i_cmp(p, pZ, i, soa) >= 0) - continue; - - pZ = p; - } - - - return pZ; -} /* dns_hints_i_skip() */ - - -static struct dns_hints_i *dns_hints_i_init(struct dns_hints_i *i, struct dns_hints *hints) { - static const struct dns_hints_i i_initializer; - struct dns_hints_soa *soa; - - i->state = i_initializer.state; - - do { - i->state.seed = dns_random(); - } while (0 == i->state.seed); - - if ((soa = dns_hints_fetch(hints, i->zone))) { - i->state.next = dns_hints_i_start(i, soa); - } - - return i; -} /* dns_hints_i_init() */ - - -unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, struct dns_hints_i *i, struct dns_hints *H) { - struct dns_hints_soa *soa; - unsigned n; - - if (!(soa = dns_hints_fetch(H, i->zone))) - return 0; - - n = 0; - - while (i->state.next < soa->count && n < lim) { - *sa = (struct sockaddr *)&soa->addrs[i->state.next].ss; - *sa_len = dns_sa_len(*sa); - - sa++; - sa_len++; - n++; - - i->state.next = dns_hints_i_skip(i->state.next, i, soa); - } - - return n; -} /* dns_hints_grep() */ - - -struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) { - struct dns_packet *A, *P; - struct dns_rr rr; - char zone[DNS_D_MAXNAME + 1]; - size_t zlen; - struct dns_hints_i i; - struct sockaddr *sa; - socklen_t slen; - int error; - - if (!dns_rr_grep(&rr, 1, dns_rr_i_new(Q, .section = DNS_S_QUESTION), Q, &error)) - goto error; - - if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error))) - goto error; - else if (zlen >= sizeof zone) - goto toolong; - - P = dns_p_new(512); - DNS_HEADER(P)->qr = 1; - - if ((error = dns_rr_copy(P, &rr, Q))) - goto error; - - if ((error = dns_p_push(P, DNS_S_AUTHORITY, ".", strlen("."), DNS_T_NS, DNS_C_IN, 0, "hints.local."))) - goto error; - - do { - i.zone = zone; - - dns_hints_i_init(&i, hints); - - while (dns_hints_grep(&sa, &slen, 1, &i, hints)) { - int af = sa->sa_family; - int rtype = (af == AF_INET6)? DNS_T_AAAA : DNS_T_A; - - if ((error = dns_p_push(P, DNS_S_ADDITIONAL, "hints.local.", strlen("hints.local."), rtype, DNS_C_IN, 0, dns_sa_addr(af, sa, NULL)))) - goto error; - } - } while ((zlen = dns_d_cleave(zone, sizeof zone, zone, zlen))); - - if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) - goto error; - - return A; -toolong: - error = DNS_EILLEGAL; -error: - *error_ = error; - - return 0; -} /* dns_hints_query() */ - - -/** ugly hack to support specifying ports other than 53 in resolv.conf. */ -static unsigned short dns_hints_port(struct dns_hints *hints, int af, void *addr) { - struct dns_hints_soa *soa; - void *addrsoa; - socklen_t addrlen; - unsigned short port; - unsigned i; - - for (soa = hints->head; soa; soa = soa->next) { - for (i = 0; i < soa->count; i++) { - if (af != soa->addrs[i].ss.ss_family) - continue; - - if (!(addrsoa = dns_sa_addr(af, &soa->addrs[i].ss, &addrlen))) - continue; - - if (memcmp(addr, addrsoa, addrlen)) - continue; - - port = *dns_sa_port(af, &soa->addrs[i].ss); - - return (port)? port : htons(53); - } - } - - return htons(53); -} /* dns_hints_port() */ - - -int dns_hints_dump(struct dns_hints *hints, FILE *fp) { - struct dns_hints_soa *soa; - char addr[INET6_ADDRSTRLEN]; - unsigned i; - int af, error; - - for (soa = hints->head; soa; soa = soa->next) { - fprintf(fp, "ZONE \"%s\"\n", soa->zone); - - for (i = 0; i < soa->count; i++) { - af = soa->addrs[i].ss.ss_family; - - if ((error = dns_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss, NULL), addr, sizeof addr))) - return error; - - fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss))); - } - } - - return 0; -} /* dns_hints_dump() */ - - -/* - * C A C H E R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -static dns_refcount_t dns_cache_acquire(struct dns_cache *cache) { - return dns_atomic_fetch_add(&cache->_.refcount); -} /* dns_cache_acquire() */ - - -static dns_refcount_t dns_cache_release(struct dns_cache *cache) { - return dns_atomic_fetch_sub(&cache->_.refcount); -} /* dns_cache_release() */ - - -static struct dns_packet *dns_cache_query(struct dns_packet *query, struct dns_cache *cache, int *error) { - (void)query; - (void)cache; - (void)error; - - return NULL; -} /* dns_cache_query() */ - - -static int dns_cache_submit(struct dns_packet *query, struct dns_cache *cache) { - (void)query; - (void)cache; - - return 0; -} /* dns_cache_submit() */ - - -static int dns_cache_check(struct dns_cache *cache) { - (void)cache; - - return 0; -} /* dns_cache_check() */ - - -static struct dns_packet *dns_cache_fetch(struct dns_cache *cache, int *error) { - (void)cache; - (void)error; - - return NULL; -} /* dns_cache_fetch() */ - - -static int dns_cache_pollfd(struct dns_cache *cache) { - (void)cache; - - return -1; -} /* dns_cache_pollfd() */ - - -static short dns_cache_events(struct dns_cache *cache) { - (void)cache; - - return 0; -} /* dns_cache_events() */ - - -static void dns_cache_clear(struct dns_cache *cache) { - (void)cache; - - return; -} /* dns_cache_clear() */ - - -struct dns_cache *dns_cache_init(struct dns_cache *cache) { - static const struct dns_cache c_init = { - .acquire = &dns_cache_acquire, - .release = &dns_cache_release, - .query = &dns_cache_query, - .submit = &dns_cache_submit, - .check = &dns_cache_check, - .fetch = &dns_cache_fetch, - .pollfd = &dns_cache_pollfd, - .events = &dns_cache_events, - .clear = &dns_cache_clear, - ._ = { .refcount = 1, }, - }; - - *cache = c_init; - - return cache; -} /* dns_cache_init() */ - - -void dns_cache_close(struct dns_cache *cache) { - if (cache) - cache->release(cache); -} /* dns_cache_close() */ - - -/* - * S O C K E T R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -static void dns_socketclose(int *fd, const struct dns_options *opts) { - if (opts && opts->closefd.cb) - opts->closefd.cb(fd, opts->closefd.arg); - - if (*fd != -1) { -#if _WIN32 - closesocket(*fd); -#else - close(*fd); -#endif - *fd = -1; - } -} /* dns_socketclose() */ - - -#ifndef HAVE_IOCTLSOCKET -#define HAVE_IOCTLSOCKET (_WIN32 || _WIN64) -#endif - -#ifndef HAVE_SOCK_CLOEXEC -#ifdef SOCK_CLOEXEC -#define HAVE_SOCK_CLOEXEC 1 -#else -#define HAVE_SOCK_CLOEXEC 0 -#endif -#endif - -#ifndef HAVE_SOCK_NONBLOCK -#ifdef SOCK_NONBLOCK -#define HAVE_SOCK_NONBLOCK 1 -#else -#define HAVE_SOCK_NONBLOCK 0 -#endif -#endif - -#define DNS_SO_MAXTRY 7 - -static int dns_socket(struct sockaddr *local, int type, int *error_) { - int fd = -1, flags, error; -#if defined FIONBIO - unsigned long opt; -#endif - - flags = 0; -#if HAVE_SOCK_CLOEXEC - flags |= SOCK_CLOEXEC; -#endif -#if HAVE_SOCK_NONBLOCK - flags |= SOCK_NONBLOCK; -#endif - if (-1 == (fd = socket(local->sa_family, type|flags, 0))) - goto soerr; - -#if defined F_SETFD && !HAVE_SOCK_CLOEXEC - if (-1 == fcntl(fd, F_SETFD, 1)) - goto syerr; -#endif - -#if defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK - if (-1 == (flags = fcntl(fd, F_GETFL))) - goto syerr; - if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) - goto syerr; -#elif defined FIONBIO && HAVE_IOCTLSOCKET - opt = 1; - if (0 != ioctlsocket(fd, FIONBIO, &opt)) - goto soerr; -#endif - -#if defined SO_NOSIGPIPE - if (type != SOCK_DGRAM) { - if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof (int))) - goto soerr; - } -#endif - - if (local->sa_family != AF_INET && local->sa_family != AF_INET6) - return fd; - - if (type != SOCK_DGRAM) - return fd; - - /* - * FreeBSD, Linux, OpenBSD, OS X, and Solaris use random ports by - * default. Though the ephemeral range is quite small on OS X - * (49152-65535 on 10.10) and Linux (32768-60999 on 4.4.0, Ubuntu - * Xenial). See also RFC 6056. - * - * TODO: Optionally rely on the kernel to select a random port. - */ - if (*dns_sa_port(local->sa_family, local) == 0) { - struct sockaddr_storage tmp; - unsigned i, port; - - memcpy(&tmp, local, dns_sa_len(local)); - - for (i = 0; i < DNS_SO_MAXTRY; i++) { - port = 1025 + (dns_random() % 64510); - - *dns_sa_port(tmp.ss_family, &tmp) = htons(port); - - if (0 == bind(fd, (struct sockaddr *)&tmp, dns_sa_len(&tmp))) - return fd; - } - - /* NB: continue to next bind statement */ - } - - if (0 == bind(fd, local, dns_sa_len(local))) - return fd; - - /* FALL THROUGH */ -soerr: - error = dns_soerr(); - - goto error; -#if (defined F_SETFD && !HAVE_SOCK_CLOEXEC) || (defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK) -syerr: - error = dns_syerr(); - - goto error; -#endif -error: - *error_ = error; - - dns_socketclose(&fd, NULL); - - return -1; -} /* dns_socket() */ - - -enum { - DNS_SO_UDP_INIT = 1, - DNS_SO_UDP_CONN, - DNS_SO_UDP_SEND, - DNS_SO_UDP_RECV, - DNS_SO_UDP_DONE, - - DNS_SO_TCP_INIT, - DNS_SO_TCP_CONN, - DNS_SO_TCP_SEND, - DNS_SO_TCP_RECV, - DNS_SO_TCP_DONE, -}; - -struct dns_socket { - struct dns_options opts; - - int udp; - int tcp; - - int *old; - unsigned onum, olim; - - int type; - - struct sockaddr_storage local, remote; - - struct dns_k_permutor qids; - - struct dns_stat stat; - - /* - * NOTE: dns_so_reset() zeroes everything from here down. - */ - int state; - - unsigned short qid; - char qname[DNS_D_MAXNAME + 1]; - size_t qlen; - enum dns_type qtype; - enum dns_class qclass; - - struct dns_packet *query; - size_t qout; - - struct dns_clock elapsed; - - struct dns_packet *answer; - size_t alen, apos; -}; /* struct dns_socket */ - - -/* - * NOTE: Actual closure delayed so that kqueue(2) and epoll(2) callers have - * a chance to recognize a state change after installing a persistent event - * and where sequential descriptors with the same integer value returned - * from _pollfd() would be ambiguous. See dns_so_closefds(). - */ -static int dns_so_closefd(struct dns_socket *so, int *fd) { - int error; - - if (*fd == -1) - return 0; - - if (so->opts.closefd.cb) { - if ((error = so->opts.closefd.cb(fd, so->opts.closefd.arg))) { - return error; - } else if (*fd == -1) - return 0; - } - - if (!(so->onum < so->olim)) { - unsigned olim = DNS_PP_MAX(4, so->olim * 2); - void *old; - - if (!(old = realloc(so->old, sizeof so->old[0] * olim))) - return dns_syerr(); - - so->old = old; - so->olim = olim; - } - - so->old[so->onum++] = *fd; - *fd = -1; - - return 0; -} /* dns_so_closefd() */ - - -#define DNS_SO_CLOSE_UDP 0x01 -#define DNS_SO_CLOSE_TCP 0x02 -#define DNS_SO_CLOSE_OLD 0x04 -#define DNS_SO_CLOSE_ALL (DNS_SO_CLOSE_UDP|DNS_SO_CLOSE_TCP|DNS_SO_CLOSE_OLD) - -static void dns_so_closefds(struct dns_socket *so, int which) { - if (DNS_SO_CLOSE_UDP & which) - dns_socketclose(&so->udp, &so->opts); - if (DNS_SO_CLOSE_TCP & which) - dns_socketclose(&so->tcp, &so->opts); - if (DNS_SO_CLOSE_OLD & which) { - unsigned i; - for (i = 0; i < so->onum; i++) - dns_socketclose(&so->old[i], &so->opts); - so->onum = 0; - free(so->old); - so->old = 0; - so->olim = 0; - } -} /* dns_so_closefds() */ - - -static void dns_so_destroy(struct dns_socket *); - -static struct dns_socket *dns_so_init(struct dns_socket *so, const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { - static const struct dns_socket so_initializer = { .opts = DNS_OPTS_INITIALIZER, .udp = -1, .tcp = -1, }; - - *so = so_initializer; - so->type = type; - - if (opts) - so->opts = *opts; - - if (local) - memcpy(&so->local, local, dns_sa_len(local)); - - if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, error))) - goto error; - - dns_k_permutor_init(&so->qids, 1, 65535); - - return so; -error: - dns_so_destroy(so); - - return 0; -} /* dns_so_init() */ - - -struct dns_socket *dns_so_open(const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { - struct dns_socket *so; - - if (!(so = malloc(sizeof *so))) - goto syerr; - - if (!dns_so_init(so, local, type, opts, error)) - goto error; - - return so; -syerr: - *error = dns_syerr(); -error: - dns_so_close(so); - - return 0; -} /* dns_so_open() */ - - -static void dns_so_destroy(struct dns_socket *so) { - dns_so_reset(so); - dns_so_closefds(so, DNS_SO_CLOSE_ALL); -} /* dns_so_destroy() */ - - -void dns_so_close(struct dns_socket *so) { - if (!so) - return; - - dns_so_destroy(so); - - free(so); -} /* dns_so_close() */ - - -void dns_so_reset(struct dns_socket *so) { - dns_p_setptr(&so->answer, NULL); - - memset(&so->state, '\0', sizeof *so - offsetof(struct dns_socket, state)); -} /* dns_so_reset() */ - - -unsigned short dns_so_mkqid(struct dns_socket *so) { - return dns_k_permutor_step(&so->qids); -} /* dns_so_mkqid() */ - - -#define DNS_SO_MINBUF 768 - -static int dns_so_newanswer(struct dns_socket *so, size_t len) { - size_t size = offsetof(struct dns_packet, data) + DNS_PP_MAX(len, DNS_SO_MINBUF); - void *p; - - if (!(p = realloc(so->answer, size))) - return dns_syerr(); - - so->answer = dns_p_init(p, size); - - return 0; -} /* dns_so_newanswer() */ - - -int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host) { - struct dns_rr rr; - int error = DNS_EUNKNOWN; - - dns_so_reset(so); - - if ((error = dns_rr_parse(&rr, 12, Q))) - goto error; - - if (!(so->qlen = dns_d_expand(so->qname, sizeof so->qname, rr.dn.p, Q, &error))) - goto error; - /* - * NOTE: Don't bail if expansion is too long; caller may be - * intentionally sending long names. However, we won't be able to - * verify it on return. - */ - - so->qtype = rr.type; - so->qclass = rr.class; - - if ((error = dns_so_newanswer(so, (Q->memo.opt.maxudp)? Q->memo.opt.maxudp : DNS_SO_MINBUF))) - goto syerr; - - memcpy(&so->remote, host, dns_sa_len(host)); - - so->query = Q; - so->qout = 0; - - dns_begin(&so->elapsed); - - if (DNS_HEADER(so->query)->qid == 0) - DNS_HEADER(so->query)->qid = dns_so_mkqid(so); - - so->qid = DNS_HEADER(so->query)->qid; - so->state = (so->type == SOCK_STREAM)? DNS_SO_TCP_INIT : DNS_SO_UDP_INIT; - - so->stat.queries++; - - return 0; -syerr: - error = dns_syerr(); -error: - dns_so_reset(so); - - return error; -} /* dns_so_submit() */ - - -static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) { - char qname[DNS_D_MAXNAME + 1]; - size_t qlen; - struct dns_rr rr; - int error = -1; - - if (so->qid != DNS_HEADER(so->answer)->qid) - goto reject; - - if (!dns_p_count(so->answer, DNS_S_QD)) - goto reject; - - if (0 != dns_rr_parse(&rr, 12, so->answer)) - goto reject; - - if (rr.type != so->qtype || rr.class != so->qclass) - goto reject; - - if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, P, &error))) - goto error; - else if (qlen >= sizeof qname || qlen != so->qlen) - goto reject; - - if (0 != strcasecmp(so->qname, qname)) - goto reject; - - return 0; -reject: - error = DNS_EUNKNOWN; -error: - DNS_SHOW(P, "rejecting packet (%s)", dns_strerror(error)); - - return error; -} /* dns_so_verify() */ - - -static _Bool dns_so_tcp_keep(struct dns_socket *so) { - struct sockaddr_storage remote; - - if (so->tcp == -1) - return 0; - - if (0 != getpeername(so->tcp, (struct sockaddr *)&remote, &(socklen_t){ sizeof remote })) - return 0; - - return 0 == dns_sa_cmp(&remote, &so->remote); -} /* dns_so_tcp_keep() */ - - -#if defined __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warray-bounds" -#endif - -static int dns_so_tcp_send(struct dns_socket *so) { - unsigned char *qsrc; - size_t qend; - long n; - - so->query->data[-2] = 0xff & (so->query->end >> 8); - so->query->data[-1] = 0xff & (so->query->end >> 0); - - qsrc = &so->query->data[-2] + so->qout; - qend = so->query->end + 2; - - while (so->qout < qend) { - if (0 > (n = dns_send(so->tcp, (void *)&qsrc[so->qout], qend - so->qout, 0))) - return dns_soerr(); - - so->qout += n; - so->stat.tcp.sent.bytes += n; - } - - so->stat.tcp.sent.count++; - - return 0; -} /* dns_so_tcp_send() */ - - -static int dns_so_tcp_recv(struct dns_socket *so) { - unsigned char *asrc; - size_t aend, alen; - int error; - long n; - - aend = so->alen + 2; - - while (so->apos < aend) { - if (read_wait0(so->udp) <= 0) // add by zsx - return dns_soerr(); - - asrc = &so->answer->data[-2]; - - if (0 > (n = recv(so->tcp, (void *)&asrc[so->apos], aend - so->apos, 0))) - return dns_soerr(); - else if (n == 0) - return DNS_EUNKNOWN; /* FIXME */ - - so->apos += n; - so->stat.tcp.rcvd.bytes += n; - - if (so->alen == 0 && so->apos >= 2) { - alen = ((0xff & so->answer->data[-2]) << 8) - | ((0xff & so->answer->data[-1]) << 0); - - if ((error = dns_so_newanswer(so, alen))) - return error; - - so->alen = alen; - aend = alen + 2; - } - } - - so->answer->end = so->alen; - so->stat.tcp.rcvd.count++; - - return 0; -} /* dns_so_tcp_recv() */ - -#if __clang__ -#pragma clang diagnostic pop -#endif - - -int dns_so_check(struct dns_socket *so) { - int error; - long n; - -retry: - switch (so->state) { - case DNS_SO_UDP_INIT: - so->state++; - /* FALL THROUGH */ - case DNS_SO_UDP_CONN: - if (0 != connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) - goto soerr; - - so->state++; - /* FALL THROUGH */ - case DNS_SO_UDP_SEND: - if (0 > (n = send(so->udp, (void *)so->query->data, so->query->end, 0))) - goto soerr; - - so->stat.udp.sent.bytes += n; - so->stat.udp.sent.count++; - - so->state++; - /* FALL THROUGH */ - case DNS_SO_UDP_RECV: - if (read_wait0(so->udp) <= 0) // add by zsx - goto soerr; - - if (0 > (n = recv(so->udp, (void *)so->answer->data, so->answer->size, 0))) - goto soerr; - - so->stat.udp.rcvd.bytes += n; - so->stat.udp.rcvd.count++; - - if ((so->answer->end = n) < 12) - goto trash; - - if ((error = dns_so_verify(so, so->answer))) - goto trash; - - so->state++; - /* FALL THROUGH */ - case DNS_SO_UDP_DONE: - if (!DNS_HEADER(so->answer)->tc || so->type == SOCK_DGRAM) - return 0; - - so->state++; - /* FALL THROUGH */ - case DNS_SO_TCP_INIT: - if (dns_so_tcp_keep(so)) { - so->state = DNS_SO_TCP_SEND; - - goto retry; - } - - if ((error = dns_so_closefd(so, &so->tcp))) - goto error; - - if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) - goto error; - - so->state++; - /* FALL THROUGH */ - case DNS_SO_TCP_CONN: - if (0 != connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) { - if (dns_soerr() != DNS_EISCONN) - goto soerr; - } - - so->state++; - /* FALL THROUGH */ - case DNS_SO_TCP_SEND: - if ((error = dns_so_tcp_send(so))) - goto error; - - so->state++; - /* FALL THROUGH */ - case DNS_SO_TCP_RECV: - if ((error = dns_so_tcp_recv(so))) - goto error; - - so->state++; - /* FALL THROUGH */ - case DNS_SO_TCP_DONE: - /* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */ - if (so->type != SOCK_STREAM) { - if ((error = dns_so_closefd(so, &so->tcp))) - goto error; - } - - if (so->answer->end < 12) - return DNS_EILLEGAL; - - if ((error = dns_so_verify(so, so->answer))) - goto error; - - return 0; - default: - error = DNS_EUNKNOWN; - - goto error; - } /* switch() */ - -trash: - DNS_CARP("discarding packet"); - goto retry; -soerr: - error = dns_soerr(); - - goto error; -error: - switch (error) { - case DNS_EINTR: - goto retry; - case DNS_EINPROGRESS: - /* FALL THROUGH */ - case DNS_EALREADY: - /* FALL THROUGH */ -#if DNS_EWOULDBLOCK != DNS_EAGAIN - case DNS_EWOULDBLOCK: - /* FALL THROUGH */ -#endif - error = DNS_EAGAIN; - - break; - } /* switch() */ - - return error; -} /* dns_so_check() */ - - -struct dns_packet *dns_so_fetch(struct dns_socket *so, int *error) { - struct dns_packet *answer; - - switch (so->state) { - case DNS_SO_UDP_DONE: - case DNS_SO_TCP_DONE: - answer = so->answer; - so->answer = 0; - - return answer; - default: - *error = DNS_EUNKNOWN; - - return 0; - } -} /* dns_so_fetch() */ - - -struct dns_packet *dns_so_query(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host, int *error_) { - struct dns_packet *A; - int error; - - if (!so->state) { - if ((error = dns_so_submit(so, Q, host))) - goto error; - } - - if ((error = dns_so_check(so))) - goto error; - - if (!(A = dns_so_fetch(so, &error))) - goto error; - - dns_so_reset(so); - - return A; -error: - *error_ = error; - - return 0; -} /* dns_so_query() */ - - -time_t dns_so_elapsed(struct dns_socket *so) { - return dns_elapsed(&so->elapsed); -} /* dns_so_elapsed() */ - - -void dns_so_clear(struct dns_socket *so) { - dns_so_closefds(so, DNS_SO_CLOSE_OLD); -} /* dns_so_clear() */ - - -static int dns_so_events2(struct dns_socket *so, enum dns_events type) { - int events = 0; - - switch (so->state) { - case DNS_SO_UDP_CONN: - case DNS_SO_UDP_SEND: - events |= DNS_POLLOUT; - - break; - case DNS_SO_UDP_RECV: - events |= DNS_POLLIN; - - break; - case DNS_SO_TCP_CONN: - case DNS_SO_TCP_SEND: - events |= DNS_POLLOUT; - - break; - case DNS_SO_TCP_RECV: - events |= DNS_POLLIN; - - break; - } /* switch() */ - - switch (type) { - case DNS_LIBEVENT: - return DNS_POLL2EV(events); - default: - return events; - } /* switch() */ -} /* dns_so_events2() */ - - -int dns_so_events(struct dns_socket *so) { - return dns_so_events2(so, so->opts.events); -} /* dns_so_events() */ - - -int dns_so_pollfd(struct dns_socket *so) { - switch (so->state) { - case DNS_SO_UDP_CONN: - case DNS_SO_UDP_SEND: - case DNS_SO_UDP_RECV: - return so->udp; - case DNS_SO_TCP_CONN: - case DNS_SO_TCP_SEND: - case DNS_SO_TCP_RECV: - return so->tcp; - } /* switch() */ - - return -1; -} /* dns_so_pollfd() */ - - -int dns_so_poll(struct dns_socket *so, int timeout) { - return dns_poll(dns_so_pollfd(so), dns_so_events2(so, DNS_SYSPOLL), timeout); -} /* dns_so_poll() */ - - -const struct dns_stat *dns_so_stat(struct dns_socket *so) { - return &so->stat; -} /* dns_so_stat() */ - - -/* - * R E S O L V E R R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -enum dns_res_state { - DNS_R_INIT, - DNS_R_GLUE, - DNS_R_SWITCH, /* (B)IND, (F)ILE, (C)ACHE */ - - DNS_R_FILE, /* Lookup in local hosts database */ - - DNS_R_CACHE, /* Lookup in application cache */ - DNS_R_SUBMIT, - DNS_R_CHECK, - DNS_R_FETCH, - - DNS_R_BIND, /* Lookup in the network */ - DNS_R_SEARCH, - DNS_R_HINTS, - DNS_R_ITERATE, - DNS_R_FOREACH_NS, - DNS_R_RESOLV0_NS, /* Prologue: Setup next frame and recurse */ - DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */ - DNS_R_FOREACH_A, - DNS_R_QUERY_A, - DNS_R_CNAME0_A, - DNS_R_CNAME1_A, - - DNS_R_FINISH, - DNS_R_SMART0_A, - DNS_R_SMART1_A, - DNS_R_DONE, - DNS_R_SERVFAIL, -}; /* enum dns_res_state */ - - -#define DNS_R_MAXDEPTH 8 -#define DNS_R_ENDFRAME (DNS_R_MAXDEPTH - 1) - -struct dns_resolver { - struct dns_socket so; - - struct dns_resolv_conf *resconf; - struct dns_hosts *hosts; - struct dns_hints *hints; - struct dns_cache *cache; - - dns_atomic_t refcount; - - /* Reset zeroes everything below here. */ - - char qname[DNS_D_MAXNAME + 1]; - size_t qlen; - - enum dns_type qtype; - enum dns_class qclass; - - struct dns_clock elapsed; - - dns_resconf_i_t search; - - struct dns_rr_i smart; - - struct dns_packet *nodata; /* answer if nothing better */ - - unsigned sp; - - struct dns_res_frame { - enum dns_res_state state; - - int error; - int which; /* (B)IND, (F)ILE; index into resconf->lookup */ - int qflags; - - unsigned attempts; - - struct dns_packet *query, *answer, *hints; - - struct dns_rr_i hints_i, hints_j; - struct dns_rr hints_ns, ans_cname; - } stack[DNS_R_MAXDEPTH]; -}; /* struct dns_resolver */ - - -static int dns_res_tcp2type(int tcp) { - switch (tcp) { - case DNS_RESCONF_TCP_ONLY: - return SOCK_STREAM; - case DNS_RESCONF_TCP_DISABLE: - return SOCK_DGRAM; - default: - return 0; - } -} /* dns_res_tcp2type() */ - -struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *_error) { - static const struct dns_resolver R_initializer - = { .refcount = 1, }; - struct dns_resolver *R = 0; - int type, error; - - /* - * Grab ref count early because the caller may have passed us a mortal - * reference, and we want to do the right thing if we return early - * from an error. - */ - if (resconf) - dns_resconf_acquire(resconf); - if (hosts) - dns_hosts_acquire(hosts); - if (hints) - dns_hints_acquire(hints); - if (cache) - dns_cache_acquire(cache); - - /* - * Don't try to load it ourselves because a NULL object might be an - * error from, say, dns_resconf_root(), and loading - * dns_resconf_local() by default would create undesirable surpises. - */ - if (!resconf || !hosts || !hints) { - if (!*_error) - *_error = EINVAL; - goto _error; - } - - if (!(R = malloc(sizeof *R))) - goto syerr; - - *R = R_initializer; - type = dns_res_tcp2type(resconf->options.tcp); - - if (!dns_so_init(&R->so, (struct sockaddr *)&resconf->iface, type, opts, &error)) - goto error; - - R->resconf = resconf; - R->hosts = hosts; - R->hints = hints; - R->cache = cache; - - return R; -syerr: - error = dns_syerr(); -error: - *_error = error; -_error: - dns_res_close(R); - - dns_resconf_close(resconf); - dns_hosts_close(hosts); - dns_hints_close(hints); - dns_cache_close(cache); - - return 0; -} /* dns_res_open() */ - - -struct dns_resolver *dns_res_stub(const struct dns_options *opts, int *error) { - struct dns_resolv_conf *resconf = 0; - struct dns_hosts *hosts = 0; - struct dns_hints *hints = 0; - struct dns_resolver *res = 0; - - if (!(resconf = dns_resconf_local(error))) - goto epilog; - - if (!(hosts = dns_hosts_local(error))) - goto epilog; - - if (!(hints = dns_hints_local(resconf, error))) - goto epilog; - - if (!(res = dns_res_open(resconf, hosts, hints, NULL, opts, error))) - goto epilog; - -epilog: - dns_resconf_close(resconf); - dns_hosts_close(hosts); - dns_hints_close(hints); - - return res; -} /* dns_res_stub() */ - - -static void dns_res_frame_destroy(struct dns_resolver *R, struct dns_res_frame *frame) { - (void)R; - - dns_p_setptr(&frame->query, NULL); - dns_p_setptr(&frame->answer, NULL); - dns_p_setptr(&frame->hints, NULL); -} /* dns_res_frame_destroy() */ - - -static void dns_res_frame_init(struct dns_resolver *R, struct dns_res_frame *frame) { - memset(frame, '\0', sizeof *frame); - - /* - * NB: Can be invoked from dns_res_open, before R->resconf has been - * initialized. - */ - if (R->resconf) { - if (!R->resconf->options.recurse) - frame->qflags |= DNS_Q_RD; - if (R->resconf->options.edns0) - frame->qflags |= DNS_Q_EDNS0; - } -} /* dns_res_frame_init() */ - - -static void dns_res_frame_reset(struct dns_resolver *R, struct dns_res_frame *frame) { - dns_res_frame_destroy(R, frame); - dns_res_frame_init(R, frame); -} /* dns_res_frame_reset() */ - - -static dns_error_t dns_res_frame_prepare(struct dns_resolver *R, struct dns_res_frame *F, char *qname, enum dns_type qtype, enum dns_class qclass) { - struct dns_packet *P = NULL; - - if (!(F < endof(R->stack))) - return DNS_EUNKNOWN; - - dns_p_movptr(&P, &F->query); - dns_res_frame_reset(R, F); - dns_p_movptr(&F->query, &P); - - return dns_q_make(&F->query, qname, qtype, qclass, F->qflags); -} /* dns_res_frame_prepare() */ - - -void dns_res_reset(struct dns_resolver *R) { - unsigned i; - - dns_so_reset(&R->so); - dns_p_setptr(&R->nodata, NULL); - - for (i = 0; i < lengthof(R->stack); i++) - dns_res_frame_destroy(R, &R->stack[i]); - - memset(&R->qname, '\0', sizeof *R - offsetof(struct dns_resolver, qname)); - - for (i = 0; i < lengthof(R->stack); i++) - dns_res_frame_init(R, &R->stack[i]); -} /* dns_res_reset() */ - - -void dns_res_close(struct dns_resolver *R) { - if (!R || 1 < dns_res_release(R)) - return; - - dns_res_reset(R); - - dns_so_destroy(&R->so); - - dns_hints_close(R->hints); - dns_hosts_close(R->hosts); - dns_resconf_close(R->resconf); - dns_cache_close(R->cache); - - free(R); -} /* dns_res_close() */ - - -dns_refcount_t dns_res_acquire(struct dns_resolver *R) { - return dns_atomic_fetch_add(&R->refcount); -} /* dns_res_acquire() */ - - -dns_refcount_t dns_res_release(struct dns_resolver *R) { - return dns_atomic_fetch_sub(&R->refcount); -} /* dns_res_release() */ - - -struct dns_resolver *dns_res_mortal(struct dns_resolver *res) { - if (res) - dns_res_release(res); - return res; -} /* dns_res_mortal() */ - - -static struct dns_packet *dns_res_merge(struct dns_packet *P0, struct dns_packet *P1, int *error_) { - size_t bufsiz = P0->end + P1->end; - struct dns_packet *P[3] = { P0, P1, 0 }; - struct dns_rr rr[3]; - int error, copy, i; - enum dns_section section; - -retry: - if (!(P[2] = dns_p_make(bufsiz, &error))) - goto error; - - dns_rr_foreach(&rr[0], P[0], .section = DNS_S_QD) { - if ((error = dns_rr_copy(P[2], &rr[0], P[0]))) - goto error; - } - - for (section = DNS_S_AN; (DNS_S_ALL & section); section <<= 1) { - for (i = 0; i < 2; i++) { - dns_rr_foreach(&rr[i], P[i], .section = section) { - copy = 1; - - dns_rr_foreach(&rr[2], P[2], .type = rr[i].type, .section = (DNS_S_ALL & ~DNS_S_QD)) { - if (0 == dns_rr_cmp(&rr[i], P[i], &rr[2], P[2])) { - copy = 0; - - break; - } - } - - if (copy && (error = dns_rr_copy(P[2], &rr[i], P[i]))) { - if (error == DNS_ENOBUFS && bufsiz < 65535) { - dns_p_setptr(&P[2], NULL); - - bufsiz = DNS_PP_MAX(65535, bufsiz * 2); - - goto retry; - } - - goto error; - } - } /* foreach(rr) */ - } /* foreach(packet) */ - } /* foreach(section) */ - - return P[2]; -error: - *error_ = error; - - dns_p_free(P[2]); - - return 0; -} /* dns_res_merge() */ - - -static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) { - struct dns_packet *P = dns_p_new(512); - char qname[DNS_D_MAXNAME + 1]; - size_t qlen; - enum dns_type qtype; - struct dns_rr rr; - unsigned sp; - int error; - - if (!(qlen = dns_d_expand(qname, sizeof qname, 12, Q, &error)) - || qlen >= sizeof qname) - return 0; - - if (!(qtype = dns_rr_type(12, Q))) - return 0; - - if ((error = dns_p_push(P, DNS_S_QD, qname, strlen(qname), qtype, DNS_C_IN, 0, 0))) - return 0; - - for (sp = 0; sp <= R->sp; sp++) { - if (!R->stack[sp].answer) - continue; - - dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = qtype, .section = (DNS_S_ALL & ~DNS_S_QD)) { - rr.section = DNS_S_AN; - - if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) - return 0; - } - } - - if (dns_p_count(P, DNS_S_AN) > 0) - goto copy; - - /* Otherwise, look for a CNAME */ - for (sp = 0; sp <= R->sp; sp++) { - if (!R->stack[sp].answer) - continue; - - dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = DNS_T_CNAME, .section = (DNS_S_ALL & ~DNS_S_QD)) { - rr.section = DNS_S_AN; - - if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) - return 0; - } - } - - if (!dns_p_count(P, DNS_S_AN)) - return 0; - -copy: - return dns_p_copy(dns_p_make(P->end, &error), P); -} /* dns_res_glue() */ - - -/* - * Sort NS records by three criteria: - * - * 1) Whether glue is present. - * 2) Whether glue record is original or of recursive lookup. - * 3) Randomly shuffle records which share the above criteria. - * - * NOTE: Assumes only NS records passed, AND ASSUMES no new NS records will - * be added during an iteration. - * - * FIXME: Only groks A glue, not AAAA glue. - */ -static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { - _Bool glued[2] = { 0 }; - struct dns_rr x, y; - struct dns_ns ns; - int cmp, error; - - memset(&x, 0, sizeof(x)); - memset(&y, 0, sizeof(y)); - - if (!(error = dns_ns_parse(&ns, a, P))) - glued[0] = !!dns_rr_grep(&x, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); - - if (!(error = dns_ns_parse(&ns, b, P))) - glued[1] = !!dns_rr_grep(&y, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); - - if ((cmp = glued[1] - glued[0])) { - return cmp; - } else if ((cmp = (dns_rr_offset(&y) < i->args[0]) - (dns_rr_offset(&x) < i->args[0]))) { - return cmp; - } else { - return dns_rr_i_shuffle(a, b, i, P); - } -} /* dns_res_nameserv_cmp() */ - - -#define dgoto(sp, i) \ - do { R->stack[(sp)].state = (i); goto exec; } while (0) - -static int dns_res_exec(struct dns_resolver *R) { - struct dns_res_frame *F; - struct dns_packet *P; - union { - char host[DNS_D_MAXNAME + 1]; - char name[DNS_D_MAXNAME + 1]; - struct dns_ns ns; - struct dns_cname cname; - } u; - size_t len; - struct dns_rr rr; - int error; - -exec: - - F = &R->stack[R->sp]; - - switch (F->state) { - case DNS_R_INIT: - F->state++; - /* FALL THROUGH */ - case DNS_R_GLUE: - if (R->sp == 0) - dgoto(R->sp, DNS_R_SWITCH); - - if (!F->query) - goto noquery; - - if (!(F->answer = dns_res_glue(R, F->query))) - dgoto(R->sp, DNS_R_SWITCH); - - if (!(len = dns_d_expand(u.name, sizeof u.name, 12, F->query, &error))) - goto error; - else if (len >= sizeof u.name) - goto toolong; - - dns_rr_foreach(&rr, F->answer, .name = u.name, .type = dns_rr_type(12, F->query), .section = DNS_S_AN) { - dgoto(R->sp, DNS_R_FINISH); - } - - dns_rr_foreach(&rr, F->answer, .name = u.name, .type = DNS_T_CNAME, .section = DNS_S_AN) { - F->ans_cname = rr; - - dgoto(R->sp, DNS_R_CNAME0_A); - } - - F->state++; - /* FALL THROUGH */ - case DNS_R_SWITCH: - while (F->which < (int)sizeof R->resconf->lookup && R->resconf->lookup[F->which]) { - switch (R->resconf->lookup[F->which++]) { - case 'b': case 'B': - dgoto(R->sp, DNS_R_BIND); - case 'f': case 'F': - dgoto(R->sp, DNS_R_FILE); - case 'c': case 'C': - if (R->cache) - dgoto(R->sp, DNS_R_CACHE); - - break; - default: - break; - } - } - - /* - * FIXME: Examine more closely whether our logic is correct - * and DNS_R_SERVFAIL is the correct default response. - * - * Case 1: We got here because we never got an answer on the - * wire. All queries timed-out and we reached maximum - * attempts count. See DNS_R_FOREACH_NS. In that case - * DNS_R_SERVFAIL is the correct state, unless we want to - * return DNS_ETIMEDOUT. - * - * Case 2: We were a stub resolver and got an unsatisfactory - * answer (empty ANSWER section) which caused us to jump - * back to DNS_R_SEARCH and ultimately to DNS_R_SWITCH. We - * return the answer returned from the wire, which we - * stashed in R->nodata. - * - * Case 3: We reached maximum attempts count as in case #1, - * but never got an authoritative response which caused us - * to short-circuit. See end of DNS_R_QUERY_A case. We - * should probably prepare R->nodata as in case #2. - */ - if (R->sp == 0 && R->nodata) { /* XXX: can we just return nodata regardless? */ - dns_p_movptr(&F->answer, &R->nodata); - dgoto(R->sp, DNS_R_FINISH); - } - - dgoto(R->sp, DNS_R_SERVFAIL); - case DNS_R_FILE: - if (R->sp > 0) { - if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) - goto error; - - if (dns_p_count(F->answer, DNS_S_AN) > 0) - dgoto(R->sp, DNS_R_FINISH); - - dns_p_setptr(&F->answer, NULL); - } else { - R->search = 0; - - while ((len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) { - if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) - goto error; - - if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) - goto error; - - if (dns_p_count(F->answer, DNS_S_AN) > 0) - dgoto(R->sp, DNS_R_FINISH); - - dns_p_setptr(&F->answer, NULL); - } - } - - dgoto(R->sp, DNS_R_SWITCH); - case DNS_R_CACHE: - error = 0; - - if (!F->query && (error = dns_q_make(&F->query, R->qname, R->qtype, R->qclass, F->qflags))) - goto error; - - if (dns_p_setptr(&F->answer, R->cache->query(F->query, R->cache, &error))) { - if (dns_p_count(F->answer, DNS_S_AN) > 0) - dgoto(R->sp, DNS_R_FINISH); - - dns_p_setptr(&F->answer, NULL); - - dgoto(R->sp, DNS_R_SWITCH); - } else if (error) - goto error; - - F->state++; - /* FALL THROUGH */ - case DNS_R_SUBMIT: - if ((error = R->cache->submit(F->query, R->cache))) - goto error; - - F->state++; - /* FALL THROUGH */ - case DNS_R_CHECK: - if ((error = R->cache->check(R->cache))) - goto error; - - F->state++; - /* FALL THROUGH */ - case DNS_R_FETCH: - error = 0; - - if (dns_p_setptr(&F->answer, R->cache->fetch(R->cache, &error))) { - if (dns_p_count(F->answer, DNS_S_AN) > 0) - dgoto(R->sp, DNS_R_FINISH); - - dns_p_setptr(&F->answer, NULL); - - dgoto(R->sp, DNS_R_SWITCH); - } else if (error) - goto error; - - dgoto(R->sp, DNS_R_SWITCH); - case DNS_R_BIND: - if (R->sp > 0) { - if (!F->query) - goto noquery; - - dgoto(R->sp, DNS_R_HINTS); - } - - R->search = 0; - - F->state++; - /* FALL THROUGH */ - case DNS_R_SEARCH: - /* - * XXX: We probably should only apply the domain search - * algorithm if R->sp == 0. - */ - if (!(len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) - dgoto(R->sp, DNS_R_SWITCH); - - if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) - goto error; - - F->state++; - /* FALL THROUGH */ - case DNS_R_HINTS: - if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error))) - goto error; - - F->state++; - /* FALL THROUGH */ - case DNS_R_ITERATE: - dns_rr_i_init(&F->hints_i, F->hints); - - F->hints_i.section = DNS_S_AUTHORITY; - F->hints_i.type = DNS_T_NS; - F->hints_i.sort = &dns_res_nameserv_cmp; - F->hints_i.args[0] = F->hints->end; - - F->state++; - /* FALL THROUGH */ - case DNS_R_FOREACH_NS: - dns_rr_i_save(&F->hints_i); - - /* Load our next nameserver host. */ - if (!dns_rr_grep(&F->hints_ns, 1, &F->hints_i, F->hints, &error)) { - if (++F->attempts < R->resconf->options.attempts) - dgoto(R->sp, DNS_R_ITERATE); - - dgoto(R->sp, DNS_R_SWITCH); - } - - dns_rr_i_init(&F->hints_j, F->hints); - - /* Assume there are glue records */ - dgoto(R->sp, DNS_R_FOREACH_A); - case DNS_R_RESOLV0_NS: - /* Have we reached our max depth? */ - if (&F[1] >= endof(R->stack)) - dgoto(R->sp, DNS_R_FOREACH_NS); - - if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) - goto error; - if ((error = dns_res_frame_prepare(R, &F[1], u.ns.host, DNS_T_A, DNS_C_IN))) - goto error; - - F->state++; - - dgoto(++R->sp, DNS_R_INIT); - case DNS_R_RESOLV1_NS: - if (!(len = dns_d_expand(u.host, sizeof u.host, 12, F[1].query, &error))) - goto error; - else if (len >= sizeof u.host) - goto toolong; - - dns_rr_foreach(&rr, F[1].answer, .name = u.host, .type = DNS_T_A, .section = (DNS_S_ALL & ~DNS_S_QD)) { - rr.section = DNS_S_AR; - - if ((error = dns_rr_copy(F->hints, &rr, F[1].answer))) - goto error; - - dns_rr_i_rewind(&F->hints_i); /* Now there's glue. */ - } - - dgoto(R->sp, DNS_R_FOREACH_NS); - case DNS_R_FOREACH_A: { - struct dns_a a; - struct sockaddr_in sin; - - /* - * NOTE: Iterator initialized in DNS_R_FOREACH_NS because - * this state is re-entrant, but we need to reset - * .name to a valid pointer each time. - */ - if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) - goto error; - - F->hints_j.name = u.ns.host; - F->hints_j.type = DNS_T_A; - F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; - - if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { - if (!dns_rr_i_count(&F->hints_j)) - dgoto(R->sp, DNS_R_RESOLV0_NS); - - dgoto(R->sp, DNS_R_FOREACH_NS); - } - - if ((error = dns_a_parse(&a, &rr, F->hints))) - goto error; - - sin.sin_family = AF_INET; - sin.sin_addr = a.addr; - if (R->sp == 0) - sin.sin_port = dns_hints_port(R->hints, AF_INET, &sin.sin_addr); - else - sin.sin_port = htons(53); - - if (DNS_DEBUG) { - char addr[INET_ADDRSTRLEN + 1]; - dns_a_print(addr, sizeof addr, &a); - DNS_HEADER(F->query)->qid = dns_so_mkqid(&R->so); - DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp); - } - - if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin))) - goto error; - - F->state++; - } - /* FALL THROUGH */ - case DNS_R_QUERY_A: - if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) - dgoto(R->sp, DNS_R_FOREACH_A); - - if ((error = dns_so_check(&R->so))) - goto error; - - if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) - goto error; - - if (DNS_DEBUG) { - DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp); - } - - if (dns_p_rcode(F->answer) == DNS_RC_FORMERR || - dns_p_rcode(F->answer) == DNS_RC_NOTIMP || - dns_p_rcode(F->answer) == DNS_RC_BADVERS) { - /* Temporarily disable EDNS0 and try again. */ - if (F->qflags & DNS_Q_EDNS0) { - F->qflags &= ~DNS_Q_EDNS0; - if ((error = dns_q_remake(&F->query, F->qflags))) - goto error; - - dgoto(R->sp, DNS_R_FOREACH_A); - } - } - - if ((error = dns_rr_parse(&rr, 12, F->query))) - goto error; - - if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error))) - goto error; - else if (len >= sizeof u.name) - goto toolong; - - dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) { - dgoto(R->sp, DNS_R_FINISH); /* Found */ - } - - dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) { - F->ans_cname = rr; - - dgoto(R->sp, DNS_R_CNAME0_A); - } - - /* - * XXX: The condition here should probably check whether - * R->sp == 0, because DNS_R_SEARCH runs regardless of - * options.recurse. See DNS_R_BIND. - */ - if (!R->resconf->options.recurse) { - /* Make first answer our tentative answer */ - if (!R->nodata) - dns_p_movptr(&R->nodata, &F->answer); - - dgoto(R->sp, DNS_R_SEARCH); - } - - dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) { - dns_p_movptr(&F->hints, &F->answer); - - dgoto(R->sp, DNS_R_ITERATE); - } - - /* XXX: Should this go further up? */ - if (DNS_HEADER(F->answer)->aa) - dgoto(R->sp, DNS_R_FINISH); - - /* XXX: Should we copy F->answer to R->nodata? */ - - dgoto(R->sp, DNS_R_FOREACH_A); - case DNS_R_CNAME0_A: - if (&F[1] >= endof(R->stack)) - dgoto(R->sp, DNS_R_FINISH); - - if ((error = dns_cname_parse(&u.cname, &F->ans_cname, F->answer))) - goto error; - if ((error = dns_res_frame_prepare(R, &F[1], u.cname.host, dns_rr_type(12, F->query), DNS_C_IN))) - goto error; - - F->state++; - - dgoto(++R->sp, DNS_R_INIT); - case DNS_R_CNAME1_A: - if (!(P = dns_res_merge(F->answer, F[1].answer, &error))) - goto error; - - dns_p_setptr(&F->answer, P); - - dgoto(R->sp, DNS_R_FINISH); - case DNS_R_FINISH: - if (!F->answer) - goto noanswer; - - if (!R->resconf->options.smart || R->sp > 0) - dgoto(R->sp, DNS_R_DONE); - - R->smart.section = DNS_S_AN; - R->smart.type = R->qtype; - - dns_rr_i_init(&R->smart, F->answer); - - F->state++; - /* FALL THROUGH */ - case DNS_R_SMART0_A: - if (&F[1] >= endof(R->stack)) - dgoto(R->sp, DNS_R_DONE); - - while (dns_rr_grep(&rr, 1, &R->smart, F->answer, &error)) { - union { - struct dns_ns ns; - struct dns_mx mx; - struct dns_srv srv; - } rd; - char *qname; - enum dns_type qtype; - enum dns_class qclass; - - switch (rr.type) { - case DNS_T_NS: - if ((error = dns_ns_parse(&rd.ns, &rr, F->answer))) - goto error; - - qname = rd.ns.host; - qtype = DNS_T_A; - qclass = DNS_C_IN; - - break; - case DNS_T_MX: - if ((error = dns_mx_parse(&rd.mx, &rr, F->answer))) - goto error; - - qname = rd.mx.host; - qtype = DNS_T_A; - qclass = DNS_C_IN; - - break; - case DNS_T_SRV: - if ((error = dns_srv_parse(&rd.srv, &rr, F->answer))) - goto error; - - qname = rd.srv.target; - qtype = DNS_T_A; - qclass = DNS_C_IN; - - break; - default: - continue; - } /* switch() */ - - if ((error = dns_res_frame_prepare(R, &F[1], qname, qtype, qclass))) - goto error; - - F->state++; - - dgoto(++R->sp, DNS_R_INIT); - } /* while() */ - - /* - * NOTE: SMTP specification says to fallback to A record. - * - * XXX: Should we add a mock MX answer? - */ - if (R->qtype == DNS_T_MX && R->smart.state.count == 0) { - if ((error = dns_res_frame_prepare(R, &F[1], R->qname, DNS_T_A, DNS_C_IN))) - goto error; - - R->smart.state.count++; - F->state++; - - dgoto(++R->sp, DNS_R_INIT); - } - - dgoto(R->sp, DNS_R_DONE); - case DNS_R_SMART1_A: - if (!F[1].answer) - goto noanswer; - - /* - * FIXME: For CNAME chains (which are typically illegal in - * this context), we should rewrite the record host name - * to the original smart qname. All the user cares about - * is locating that A/AAAA record. - */ - dns_rr_foreach(&rr, F[1].answer, .section = DNS_S_AN, .type = DNS_T_A) { - rr.section = DNS_S_AR; - - if (dns_rr_exists(&rr, F[1].answer, F->answer)) - continue; - - while ((error = dns_rr_copy(F->answer, &rr, F[1].answer))) { - if (error != DNS_ENOBUFS) - goto error; - if ((error = dns_p_grow(&F->answer))) - goto error; - } - } - - dgoto(R->sp, DNS_R_SMART0_A); - case DNS_R_DONE: - if (!F->answer) - goto noanswer; - - if (R->sp > 0) - dgoto(--R->sp, F[-1].state); - - break; - case DNS_R_SERVFAIL: - if (!dns_p_setptr(&F->answer, dns_p_make(DNS_P_QBUFSIZ, &error))) - goto error; - - DNS_HEADER(F->answer)->qr = 1; - DNS_HEADER(F->answer)->rcode = DNS_RC_SERVFAIL; - - if ((error = dns_p_push(F->answer, DNS_S_QD, R->qname, strlen(R->qname), R->qtype, R->qclass, 0, 0))) - goto error; - - dgoto(R->sp, DNS_R_DONE); - default: - error = EINVAL; - - goto error; - } /* switch () */ - - return 0; -noquery: - error = DNS_ENOQUERY; - - goto error; -noanswer: - error = DNS_ENOANSWER; - - goto error; -toolong: - error = DNS_EILLEGAL; - - /* FALL THROUGH */ -error: - return error; -} /* dns_res_exec() */ - -#undef goto - - -void dns_res_clear(struct dns_resolver *R) { - switch (R->stack[R->sp].state) { - case DNS_R_CHECK: - R->cache->clear(R->cache); - break; - default: - dns_so_clear(&R->so); - break; - } -} /* dns_res_clear() */ - - -static int dns_res_events2(struct dns_resolver *R, enum dns_events type) { - int events; - - switch (R->stack[R->sp].state) { - case DNS_R_CHECK: - events = R->cache->events(R->cache); - - return (type == DNS_LIBEVENT)? DNS_POLL2EV(events) : events; - default: - return dns_so_events2(&R->so, type); - } -} /* dns_res_events2() */ - - -int dns_res_events(struct dns_resolver *R) { - return dns_res_events2(R, R->so.opts.events); -} /* dns_res_events() */ - - -int dns_res_pollfd(struct dns_resolver *R) { - switch (R->stack[R->sp].state) { - case DNS_R_CHECK: - return R->cache->pollfd(R->cache); - default: - return dns_so_pollfd(&R->so); - } -} /* dns_res_pollfd() */ - - -time_t dns_res_timeout(struct dns_resolver *R) { - time_t elapsed; - - switch (R->stack[R->sp].state) { -#if 0 - case DNS_R_QUERY_AAAA: -#endif - case DNS_R_QUERY_A: - elapsed = dns_so_elapsed(&R->so); - - if (elapsed <= dns_resconf_timeout(R->resconf)) - return R->resconf->options.timeout - elapsed; - - break; - default: - break; - } /* switch() */ - - /* - * NOTE: We're not in a pollable state, or the user code hasn't - * called dns_res_check properly. The calling code is probably - * broken. Put them into a slow-burn pattern. - */ - return 1; -} /* dns_res_timeout() */ - - -time_t dns_res_elapsed(struct dns_resolver *R) { - return dns_elapsed(&R->elapsed); -} /* dns_res_elapsed() */ - - -int dns_res_poll(struct dns_resolver *R, int timeout) { - return dns_poll(dns_res_pollfd(R), dns_res_events2(R, DNS_SYSPOLL), timeout); -} /* dns_res_poll() */ - - -int dns_res_submit2(struct dns_resolver *R, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass) { - dns_res_reset(R); - - /* Don't anchor; that can conflict with searchlist generation. */ - dns_d_init(R->qname, sizeof R->qname, qname, (R->qlen = qlen), 0); - - R->qtype = qtype; - R->qclass = qclass; - - dns_begin(&R->elapsed); - - return 0; -} /* dns_res_submit2() */ - - -int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass) { - return dns_res_submit2(R, qname, strlen(qname), qtype, qclass); -} /* dns_res_submit() */ - - -int dns_res_check(struct dns_resolver *R) { - int error; - - if (R->stack[0].state != DNS_R_DONE) { - if ((error = dns_res_exec(R))) - return error; - } - - return 0; -} /* dns_res_check() */ - - -struct dns_packet *dns_res_fetch(struct dns_resolver *R, int *error) { - struct dns_packet *P = NULL; - - if (R->stack[0].state != DNS_R_DONE) - return *error = DNS_EUNKNOWN, (void *)0; - - if (!dns_p_movptr(&P, &R->stack[0].answer)) - return *error = DNS_EFETCHED, (void *)0; - - return P; -} /* dns_res_fetch() */ - - -static struct dns_packet *dns_res_fetch_and_study(struct dns_resolver *R, int *_error) { - struct dns_packet *P = NULL; - int error; - - if (!(P = dns_res_fetch(R, &error))) - goto error; - if ((error = dns_p_study(P))) - goto error; - - return P; -error: - *_error = error; - - dns_p_free(P); - - return NULL; -} /* dns_res_fetch_and_study() */ - - -struct dns_packet *dns_res_query(struct dns_resolver *res, const char *qname, enum dns_type qtype, enum dns_class qclass, int timeout, int *error_) { - int error; - - if ((error = dns_res_submit(res, qname, qtype, qclass))) - goto error; - - while ((error = dns_res_check(res))) { - if (dns_res_elapsed(res) > timeout) - error = DNS_ETIMEDOUT; - - if (error != DNS_EAGAIN) - goto error; - - if ((error = dns_res_poll(res, 1))) - goto error; - } - - return dns_res_fetch(res, error_); -error: - *error_ = error; - - return 0; -} /* dns_res_query() */ - - -const struct dns_stat *dns_res_stat(struct dns_resolver *res) { - return dns_so_stat(&res->so); -} /* dns_res_stat() */ - - -void dns_res_sethints(struct dns_resolver *res, struct dns_hints *hints) { - dns_hints_acquire(hints); /* acquire first in case same hints object */ - dns_hints_close(res->hints); - res->hints = hints; -} /* dns_res_sethints() */ - - -/* - * A D D R I N F O R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_addrinfo { - struct addrinfo hints; - struct dns_resolver *res; - - char qname[DNS_D_MAXNAME + 1]; - enum dns_type qtype; - unsigned short qport, port; - - struct { - unsigned long todo; - int state; - int atype; - enum dns_type qtype; - } af; - - struct dns_packet *answer; - struct dns_packet *glue; - - struct dns_rr_i i, g; - struct dns_rr rr; - - char cname[DNS_D_MAXNAME + 1]; - char i_cname[DNS_D_MAXNAME + 1], g_cname[DNS_D_MAXNAME + 1]; - - int g_depth; - - int state; - int found; - - struct dns_stat st; -}; /* struct dns_addrinfo */ - - -#define DNS_AI_AFMAX 32 -#define DNS_AI_AF2INDEX(af) (1UL << ((af) - 1)) - -static inline unsigned long dns_ai_af2index(int af) { - dns_static_assert(dns_same_type(unsigned long, DNS_AI_AF2INDEX(1), 1), "internal type mismatch"); - dns_static_assert(dns_same_type(unsigned long, ((struct dns_addrinfo *)0)->af.todo, 1), "internal type mismatch"); - - return (af > 0 && af <= DNS_AI_AFMAX)? DNS_AI_AF2INDEX(af) : 0; -} - -static int dns_ai_setaf(struct dns_addrinfo *ai, int af, int qtype) { - ai->af.atype = af; - ai->af.qtype = qtype; - - ai->af.todo &= ~dns_ai_af2index(af); - - return af; -} /* dns_ai_setaf() */ - -#define DNS_SM_RESTORE \ - do { pc = 0xff & (ai->af.state >> 0); i = 0xff & (ai->af.state >> 8); } while (0) -#define DNS_SM_SAVE \ - do { ai->af.state = ((0xff & pc) << 0) | ((0xff & i) << 8); } while (0) - -static int dns_ai_nextaf(struct dns_addrinfo *ai) { - int i, pc; - - dns_static_assert(AF_UNSPEC == 0, "AF_UNSPEC constant not 0"); - dns_static_assert(AF_INET <= DNS_AI_AFMAX, "AF_INET constant too large"); - dns_static_assert(AF_INET6 <= DNS_AI_AFMAX, "AF_INET6 constant too large"); - - DNS_SM_ENTER; - - if (ai->res) { - /* - * NB: On OpenBSD, at least, the types of entries resolved - * is the intersection of the /etc/resolv.conf families and - * the families permitted by the .ai_type hint. So if - * /etc/resolv.conf has "family inet4" and .ai_type - * is AF_INET6, then the address ::1 will return 0 entries - * even if AI_NUMERICHOST is specified in .ai_flags. - */ - while (i < (int)lengthof(ai->res->resconf->family)) { - int af = ai->res->resconf->family[i++]; - - if (af == AF_UNSPEC) { - DNS_SM_EXIT; - } else if (af < 0 || af > DNS_AI_AFMAX) { - continue; - } else if (!(DNS_AI_AF2INDEX(af) & ai->af.todo)) { - continue; - } else if (af == AF_INET) { - DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); - } else if (af == AF_INET6) { - DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); - } - } - } else { - /* - * NB: If we get here than AI_NUMERICFLAGS should be set and - * order shouldn't matter. - */ - if (DNS_AI_AF2INDEX(AF_INET) & ai->af.todo) - DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); - if (DNS_AI_AF2INDEX(AF_INET6) & ai->af.todo) - DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); - } - - DNS_SM_LEAVE; - - return dns_ai_setaf(ai, AF_UNSPEC, 0); -} /* dns_ai_nextaf() */ - -#undef DNS_SM_RESTORE -#undef DNS_SM_SAVE - -static enum dns_type dns_ai_qtype(struct dns_addrinfo *ai) { - return (ai->qtype)? ai->qtype : ai->af.qtype; -} /* dns_ai_qtype() */ - - -static dns_error_t dns_ai_parseport(unsigned short *port, const char *serv, const struct addrinfo *hints) { - const char *cp = serv; - unsigned long n = 0; - - while (*cp >= '0' && *cp <= '9' && n < 65536) { - n *= 10; - n += *cp++ - '0'; - } - - if (*cp == '\0') { - if (cp == serv || n >= 65536) - return DNS_ESERVICE; - - *port = n; - - return 0; - } - - if (hints->ai_flags & AI_NUMERICSERV) - return DNS_ESERVICE; - - /* TODO: try getaddrinfo(NULL, serv, { .ai_flags = AI_NUMERICSERV }) */ - - return DNS_ESERVICE; -} /* dns_ai_parseport() */ - - -struct dns_addrinfo *dns_ai_open(const char *host, const char *serv, enum dns_type qtype, const struct addrinfo *hints, struct dns_resolver *res, int *_error) { - static const struct dns_addrinfo ai_initializer; - struct dns_addrinfo *ai; - int error; - - if (res) { - dns_res_acquire(res); - } else if (!(hints->ai_flags & AI_NUMERICHOST)) { - /* - * NOTE: it's assumed that *_error is set from a previous - * API function call, such as dns_res_stub(). Should change - * this semantic, but it's applied elsewhere, too. - */ - if (!*_error) - *_error = EINVAL; - return NULL; - } - - if (!(ai = malloc(sizeof *ai))) - goto syerr; - - *ai = ai_initializer; - ai->hints = *hints; - - ai->res = res; - res = NULL; - - if (sizeof ai->qname <= dns_strlcpy(ai->qname, host, sizeof ai->qname)) - { error = ENAMETOOLONG; goto error; } - - ai->qtype = qtype; - ai->qport = 0; - - if (serv && (error = dns_ai_parseport(&ai->qport, serv, hints))) - goto error; - ai->port = ai->qport; - - /* - * FIXME: If an explicit A or AAAA record type conflicts with - * .ai_family or with resconf.family (i.e. AAAA specified but - * AF_INET6 not in interection of .ai_family and resconf.family), - * then what? - */ - switch (ai->qtype) { - case DNS_T_A: - ai->af.todo = DNS_AI_AF2INDEX(AF_INET); - break; - case DNS_T_AAAA: - ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); - break; - default: /* 0, MX, SRV, etc */ - switch (ai->hints.ai_family) { - case AF_UNSPEC: - ai->af.todo = DNS_AI_AF2INDEX(AF_INET) | DNS_AI_AF2INDEX(AF_INET6); - break; - case AF_INET: - ai->af.todo = DNS_AI_AF2INDEX(AF_INET); - break; - case AF_INET6: - ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); - break; - default: - break; - } - } - - return ai; -syerr: - error = dns_syerr(); -error: - *_error = error; - - dns_ai_close(ai); - dns_res_close(res); - - return NULL; -} /* dns_ai_open() */ - - -void dns_ai_close(struct dns_addrinfo *ai) { - if (!ai) - return; - - dns_res_close(ai->res); - - if (ai->answer != ai->glue) - dns_p_free(ai->glue); - - dns_p_free(ai->answer); - free(ai); -} /* dns_ai_close() */ - - -static int dns_ai_setent(struct addrinfo **ent, union dns_any *any, enum dns_type type, struct dns_addrinfo *ai) { - union { struct sockaddr saddr; struct sockaddr_in sin; struct sockaddr_in6 sin6; } saddr; - memset(&saddr, 0, sizeof(saddr)); - const char *cname; - size_t clen; - - switch (type) { - case DNS_T_A: - saddr.sin.sin_family = AF_INET; - saddr.sin.sin_port = htons(ai->port); - - memcpy(&saddr.sin.sin_addr, any, sizeof saddr.sin.sin_addr); - - break; - case DNS_T_AAAA: - saddr.sin6.sin6_family = AF_INET6; - saddr.sin6.sin6_port = htons(ai->port); - - memcpy(&saddr.sin6.sin6_addr, any, sizeof saddr.sin6.sin6_addr); - - break; - default: - return EINVAL; - } /* switch() */ - - if (ai->hints.ai_flags & AI_CANONNAME) { - cname = (*ai->cname)? ai->cname : ai->qname; - clen = strlen(cname); - } else { - cname = NULL; - clen = 0; - } - - if (!(*ent = malloc(sizeof **ent + dns_sa_len(&saddr) + ((ai->hints.ai_flags & AI_CANONNAME)? clen + 1 : 0)))) - return dns_syerr(); - - memset(*ent, '\0', sizeof **ent); - - (*ent)->ai_family = saddr.saddr.sa_family; - (*ent)->ai_socktype = ai->hints.ai_socktype; - (*ent)->ai_protocol = ai->hints.ai_protocol; - - (*ent)->ai_addr = memcpy((unsigned char *)*ent + sizeof **ent, &saddr, dns_sa_len(&saddr)); - (*ent)->ai_addrlen = dns_sa_len(&saddr); - - if (ai->hints.ai_flags & AI_CANONNAME) - (*ent)->ai_canonname = memcpy((unsigned char *)*ent + sizeof **ent + dns_sa_len(&saddr), cname, clen + 1); - - ai->found++; - - return 0; -} /* dns_ai_setent() */ - - -enum dns_ai_state { - DNS_AI_S_INIT, - DNS_AI_S_NEXTAF, - DNS_AI_S_NUMERIC, - DNS_AI_S_SUBMIT, - DNS_AI_S_CHECK, - DNS_AI_S_FETCH, - DNS_AI_S_FOREACH_I, - DNS_AI_S_INIT_G, - DNS_AI_S_ITERATE_G, - DNS_AI_S_FOREACH_G, - DNS_AI_S_SUBMIT_G, - DNS_AI_S_CHECK_G, - DNS_AI_S_FETCH_G, - DNS_AI_S_DONE, -}; /* enum dns_ai_state */ - -#define dns_ai_goto(which) do { ai->state = (which); goto exec; } while (0) - -int dns_ai_nextent(struct addrinfo **ent, struct dns_addrinfo *ai) { - struct dns_packet *ans, *glue; - struct dns_rr rr; - char qname[DNS_D_MAXNAME + 1]; - union dns_any any; - size_t qlen, clen; - int error; - - *ent = 0; - -exec: - - switch (ai->state) { - case DNS_AI_S_INIT: - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_NEXTAF: - if (!dns_ai_nextaf(ai)) - dns_ai_goto(DNS_AI_S_DONE); - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_NUMERIC: - if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) { - if (ai->af.atype == AF_INET) { - ai->state = DNS_AI_S_NEXTAF; - return dns_ai_setent(ent, &any, DNS_T_A, ai); - } else { - dns_ai_goto(DNS_AI_S_NEXTAF); - } - } - - if (1 == dns_inet_pton(AF_INET6, ai->qname, &any.aaaa)) { - if (ai->af.atype == AF_INET6) { - ai->state = DNS_AI_S_NEXTAF; - return dns_ai_setent(ent, &any, DNS_T_AAAA, ai); - } else { - dns_ai_goto(DNS_AI_S_NEXTAF); - } - } - - if (ai->hints.ai_flags & AI_NUMERICHOST) - dns_ai_goto(DNS_AI_S_NEXTAF); - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_SUBMIT: - assert(ai->res); - - if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN))) - return error; - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_CHECK: - if ((error = dns_res_check(ai->res))) - return error; - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_FETCH: - if (!(ans = dns_res_fetch_and_study(ai->res, &error))) - return error; - if (ai->glue != ai->answer) - dns_p_free(ai->glue); - ai->glue = dns_p_movptr(&ai->answer, &ans); - - /* Search generator may have changed the qname. */ - if (!(qlen = dns_d_expand(qname, sizeof qname, 12, ai->answer, &error))) - return error; - else if (qlen >= sizeof qname) - return DNS_EILLEGAL; - if (!dns_d_cname(ai->cname, sizeof ai->cname, qname, qlen, ai->answer, &error)) - return error; - - dns_strlcpy(ai->i_cname, ai->cname, sizeof ai->i_cname); - dns_rr_i_init(&ai->i, ai->answer); - ai->i.section = DNS_S_AN; - ai->i.name = ai->i_cname; - ai->i.type = dns_ai_qtype(ai); - ai->i.sort = &dns_rr_i_order; - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_FOREACH_I: - if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error)) - dns_ai_goto(DNS_AI_S_NEXTAF); - - if ((error = dns_any_parse(&any, &rr, ai->answer))) - return error; - - ai->port = ai->qport; - - switch (rr.type) { - case DNS_T_A: - case DNS_T_AAAA: - return dns_ai_setent(ent, &any, rr.type, ai); - default: - if (!(clen = dns_any_cname(ai->cname, sizeof ai->cname, &any, rr.type))) - dns_ai_goto(DNS_AI_S_FOREACH_I); - - /* - * Find the "real" canonical name. Some authorities - * publish aliases where an RFC defines a canonical - * name. We trust that the resolver followed any - * CNAME chains on it's own, regardless of whether - * the "smart" option is enabled. - */ - if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, clen, ai->answer, &error)) - return error; - - if (rr.type == DNS_T_SRV) - ai->port = any.srv.port; - - break; - } /* switch() */ - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_INIT_G: - ai->g_depth = 0; - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_ITERATE_G: - dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname); - dns_rr_i_init(&ai->g, ai->glue); - ai->g.section = DNS_S_ALL & ~DNS_S_QD; - ai->g.name = ai->g_cname; - ai->g.type = ai->af.qtype; - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_FOREACH_G: - if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) { - if (dns_rr_i_count(&ai->g) > 0) - dns_ai_goto(DNS_AI_S_FOREACH_I); - else - dns_ai_goto(DNS_AI_S_SUBMIT_G); - } - - if ((error = dns_any_parse(&any, &rr, ai->glue))) - return error; - - return dns_ai_setent(ent, &any, rr.type, ai); - case DNS_AI_S_SUBMIT_G: - /* skip if already queried */ - if (dns_rr_grep(&rr, 1, dns_rr_i_new(ai->glue, .section = DNS_S_QD, .name = ai->g.name, .type = ai->g.type), ai->glue, &error)) - dns_ai_goto(DNS_AI_S_FOREACH_I); - /* skip if we recursed (CNAME chains should have been handled in the resolver) */ - if (++ai->g_depth > 1) - dns_ai_goto(DNS_AI_S_FOREACH_I); - - if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN))) - return error; - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_CHECK_G: - if ((error = dns_res_check(ai->res))) - return error; - - ai->state++; - /* FALL THROUGH */ - case DNS_AI_S_FETCH_G: - if (!(ans = dns_res_fetch_and_study(ai->res, &error))) - return error; - - glue = dns_p_merge(ai->glue, DNS_S_ALL, ans, DNS_S_ALL, &error); - dns_p_setptr(&ans, NULL); - if (!glue) - return error; - - if (ai->glue != ai->answer) - dns_p_free(ai->glue); - ai->glue = glue; - - if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->g.name, strlen(ai->g.name), ai->glue, &error)) - dns_ai_goto(DNS_AI_S_FOREACH_I); - - dns_ai_goto(DNS_AI_S_ITERATE_G); - case DNS_AI_S_DONE: - if (ai->found) { - return ENOENT; /* TODO: Just return 0 */ - } else if (ai->answer) { - switch (dns_p_rcode(ai->answer)) { - case DNS_RC_NOERROR: - /* FALL THROUGH */ - case DNS_RC_NXDOMAIN: - return DNS_ENONAME; - default: - return DNS_EFAIL; - } - } else { - return DNS_EFAIL; - } - default: - return EINVAL; - } /* switch() */ -} /* dns_ai_nextent() */ - - -time_t dns_ai_elapsed(struct dns_addrinfo *ai) { - return (ai->res)? dns_res_elapsed(ai->res) : 0; -} /* dns_ai_elapsed() */ - - -void dns_ai_clear(struct dns_addrinfo *ai) { - if (ai->res) - dns_res_clear(ai->res); -} /* dns_ai_clear() */ - - -int dns_ai_events(struct dns_addrinfo *ai) { - return (ai->res)? dns_res_events(ai->res) : 0; -} /* dns_ai_events() */ - - -int dns_ai_pollfd(struct dns_addrinfo *ai) { - return (ai->res)? dns_res_pollfd(ai->res) : -1; -} /* dns_ai_pollfd() */ - - -time_t dns_ai_timeout(struct dns_addrinfo *ai) { - return (ai->res)? dns_res_timeout(ai->res) : 0; -} /* dns_ai_timeout() */ - - -int dns_ai_poll(struct dns_addrinfo *ai, int timeout) { - return (ai->res)? dns_res_poll(ai->res, timeout) : 0; -} /* dns_ai_poll() */ - - -size_t dns_ai_print(void *_dst, size_t lim, struct addrinfo *ent, struct dns_addrinfo *ai) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - char addr[DNS_PP_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; - - dns_b_puts(&dst, "[ "); - dns_b_puts(&dst, ai->qname); - dns_b_puts(&dst, " IN "); - if (ai->qtype) { - dns_b_puts(&dst, dns_strtype(ai->qtype)); - } else if (ent->ai_family == AF_INET) { - dns_b_puts(&dst, dns_strtype(DNS_T_A)); - } else if (ent->ai_family == AF_INET6) { - dns_b_puts(&dst, dns_strtype(DNS_T_AAAA)); - } else { - dns_b_puts(&dst, "0"); - } - dns_b_puts(&dst, " ]\n"); - - dns_b_puts(&dst, ".ai_family = "); - switch (ent->ai_family) { - case AF_INET: - dns_b_puts(&dst, "AF_INET"); - break; - case AF_INET6: - dns_b_puts(&dst, "AF_INET6"); - break; - default: - dns_b_fmtju(&dst, ent->ai_family, 0); - break; - } - dns_b_putc(&dst, '\n'); - - dns_b_puts(&dst, ".ai_socktype = "); - switch (ent->ai_socktype) { - case SOCK_STREAM: - dns_b_puts(&dst, "SOCK_STREAM"); - break; - case SOCK_DGRAM: - dns_b_puts(&dst, "SOCK_DGRAM"); - break; - default: - dns_b_fmtju(&dst, ent->ai_socktype, 0); - break; - } - dns_b_putc(&dst, '\n'); - - dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr, NULL), addr, sizeof addr); - dns_b_puts(&dst, ".ai_addr = ["); - dns_b_puts(&dst, addr); - dns_b_puts(&dst, "]:"); - dns_b_fmtju(&dst, ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)), 0); - dns_b_putc(&dst, '\n'); - - dns_b_puts(&dst, ".ai_canonname = "); - dns_b_puts(&dst, (ent->ai_canonname)? ent->ai_canonname : "[NULL]"); - dns_b_putc(&dst, '\n'); - - return dns_b_strllen(&dst); -} /* dns_ai_print() */ - - -const struct dns_stat *dns_ai_stat(struct dns_addrinfo *ai) { - return (ai->res)? dns_res_stat(ai->res) : &ai->st; -} /* dns_ai_stat() */ - - -/* - * M I S C E L L A N E O U S R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -static const struct { - char name[16]; - enum dns_section type; -} dns_sections[] = { - { "QUESTION", DNS_S_QUESTION }, - { "QD", DNS_S_QUESTION }, - { "ANSWER", DNS_S_ANSWER }, - { "AN", DNS_S_ANSWER }, - { "AUTHORITY", DNS_S_AUTHORITY }, - { "NS", DNS_S_AUTHORITY }, - { "ADDITIONAL", DNS_S_ADDITIONAL }, - { "AR", DNS_S_ADDITIONAL }, -}; - -const char *(dns_strsection)(enum dns_section section, void *_dst, size_t lim) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - unsigned i; - - for (i = 0; i < lengthof(dns_sections); i++) { - if (dns_sections[i].type & section) { - dns_b_puts(&dst, dns_sections[i].name); - section &= ~dns_sections[i].type; - if (section) - dns_b_putc(&dst, '|'); - } - } - - if (section || dst.p == dst.base) - dns_b_fmtju(&dst, (0xffff & section), 0); - - return dns_b_tostring(&dst); -} /* dns_strsection() */ - - -enum dns_section dns_isection(const char *src) { - enum dns_section section = 0; - char sbuf[128]; - char *name, *next; - unsigned i; - - dns_strlcpy(sbuf, src, sizeof sbuf); - next = sbuf; - - while ((name = dns_strsep(&next, "|+, \t"))) { - for (i = 0; i < lengthof(dns_sections); i++) { - if (!strcasecmp(dns_sections[i].name, name)) { - section |= dns_sections[i].type; - break; - } - } - } - - return section; -} /* dns_isection() */ - - -static const struct { - char name[8]; - enum dns_class type; -} dns_classes[] = { - { "IN", DNS_C_IN }, -}; - -const char *(dns_strclass)(enum dns_class type, void *_dst, size_t lim) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - unsigned i; - - for (i = 0; i < lengthof(dns_classes); i++) { - if (dns_classes[i].type == type) { - dns_b_puts(&dst, dns_classes[i].name); - break; - } - } - - if (dst.p == dst.base) - dns_b_fmtju(&dst, (0xffff & type), 0); - - return dns_b_tostring(&dst); -} /* dns_strclass() */ - - -enum dns_class dns_iclass(const char *name) { - unsigned i, class; - - for (i = 0; i < lengthof(dns_classes); i++) { - if (!strcasecmp(dns_classes[i].name, name)) - return dns_classes[i].type; - } - - class = 0; - while (dns_isdigit(*name)) { - class *= 10; - class += *name++ - '0'; - } - - return DNS_PP_MIN(class, 0xffff); -} /* dns_iclass() */ - - -const char *(dns_strtype)(enum dns_type type, void *_dst, size_t lim) { - struct dns_buf dst = DNS_B_INTO(_dst, lim); - unsigned i; - - for (i = 0; i < lengthof(dns_rrtypes); i++) { - if (dns_rrtypes[i].type == type) { - dns_b_puts(&dst, dns_rrtypes[i].name); - break; - } - } - - if (dst.p == dst.base) - dns_b_fmtju(&dst, (0xffff & type), 0); - - return dns_b_tostring(&dst); -} /* dns_strtype() */ - - -enum dns_type dns_itype(const char *name) { - unsigned i, type; - - for (i = 0; i < lengthof(dns_rrtypes); i++) { - if (!strcasecmp(dns_rrtypes[i].name, name)) - return dns_rrtypes[i].type; - } - - type = 0; - while (dns_isdigit(*name)) { - type *= 10; - type += *name++ - '0'; - } - - return DNS_PP_MIN(type, 0xffff); -} /* dns_itype() */ - - -static char dns_opcodes[16][16] = { - [DNS_OP_QUERY] = "QUERY", - [DNS_OP_IQUERY] = "IQUERY", - [DNS_OP_STATUS] = "STATUS", - [DNS_OP_NOTIFY] = "NOTIFY", - [DNS_OP_UPDATE] = "UPDATE", -}; - -static char *dns__strcode(int code, char *dst, size_t lim) { - char _tmp[48] = ""; - struct dns_buf tmp; - size_t p; - - assert(lim > 0); - dns_b_fmtju(dns_b_into(&tmp, _tmp, DNS_PP_MIN(sizeof _tmp, lim - 1)), code, 0); - - /* copy downwards so first byte is copied last (see below) */ - p = (size_t)(tmp.p - tmp.base); - dst[p] = '\0'; - while (p--) - dst[p] = _tmp[p]; - - return (char *)dst; -} - -const char *dns_stropcode(enum dns_opcode opcode) { - opcode = (unsigned)opcode % lengthof(dns_opcodes); - - if ('\0' == dns_opcodes[opcode][0]) - return dns__strcode(opcode, dns_opcodes[opcode], sizeof dns_opcodes[opcode]); - - return dns_opcodes[opcode]; -} /* dns_stropcode() */ - - -enum dns_opcode dns_iopcode(const char *name) { - unsigned opcode; - - for (opcode = 0; opcode < lengthof(dns_opcodes); opcode++) { - if (!strcasecmp(name, dns_opcodes[opcode])) - return opcode; - } - - opcode = 0; - while (dns_isdigit(*name)) { - opcode *= 10; - opcode += *name++ - '0'; - } - - return DNS_PP_MIN(opcode, 0x0f); -} /* dns_iopcode() */ - - -static char dns_rcodes[32][16] = { - [DNS_RC_NOERROR] = "NOERROR", - [DNS_RC_FORMERR] = "FORMERR", - [DNS_RC_SERVFAIL] = "SERVFAIL", - [DNS_RC_NXDOMAIN] = "NXDOMAIN", - [DNS_RC_NOTIMP] = "NOTIMP", - [DNS_RC_REFUSED] = "REFUSED", - [DNS_RC_YXDOMAIN] = "YXDOMAIN", - [DNS_RC_YXRRSET] = "YXRRSET", - [DNS_RC_NXRRSET] = "NXRRSET", - [DNS_RC_NOTAUTH] = "NOTAUTH", - [DNS_RC_NOTZONE] = "NOTZONE", - /* EDNS(0) extended RCODEs ... */ - [DNS_RC_BADVERS] = "BADVERS", -}; - -const char *dns_strrcode(enum dns_rcode rcode) { - rcode = (unsigned)rcode % lengthof(dns_rcodes); - - if ('\0' == dns_rcodes[rcode][0]) - return dns__strcode(rcode, dns_rcodes[rcode], sizeof dns_rcodes[rcode]); - - return dns_rcodes[rcode]; -} /* dns_strrcode() */ - - -enum dns_rcode dns_ircode(const char *name) { - unsigned rcode; - - for (rcode = 0; rcode < lengthof(dns_rcodes); rcode++) { - if (!strcasecmp(name, dns_rcodes[rcode])) - return rcode; - } - - rcode = 0; - while (dns_isdigit(*name)) { - rcode *= 10; - rcode += *name++ - '0'; - } - - return DNS_PP_MIN(rcode, 0xfff); -} /* dns_ircode() */ - - - -/* - * C O M M A N D - L I N E / R E G R E S S I O N R O U T I N E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#if DNS_MAIN - -#include -#include -#include - -#include - -#if _WIN32 -#include -#endif - -#if !_WIN32 -#include -#endif - - -struct { - struct { - const char *path[8]; - unsigned count; - } resconf, nssconf, hosts, cache; - - const char *qname; - enum dns_type qtype; - - int (*sort)(); - - int verbose; -} MAIN = { - .sort = &dns_rr_i_packet, -}; - - -static void hexdump(const unsigned char *src, size_t len, FILE *fp) { - static const unsigned char hex[] = "0123456789abcdef"; - static const unsigned char tmpl[] = " | |\n"; - unsigned char ln[sizeof tmpl]; - const unsigned char *sp, *se; - unsigned char *h, *g; - unsigned i, n; - - sp = src; - se = sp + len; - - while (sp < se) { - memcpy(ln, tmpl, sizeof ln); - - h = &ln[2]; - g = &ln[53]; - - for (n = 0; n < 2; n++) { - for (i = 0; i < 8 && se - sp > 0; i++, sp++) { - h[0] = hex[0x0f & (*sp >> 4)]; - h[1] = hex[0x0f & (*sp >> 0)]; - h += 3; - - *g++ = (isgraph(*sp))? *sp : '.'; - } - - h++; - } - - fputs((char *)ln, fp); - } - - return /* void */; -} /* hexdump() */ - - -DNS_NORETURN static void panic(const char *fmt, ...) { - va_list ap; - - va_start(ap, fmt); - -#if _WIN32 - vfprintf(stderr, fmt, ap); - - exit(EXIT_FAILURE); -#else - verrx(EXIT_FAILURE, fmt, ap); -#endif -} /* panic() */ - -#define panic_(fn, ln, fmt, ...) \ - panic(fmt "%0s", (fn), (ln), __VA_ARGS__) -#define panic(...) \ - panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "") - - -static void *grow(unsigned char *p, size_t size) { - void *tmp; - - if (!(tmp = realloc(p, size))) - panic("realloc(%"PRIuZ"): %s", size, dns_strerror(errno)); - - return tmp; -} /* grow() */ - - -static size_t add(size_t a, size_t b) { - if (~a < b) - panic("%"PRIuZ" + %"PRIuZ": integer overflow", a, b); - - return a + b; -} /* add() */ - - -static size_t append(unsigned char **dst, size_t osize, const void *src, size_t len) { - size_t size = add(osize, len); - - *dst = grow(*dst, size); - memcpy(*dst + osize, src, len); - - return size; -} /* append() */ - - -static size_t slurp(unsigned char **dst, size_t osize, FILE *fp, const char *path) { - size_t size = osize; - unsigned char buf[1024]; - size_t count; - - while ((count = fread(buf, 1, sizeof buf, fp))) - size = append(dst, size, buf, count); - - if (ferror(fp)) - panic("%s: %s", path, dns_strerror(errno)); - - return size; -} /* slurp() */ - - -static struct dns_resolv_conf *resconf(void) { - static struct dns_resolv_conf *resconf; - const char *path; - unsigned i; - int error; - - if (resconf) - return resconf; - - if (!(resconf = dns_resconf_open(&error))) - panic("dns_resconf_open: %s", dns_strerror(error)); - - if (!MAIN.resconf.count) - MAIN.resconf.path[MAIN.resconf.count++] = "/etc/resolv.conf"; - - for (i = 0; i < MAIN.resconf.count; i++) { - path = MAIN.resconf.path[i]; - - if (0 == strcmp(path, "-")) - error = dns_resconf_loadfile(resconf, stdin); - else - error = dns_resconf_loadpath(resconf, path); - - if (error) - panic("%s: %s", path, dns_strerror(error)); - } - - for (i = 0; i < MAIN.nssconf.count; i++) { - path = MAIN.nssconf.path[i]; - - if (0 == strcmp(path, "-")) - error = dns_nssconf_loadfile(resconf, stdin); - else - error = dns_nssconf_loadpath(resconf, path); - - if (error) - panic("%s: %s", path, dns_strerror(error)); - } - - if (!MAIN.nssconf.count) { - path = "/etc/nsswitch.conf"; - - if (!(error = dns_nssconf_loadpath(resconf, path))) - MAIN.nssconf.path[MAIN.nssconf.count++] = path; - else if (error != ENOENT) - panic("%s: %s", path, dns_strerror(error)); - } - - return resconf; -} /* resconf() */ - - -static struct dns_hosts *hosts(void) { - static struct dns_hosts *hosts; - const char *path; - unsigned i; - int error; - - if (hosts) - return hosts; - - if (!MAIN.hosts.count) { - MAIN.hosts.path[MAIN.hosts.count++] = "/etc/hosts"; - - /* Explicitly test dns_hosts_local() */ - if (!(hosts = dns_hosts_local(&error))) - panic("%s: %s", "/etc/hosts", dns_strerror(error)); - - return hosts; - } - - if (!(hosts = dns_hosts_open(&error))) - panic("dns_hosts_open: %s", dns_strerror(error)); - - for (i = 0; i < MAIN.hosts.count; i++) { - path = MAIN.hosts.path[i]; - - if (0 == strcmp(path, "-")) - error = dns_hosts_loadfile(hosts, stdin); - else - error = dns_hosts_loadpath(hosts, path); - - if (error) - panic("%s: %s", path, dns_strerror(error)); - } - - return hosts; -} /* hosts() */ - - -#if DNS_CACHE -#include "cache.h" - -static struct dns_cache *cache(void) { - static struct cache *cache; - const char *path; - unsigned i; - int error; - - if (cache) - return cache_resi(cache); - if (!MAIN.cache.count) - return NULL; - - if (!(cache = cache_open(&error))) - panic("%s: %s", MAIN.cache.path[0], dns_strerror(error)); - - for (i = 0; i < MAIN.cache.count; i++) { - path = MAIN.cache.path[i]; - - if (!strcmp(path, "-")) { - if ((error = cache_loadfile(cache, stdin, NULL, 0))) - panic("%s: %s", path, dns_strerror(error)); - } else if ((error = cache_loadpath(cache, path, NULL, 0))) - panic("%s: %s", path, dns_strerror(error)); - } - - return cache_resi(cache); -} /* cache() */ -#else -static struct dns_cache *cache(void) { return NULL; } -#endif - - -static void print_packet(struct dns_packet *P, FILE *fp) { - dns_p_dump3(P, dns_rr_i_new(P, .sort = MAIN.sort), fp); - - if (MAIN.verbose > 2) - hexdump(P->data, P->end, fp); -} /* print_packet() */ - - -static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - struct dns_packet *P = dns_p_new(512); - struct dns_packet *Q = dns_p_new(512); - enum dns_section section; - struct dns_rr rr; - int error; - union dns_any any; - char pretty[sizeof any * 2]; - size_t len; - - P->end = fread(P->data, 1, P->size, stdin); - - fputs(";; [HEADER]\n", stdout); - fprintf(stdout, ";; qr : %s(%d)\n", (DNS_HEADER(P)->qr)? "RESPONSE" : "QUERY", DNS_HEADER(P)->qr); - fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(DNS_HEADER(P)->opcode), DNS_HEADER(P)->opcode); - fprintf(stdout, ";; aa : %s(%d)\n", (DNS_HEADER(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", DNS_HEADER(P)->aa); - fprintf(stdout, ";; tc : %s(%d)\n", (DNS_HEADER(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", DNS_HEADER(P)->tc); - fprintf(stdout, ";; rd : %s(%d)\n", (DNS_HEADER(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", DNS_HEADER(P)->rd); - fprintf(stdout, ";; ra : %s(%d)\n", (DNS_HEADER(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", DNS_HEADER(P)->ra); - fprintf(stdout, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); - - section = 0; - - dns_rr_foreach(&rr, P, .sort = MAIN.sort) { - if (section != rr.section) - fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); - - if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) - fprintf(stdout, "%s\n", pretty); - - dns_rr_copy(Q, &rr, P); - - section = rr.section; - } - - fputs("; ; ; ; ; ; ; ;\n\n", stdout); - - section = 0; - -#if 0 - dns_rr_foreach(&rr, Q, .name = "ns8.yahoo.com.") { -#else - struct dns_rr rrset[32]; - struct dns_rr_i *rri = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort); - unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error); - - for (unsigned i = 0; i < rrcount; i++) { - rr = rrset[i]; -#endif - if (section != rr.section) - fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(Q, rr.section)); - - if ((len = dns_rr_print(pretty, sizeof pretty, &rr, Q, &error))) - fprintf(stdout, "%s\n", pretty); - - section = rr.section; - } - - if (MAIN.verbose > 1) { - fprintf(stderr, "orig:%"PRIuZ"\n", P->end); - hexdump(P->data, P->end, stdout); - - fprintf(stderr, "copy:%"PRIuZ"\n", Q->end); - hexdump(Q->data, Q->end, stdout); - } - - return 0; -} /* parse_packet() */ - - -static int parse_domain(int argc, char *argv[]) { - char *dn; - - dn = (argc > 1)? argv[1] : "f.l.google.com"; - - printf("[%s]\n", dn); - - dn = dns_d_new(dn); - - do { - puts(dn); - } while (dns_d_cleave(dn, strlen(dn) + 1, dn, strlen(dn))); - - return 0; -} /* parse_domain() */ - - -static int trim_domain(int argc, char **argv) { - for (argc--, argv++; argc > 0; argc--, argv++) { - char name[DNS_D_MAXNAME + 1]; - - dns_d_trim(name, sizeof name, *argv, strlen(*argv), DNS_D_ANCHOR); - - puts(name); - } - - return 0; -} /* trim_domain() */ - - -static int expand_domain(int argc, char *argv[]) { - unsigned short rp = 0; - unsigned char *src = NULL; - unsigned char *dst; - struct dns_packet *pkt; - size_t lim = 0, len; - int error; - - if (argc > 1) - rp = atoi(argv[1]); - - len = slurp(&src, 0, stdin, "-"); - - if (!(pkt = dns_p_make(len, &error))) - panic("malloc(%"PRIuZ"): %s", len, dns_strerror(error)); - - memcpy(pkt->data, src, len); - pkt->end = len; - - lim = 1; - dst = grow(NULL, lim); - - while (lim <= (len = dns_d_expand(dst, lim, rp, pkt, &error))) { - lim = add(len, 1); - dst = grow(dst, lim); - } - - if (!len) - panic("expand: %s", dns_strerror(error)); - - fwrite(dst, 1, len, stdout); - fflush(stdout); - - free(src); - free(dst); - free(pkt); - - return 0; -} /* expand_domain() */ - - -static int show_resconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - unsigned i; - - resconf(); /* load it */ - - fputs("; SOURCES\n", stdout); - - for (i = 0; i < MAIN.resconf.count; i++) - fprintf(stdout, "; %s\n", MAIN.resconf.path[i]); - - for (i = 0; i < MAIN.nssconf.count; i++) - fprintf(stdout, "; %s\n", MAIN.nssconf.path[i]); - - fputs(";\n", stdout); - - dns_resconf_dump(resconf(), stdout); - - return 0; -} /* show_resconf() */ - - -static int show_nssconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - unsigned i; - - resconf(); - - fputs("# SOURCES\n", stdout); - - for (i = 0; i < MAIN.resconf.count; i++) - fprintf(stdout, "# %s\n", MAIN.resconf.path[i]); - - for (i = 0; i < MAIN.nssconf.count; i++) - fprintf(stdout, "# %s\n", MAIN.nssconf.path[i]); - - fputs("#\n", stdout); - - dns_nssconf_dump(resconf(), stdout); - - return 0; -} /* show_nssconf() */ - - -static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - unsigned i; - - hosts(); - - fputs("# SOURCES\n", stdout); - - for (i = 0; i < MAIN.hosts.count; i++) - fprintf(stdout, "# %s\n", MAIN.hosts.path[i]); - - fputs("#\n", stdout); - - dns_hosts_dump(hosts(), stdout); - - return 0; -} /* show_hosts() */ - - -static int query_hosts(int argc, char *argv[]) { - struct dns_packet *Q = dns_p_new(512); - struct dns_packet *A; - char qname[DNS_D_MAXNAME + 1]; - size_t qlen; - int error; - - if (!MAIN.qname) - MAIN.qname = (argc > 1)? argv[1] : "localhost"; - if (!MAIN.qtype) - MAIN.qtype = DNS_T_A; - - hosts(); - - if (MAIN.qtype == DNS_T_PTR && !strstr(MAIN.qname, "arpa")) { - union { struct in_addr a; struct in6_addr a6; } addr; - int af = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET; - - if ((error = dns_pton(af, MAIN.qname, &addr))) - panic("%s: %s", MAIN.qname, dns_strerror(error)); - - qlen = dns_ptr_qname(qname, sizeof qname, af, &addr); - } else - qlen = dns_strlcpy(qname, MAIN.qname, sizeof qname); - - if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, MAIN.qtype, DNS_C_IN, 0, 0))) - panic("%s: %s", qname, dns_strerror(error)); - - if (!(A = dns_hosts_query(hosts(), Q, &error))) - panic("%s: %s", qname, dns_strerror(error)); - - print_packet(A, stdout); - - free(A); - - return 0; -} /* query_hosts() */ - - -static int search_list(int argc, char *argv[]) { - const char *qname = (argc > 1)? argv[1] : "f.l.google.com"; - unsigned long i = 0; - char name[DNS_D_MAXNAME + 1]; - - printf("[%s]\n", qname); - - while (dns_resconf_search(name, sizeof name, qname, strlen(qname), resconf(), &i)) - puts(name); - - return 0; -} /* search_list() */ - - -static int permute_set(int argc, char *argv[]) { - unsigned lo, hi, i; - struct dns_k_permutor p; - - hi = (--argc > 0)? atoi(argv[argc]) : 8; - lo = (--argc > 0)? atoi(argv[argc]) : 0; - - fprintf(stderr, "[%u .. %u]\n", lo, hi); - - dns_k_permutor_init(&p, lo, hi); - - for (i = lo; i <= hi; i++) - fprintf(stdout, "%u\n", dns_k_permutor_step(&p)); -// printf("%u -> %u -> %u\n", i, dns_k_permutor_E(&p, i), dns_k_permutor_D(&p, dns_k_permutor_E(&p, i))); - - return 0; -} /* permute_set() */ - - -static int shuffle_16(int argc, char *argv[]) { - unsigned n, r; - - if (--argc > 0) { - n = 0xffff & atoi(argv[argc]); - r = (--argc > 0)? (unsigned)atoi(argv[argc]) : dns_random(); - - fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); - } else { - r = dns_random(); - - for (n = 0; n < 65536; n++) - fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); - } - - return 0; -} /* shuffle_16() */ - - -static int dump_random(int argc, char *argv[]) { - unsigned char b[32]; - unsigned i, j, n, r; - - n = (argc > 1)? atoi(argv[1]) : 32; - - while (n) { - i = 0; - - do { - r = dns_random(); - - for (j = 0; j < sizeof r && i < n && i < sizeof b; i++, j++) { - b[i] = 0xff & r; - r >>= 8; - } - } while (i < n && i < sizeof b); - - hexdump(b, i, stdout); - - n -= i; - } - - return 0; -} /* dump_random() */ - - -static int send_query(int argc, char *argv[]) { - struct dns_packet *A, *Q = dns_p_new(512); - char host[INET6_ADDRSTRLEN + 1]; - struct sockaddr_storage ss; - struct dns_socket *so; - int error, type; - - if (argc > 1) { - ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET; - - if ((error = dns_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss, NULL)))) - panic("%s: %s", argv[1], dns_strerror(error)); - - *dns_sa_port(ss.ss_family, &ss) = htons(53); - } else - memcpy(&ss, &resconf()->nameserver[0], dns_sa_len(&resconf()->nameserver[0])); - - if (!dns_inet_ntop(ss.ss_family, dns_sa_addr(ss.ss_family, &ss, NULL), host, sizeof host)) - panic("bad host address, or none provided"); - - if (!MAIN.qname) - MAIN.qname = "ipv6.google.com"; - if (!MAIN.qtype) - MAIN.qtype = DNS_T_AAAA; - - if ((error = dns_p_push(Q, DNS_S_QD, MAIN.qname, strlen(MAIN.qname), MAIN.qtype, DNS_C_IN, 0, 0))) - panic("dns_p_push: %s", dns_strerror(error)); - - DNS_HEADER(Q)->rd = 1; - - if (strstr(argv[0], "udp")) - type = SOCK_DGRAM; - else if (strstr(argv[0], "tcp")) - type = SOCK_STREAM; - else - type = dns_res_tcp2type(resconf()->options.tcp); - - fprintf(stderr, "querying %s for %s IN %s\n", host, MAIN.qname, dns_strtype(MAIN.qtype)); - - if (!(so = dns_so_open((struct sockaddr *)&resconf()->iface, type, dns_opts(), &error))) - panic("dns_so_open: %s", dns_strerror(error)); - - while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) { - if (error != DNS_EAGAIN) - panic("dns_so_query: %s (%d)", dns_strerror(error), error); - if (dns_so_elapsed(so) > 10) - panic("query timed-out"); - - dns_so_poll(so, 1); - } - - print_packet(A, stdout); - - dns_so_close(so); - - return 0; -} /* send_query() */ - - -static int print_arpa(int argc, char *argv[]) { - const char *ip = (argc > 1)? argv[1] : "::1"; - int af = (strchr(ip, ':'))? AF_INET6 : AF_INET; - union { struct in_addr a4; struct in6_addr a6; } addr; - char host[DNS_D_MAXNAME + 1]; - - if (1 != dns_inet_pton(af, ip, &addr) || 0 == dns_ptr_qname(host, sizeof host, af, &addr)) - panic("%s: invalid address", ip); - - fprintf(stdout, "%s\n", host); - - return 0; -} /* print_arpa() */ - - -static int show_hints(int argc, char *argv[]) { - struct dns_hints *(*load)(struct dns_resolv_conf *, int *); - const char *which, *how, *who; - struct dns_hints *hints; - int error; - - which = (argc > 1)? argv[1] : "local"; - how = (argc > 2)? argv[2] : "plain"; - who = (argc > 3)? argv[3] : "google.com"; - - load = (0 == strcmp(which, "local")) - ? &dns_hints_local - : &dns_hints_root; - - if (!(hints = load(resconf(), &error))) - panic("%s: %s", argv[0], dns_strerror(error)); - - if (0 == strcmp(how, "plain")) { - dns_hints_dump(hints, stdout); - } else { - struct dns_packet *query, *answer; - - query = dns_p_new(512); - - if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0))) - panic("%s: %s", who, dns_strerror(error)); - - if (!(answer = dns_hints_query(hints, query, &error))) - panic("%s: %s", who, dns_strerror(error)); - - print_packet(answer, stdout); - - free(answer); - } - - dns_hints_close(hints); - - return 0; -} /* show_hints() */ - - -static int resolve_query(int argc DNS_NOTUSED, char *argv[]) { - _Bool recurse = !!strstr(argv[0], "recurse"); - struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; - struct dns_resolver *R; - struct dns_packet *ans; - const struct dns_stat *st; - int error; - - if (!MAIN.qname) - MAIN.qname = "www.google.com"; - if (!MAIN.qtype) - MAIN.qtype = DNS_T_A; - - resconf()->options.recurse = recurse; - - if (!(R = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) - panic("%s: %s", MAIN.qname, dns_strerror(error)); - - if ((error = dns_res_submit(R, MAIN.qname, MAIN.qtype, DNS_C_IN))) - panic("%s: %s", MAIN.qname, dns_strerror(error)); - - while ((error = dns_res_check(R))) { - if (error != DNS_EAGAIN) - panic("dns_res_check: %s (%d)", dns_strerror(error), error); - if (dns_res_elapsed(R) > 30) - panic("query timed-out"); - - dns_res_poll(R, 1); - } - - ans = dns_res_fetch(R, &error); - print_packet(ans, stdout); - free(ans); - - st = dns_res_stat(R); - putchar('\n'); - printf(";; queries: %"PRIuZ"\n", st->queries); - printf(";; udp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.sent.count, st->udp.sent.bytes); - printf(";; udp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes); - printf(";; tcp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.sent.count, st->tcp.sent.bytes); - printf(";; tcp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes); - - dns_res_close(R); - - return 0; -} /* resolve_query() */ - - -static int resolve_addrinfo(int argc DNS_NOTUSED, char *argv[]) { - _Bool recurse = !!strstr(argv[0], "recurse"); - struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; - struct dns_resolver *res = NULL; - struct dns_addrinfo *ai = NULL; - struct addrinfo ai_hints = { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_CANONNAME }; - struct addrinfo *ent; - char pretty[512]; - int error; - - if (!MAIN.qname) - MAIN.qname = "www.google.com"; - /* NB: MAIN.qtype of 0 means obey hints.ai_family */ - - resconf()->options.recurse = recurse; - - if (!(res = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) - panic("%s: %s", MAIN.qname, dns_strerror(error)); - - if (!(ai = dns_ai_open(MAIN.qname, "80", MAIN.qtype, &ai_hints, res, &error))) - panic("%s: %s", MAIN.qname, dns_strerror(error)); - - do { - switch (error = dns_ai_nextent(&ent, ai)) { - case 0: - dns_ai_print(pretty, sizeof pretty, ent, ai); - - fputs(pretty, stdout); - - free(ent); - - break; - case ENOENT: - break; - case DNS_EAGAIN: - if (dns_ai_elapsed(ai) > 30) - panic("query timed-out"); - - dns_ai_poll(ai, 1); - - break; - default: - panic("dns_ai_nextent: %s (%d)", dns_strerror(error), error); - } - } while (error != ENOENT); - - dns_res_close(res); - dns_ai_close(ai); - - return 0; -} /* resolve_addrinfo() */ - - -static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - union { - struct sockaddr sa; - struct sockaddr_in sin; - } port; - int fd; - - memset(&port, 0, sizeof port); - port.sin.sin_family = AF_INET; - port.sin.sin_port = htons(5354); - port.sin.sin_addr.s_addr = inet_addr("127.0.0.1"); - - if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0))) - panic("socket: %s", strerror(errno)); - - if (0 != bind(fd, &port.sa, sizeof port.sa)) - panic("127.0.0.1:5353: %s", dns_strerror(errno)); - - for (;;) { - struct dns_packet *pkt = dns_p_new(512); - struct sockaddr_storage ss; - socklen_t slen = sizeof ss; - ssize_t count; -#if defined(MSG_WAITALL) /* MinGW issue */ - int rflags = MSG_WAITALL; -#else - int rflags = 0; -#endif - - count = recvfrom(fd, (char *)pkt->data, pkt->size, rflags, (struct sockaddr *)&ss, &slen); - - if (!count || count < 0) - panic("recvfrom: %s", strerror(errno)); - - pkt->end = count; - - dns_p_dump(pkt, stdout); - - (void)sendto(fd, (char *)pkt->data, pkt->end, 0, (struct sockaddr *)&ss, slen); - } - - return 0; -} /* echo_port() */ - - -static int isection(int argc, char *argv[]) { - const char *name = (argc > 1)? argv[1] : ""; - int type; - - type = dns_isection(name); - name = dns_strsection(type); - - printf("%s (%d)\n", name, type); - - return 0; -} /* isection() */ - - -static int iclass(int argc, char *argv[]) { - const char *name = (argc > 1)? argv[1] : ""; - int type; - - type = dns_iclass(name); - name = dns_strclass(type); - - printf("%s (%d)\n", name, type); - - return 0; -} /* iclass() */ - - -static int itype(int argc, char *argv[]) { - const char *name = (argc > 1)? argv[1] : ""; - int type; - - type = dns_itype(name); - name = dns_strtype(type); - - printf("%s (%d)\n", name, type); - - return 0; -} /* itype() */ - - -static int iopcode(int argc, char *argv[]) { - const char *name = (argc > 1)? argv[1] : ""; - int type; - - type = dns_iopcode(name); - name = dns_stropcode(type); - - printf("%s (%d)\n", name, type); - - return 0; -} /* iopcode() */ - - -static int ircode(int argc, char *argv[]) { - const char *name = (argc > 1)? argv[1] : ""; - int type; - - type = dns_ircode(name); - name = dns_strrcode(type); - - printf("%s (%d)\n", name, type); - - return 0; -} /* ircode() */ - - -#define SIZE1(x) { DNS_PP_STRINGIFY(x), sizeof (x) } -#define SIZE2(x, ...) SIZE1(x), SIZE1(__VA_ARGS__) -#define SIZE3(x, ...) SIZE1(x), SIZE2(__VA_ARGS__) -#define SIZE4(x, ...) SIZE1(x), SIZE3(__VA_ARGS__) -#define SIZE(...) DNS_PP_CALL(DNS_PP_XPASTE(SIZE, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) - -static int sizes(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - static const struct { const char *name; size_t size; } type[] = { - SIZE(struct dns_header, struct dns_packet, struct dns_rr, struct dns_rr_i), - SIZE(struct dns_a, struct dns_aaaa, struct dns_mx, struct dns_ns), - SIZE(struct dns_cname, struct dns_soa, struct dns_ptr, struct dns_srv), - SIZE(struct dns_sshfp, struct dns_txt, union dns_any), - SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i), - SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo), - SIZE(struct dns_cache), SIZE(size_t), SIZE(void *), SIZE(long) - }; - unsigned i, max; - - for (i = 0, max = 0; i < lengthof(type); i++) - max = DNS_PP_MAX(max, strlen(type[i].name)); - - for (i = 0; i < lengthof(type); i++) - printf("%*s : %"PRIuZ"\n", max, type[i].name, type[i].size); - - return 0; -} /* sizes() */ - - -static const struct { const char *cmd; int (*run)(); const char *help; } cmds[] = { - { "parse-packet", &parse_packet, "parse binary packet from stdin" }, - { "parse-domain", &parse_domain, "anchor and iteratively cleave domain" }, - { "trim-domain", &trim_domain, "trim and anchor domain name" }, - { "expand-domain", &expand_domain, "expand domain at offset NN in packet from stdin" }, - { "show-resconf", &show_resconf, "show resolv.conf data" }, - { "show-hosts", &show_hosts, "show hosts data" }, - { "show-nssconf", &show_nssconf, "show nsswitch.conf data" }, - { "query-hosts", &query_hosts, "query A, AAAA or PTR in hosts data" }, - { "search-list", &search_list, "generate query search list from domain" }, - { "permute-set", &permute_set, "generate random permutation -> (0 .. N or N .. M)" }, - { "shuffle-16", &shuffle_16, "simple 16-bit permutation" }, - { "dump-random", &dump_random, "generate random bytes" }, - { "send-query", &send_query, "send query to host" }, - { "send-query-udp", &send_query, "send udp query to host" }, - { "send-query-tcp", &send_query, "send tcp query to host" }, - { "print-arpa", &print_arpa, "print arpa. zone name of address" }, - { "show-hints", &show_hints, "print hints: show-hints [local|root] [plain|packet]" }, - { "resolve-stub", &resolve_query, "resolve as stub resolver" }, - { "resolve-recurse", &resolve_query, "resolve as recursive resolver" }, - { "addrinfo-stub", &resolve_addrinfo, "resolve through getaddrinfo clone" }, - { "addrinfo-recurse", &resolve_addrinfo, "resolve through getaddrinfo clone" }, -/* { "resolve-nameinfo", &resolve_query, "resolve as recursive resolver" }, */ - { "echo", &echo_port, "server echo mode, for nmap fuzzing" }, - { "isection", &isection, "parse section string" }, - { "iclass", &iclass, "parse class string" }, - { "itype", &itype, "parse type string" }, - { "iopcode", &iopcode, "parse opcode string" }, - { "ircode", &ircode, "parse rcode string" }, - { "sizes", &sizes, "print data structure sizes" }, -}; - - -static void print_usage(const char *progname, FILE *fp) { - static const char *usage = - " [OPTIONS] COMMAND [ARGS]\n" - " -c PATH Path to resolv.conf\n" - " -n PATH Path to nsswitch.conf\n" - " -l PATH Path to local hosts\n" - " -z PATH Path to zone cache\n" - " -q QNAME Query name\n" - " -t QTYPE Query type\n" - " -s HOW Sort records\n" - " -v Be more verbose (-vv show packets; -vvv hexdump packets)\n" - " -V Print version info\n" - " -h Print this usage message\n" - "\n"; - unsigned i, n, m; - - fputs(progname, fp); - fputs(usage, fp); - - for (i = 0, m = 0; i < lengthof(cmds); i++) { - if (strlen(cmds[i].cmd) > m) - m = strlen(cmds[i].cmd); - } - - for (i = 0; i < lengthof(cmds); i++) { - fprintf(fp, " %s ", cmds[i].cmd); - - for (n = strlen(cmds[i].cmd); n < m; n++) - putc(' ', fp); - - fputs(cmds[i].help, fp); - putc('\n', fp); - } - - fputs("\nReport bugs to William Ahern \n", fp); -} /* print_usage() */ - - -static void print_version(const char *progname, FILE *fp) { - fprintf(fp, "%s (dns.c) %.8X\n", progname, dns_v_rel()); - fprintf(fp, "vendor %s\n", dns_vendor()); - fprintf(fp, "release %.8X\n", dns_v_rel()); - fprintf(fp, "abi %.8X\n", dns_v_abi()); - fprintf(fp, "api %.8X\n", dns_v_api()); -} /* print_version() */ - - -int main(int argc, char **argv) { - extern int optind; - extern char *optarg; - const char *progname = argv[0]; - unsigned i; - int ch; - - while (-1 != (ch = getopt(argc, argv, "q:t:c:n:l:z:s:vVh"))) { - switch (ch) { - case 'c': - assert(MAIN.resconf.count < lengthof(MAIN.resconf.path)); - - MAIN.resconf.path[MAIN.resconf.count++] = optarg; - - break; - case 'n': - assert(MAIN.nssconf.count < lengthof(MAIN.nssconf.path)); - - MAIN.nssconf.path[MAIN.nssconf.count++] = optarg; - - break; - case 'l': - assert(MAIN.hosts.count < lengthof(MAIN.hosts.path)); - - MAIN.hosts.path[MAIN.hosts.count++] = optarg; - - break; - case 'z': - assert(MAIN.cache.count < lengthof(MAIN.cache.path)); - - MAIN.cache.path[MAIN.cache.count++] = optarg; - - break; - case 'q': - MAIN.qname = optarg; - - break; - case 't': - for (i = 0; i < lengthof(dns_rrtypes); i++) { - if (0 == strcasecmp(dns_rrtypes[i].name, optarg)) - { MAIN.qtype = dns_rrtypes[i].type; break; } - } - - if (MAIN.qtype) - break; - - for (i = 0; dns_isdigit(optarg[i]); i++) { - MAIN.qtype *= 10; - MAIN.qtype += optarg[i] - '0'; - } - - if (!MAIN.qtype) - panic("%s: invalid query type", optarg); - - break; - case 's': - if (0 == strcasecmp(optarg, "packet")) - MAIN.sort = &dns_rr_i_packet; - else if (0 == strcasecmp(optarg, "shuffle")) - MAIN.sort = &dns_rr_i_shuffle; - else if (0 == strcasecmp(optarg, "order")) - MAIN.sort = &dns_rr_i_order; - else - panic("%s: invalid sort method", optarg); - - break; - case 'v': - dns_debug = ++MAIN.verbose; - - break; - case 'V': - print_version(progname, stdout); - - return 0; - case 'h': - print_usage(progname, stdout); - - return 0; - default: - print_usage(progname, stderr); - - return EXIT_FAILURE; - } /* switch() */ - } /* while() */ - - argc -= optind; - argv += optind; - - for (i = 0; i < lengthof(cmds) && argv[0]; i++) { - if (0 == strcmp(cmds[i].cmd, argv[0])) - return cmds[i].run(argc, argv); - } - - print_usage(progname, stderr); - - return EXIT_FAILURE; -} /* main() */ - - -#endif /* DNS_MAIN */ - -/* - * pop file-scoped compiler annotations - */ -#if __clang__ -#pragma clang diagnostic pop -#elif DNS_GNUC_PREREQ(4,6,0) -#pragma GCC diagnostic pop -#endif - -#endif /* SYS_UNIX */ diff --git a/lib_fiber/c/src/dns/dns.h.bak b/lib_fiber/c/src/dns/dns.h.bak deleted file mode 100644 index cd41322e9..000000000 --- a/lib_fiber/c/src/dns/dns.h.bak +++ /dev/null @@ -1,1261 +0,0 @@ -/* ========================================================================== - * dns.h - Recursive, Reentrant DNS Resolver. - * -------------------------------------------------------------------------- - * Copyright (c) 2009, 2010, 2012-2015 William Ahern - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the - * following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN - * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * ========================================================================== - */ -#ifndef DNS_H -#define DNS_H - -#include "define.h" - -#ifdef SYS_UNIX - -#include /* size_t offsetof() */ -#include /* FILE */ - -#include /* strlen(3) */ - -#include /* time_t */ - -#if _WIN32 -#include -#include -#else -#include /* BYTE_ORDER BIG_ENDIAN _BIG_ENDIAN */ -#include /* socklen_t */ -#include /* struct socket */ - -#include /* POLLIN POLLOUT */ - -#include /* struct in_addr struct in6_addr */ - -#include /* struct addrinfo */ -#endif - - -/* - * V I S I B I L I T Y - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DNS_PUBLIC -#define DNS_PUBLIC -#endif - - -/* - * V E R S I O N - * - * Vendor: Entity for which versions numbers are relevant. (If forking - * change DNS_VENDOR to avoid confusion.) - * - * Three versions: - * - * REL Official "release"--bug fixes, new features, etc. - * ABI Changes to existing object sizes or parameter types. - * API Changes that might effect application source. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_VENDOR "william@25thandClement.com" - -#define DNS_V_REL 0x20161214 -#define DNS_V_ABI 0x20160608 -#define DNS_V_API 0x20160608 - - -DNS_PUBLIC const char *dns_vendor(void); - -DNS_PUBLIC int dns_v_rel(void); -DNS_PUBLIC int dns_v_abi(void); -DNS_PUBLIC int dns_v_api(void); - - -/* - * E R R O R S - * - * Errors and exceptions are always returned through an int. This should - * hopefully make integration easier in the majority of circumstances, and - * also cut down on useless compiler warnings. - * - * System and library errors are returned together. POSIX guarantees that - * all system errors are positive integers. Library errors are always - * negative integers in the range DNS_EBASE to DNS_ELAST, with the high bits - * set to the three magic ASCII characters "dns". - * - * dns_strerror() returns static English string descriptions of all known - * errors, and punts the remainder to strerror(3). - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_EBASE -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64) - -#define dns_error_t int /* for documentation only */ - -enum dns_errno { - DNS_ENOBUFS = DNS_EBASE, - DNS_EILLEGAL, - DNS_EORDER, - DNS_ESECTION, - DNS_EUNKNOWN, - DNS_EADDRESS, - DNS_ENOQUERY, - DNS_ENOANSWER, - DNS_EFETCHED, - DNS_ESERVICE, /* EAI_SERVICE */ - DNS_ENONAME, /* EAI_NONAME */ - DNS_EFAIL, /* EAI_FAIL */ - DNS_ELAST, -}; /* dns_errno */ - -DNS_PUBLIC const char *dns_strerror(dns_error_t); - -DNS_PUBLIC int *dns_debug_p(void); - -#define dns_debug (*dns_debug_p()) /* was extern int dns_debug before 20160523 API */ - - -/* - * C O M P I L E R A N N O T A T I O N S - * - * GCC with -Wextra, and clang by default, complain about overrides in - * initializer lists. Overriding previous member initializers is well - * defined behavior in C. dns.c relies on this behavior to define default, - * overrideable member values when instantiating configuration objects. - * - * dns_quietinit() guards a compound literal expression with pragmas to - * silence these shrill warnings. This alleviates the burden of requiring - * third-party projects to adjust their compiler flags. - * - * NOTE: If you take the address of the compound literal, take the address - * of the transformed expression, otherwise the compound literal lifetime is - * tied to the scope of the GCC statement expression. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#if defined __clang__ -#define DNS_PRAGMA_PUSH _Pragma("clang diagnostic push") -#define DNS_PRAGMA_QUIET _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"") -#define DNS_PRAGMA_POP _Pragma("clang diagnostic pop") - -#define dns_quietinit(...) \ - DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__ DNS_PRAGMA_POP -#elif (__GNUC__ < 9) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4) -#define DNS_PRAGMA_PUSH _Pragma("GCC diagnostic push") -#define DNS_PRAGMA_QUIET _Pragma("GCC diagnostic ignored \"-Woverride-init\"") -#define DNS_PRAGMA_POP _Pragma("GCC diagnostic pop") - -/* - * GCC parses the _Pragma operator less elegantly than clang. - * This only works up to GCC 9 - */ -#define dns_quietinit(...) \ - __extension__ ({ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__; DNS_PRAGMA_POP }) -#else -#define DNS_PRAGMA_PUSH -#define DNS_PRAGMA_QUIET -#define DNS_PRAGMA_POP -#define dns_quietinit(...) __VA_ARGS__ -#endif - -#if defined __GNUC__ -#define DNS_PRAGMA_EXTENSION __extension__ -#pragma GCC diagnostic ignored "-Wstrict-prototypes" -#else -#define DNS_PRAGMA_EXTENSION -#endif - - -/* - * E V E N T S I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#if defined(POLLIN) -#define DNS_POLLIN POLLIN -#else -#define DNS_POLLIN 1 -#endif - -#if defined(POLLOUT) -#define DNS_POLLOUT POLLOUT -#else -#define DNS_POLLOUT 2 -#endif - - -/* - * See Application Interface below for configuring libevent bitmasks instead - * of poll(2) bitmasks. - */ -#define DNS_EVREAD 2 -#define DNS_EVWRITE 4 - - -#define DNS_POLL2EV(set) \ - (((set) & DNS_POLLIN)? DNS_EVREAD : 0) | (((set) & DNS_POLLOUT)? DNS_EVWRITE : 0) - -#define DNS_EV2POLL(set) \ - (((set) & DNS_EVREAD)? DNS_POLLIN : 0) | (((set) & DNS_EVWRITE)? DNS_POLLOUT : 0) - - -/* - * E N U M E R A T I O N I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -enum dns_section { - DNS_S_QD = 0x01, -#define DNS_S_QUESTION DNS_S_QD - - DNS_S_AN = 0x02, -#define DNS_S_ANSWER DNS_S_AN - - DNS_S_NS = 0x04, -#define DNS_S_AUTHORITY DNS_S_NS - - DNS_S_AR = 0x08, -#define DNS_S_ADDITIONAL DNS_S_AR - - DNS_S_ALL = 0x0f -}; /* enum dns_section */ - - -enum dns_class { - DNS_C_IN = 1, - - DNS_C_ANY = 255 -}; /* enum dns_class */ - - -enum dns_type { - DNS_T_A = 1, - DNS_T_NS = 2, - DNS_T_CNAME = 5, - DNS_T_SOA = 6, - DNS_T_PTR = 12, - DNS_T_MX = 15, - DNS_T_TXT = 16, - DNS_T_AAAA = 28, - DNS_T_SRV = 33, - DNS_T_OPT = 41, - DNS_T_SSHFP = 44, - DNS_T_SPF = 99, - DNS_T_AXFR = 252, - - DNS_T_ALL = 255 -}; /* enum dns_type */ - - -enum dns_opcode { - DNS_OP_QUERY = 0, - DNS_OP_IQUERY = 1, - DNS_OP_STATUS = 2, - DNS_OP_NOTIFY = 4, - DNS_OP_UPDATE = 5, -}; /* dns_opcode */ - - -enum dns_rcode { - DNS_RC_NOERROR = 0, - DNS_RC_FORMERR = 1, - DNS_RC_SERVFAIL = 2, - DNS_RC_NXDOMAIN = 3, - DNS_RC_NOTIMP = 4, - DNS_RC_REFUSED = 5, - DNS_RC_YXDOMAIN = 6, - DNS_RC_YXRRSET = 7, - DNS_RC_NXRRSET = 8, - DNS_RC_NOTAUTH = 9, - DNS_RC_NOTZONE = 10, - - /* EDNS(0) extended RCODEs */ - DNS_RC_BADVERS = 16, -}; /* dns_rcode */ - - -/* - * NOTE: These string functions need a small buffer in case the literal - * integer value needs to be printed and returned. UNLESS this buffer is - * SPECIFIED, the returned string has ONLY BLOCK SCOPE. - */ -#define DNS_STRMAXLEN 47 /* "QUESTION|ANSWER|AUTHORITY|ADDITIONAL" */ - -DNS_PUBLIC const char *dns_strsection(enum dns_section, void *, size_t); -#define dns_strsection3(a, b, c) \ - dns_strsection((a), (b), (c)) -#define dns_strsection1(a) dns_strsection((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) -#define dns_strsection(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strsection, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) - -DNS_PUBLIC enum dns_section dns_isection(const char *); - -DNS_PUBLIC const char *dns_strclass(enum dns_class, void *, size_t); -#define dns_strclass3(a, b, c) dns_strclass((a), (b), (c)) -#define dns_strclass1(a) dns_strclass((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) -#define dns_strclass(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strclass, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) - -DNS_PUBLIC enum dns_class dns_iclass(const char *); - -DNS_PUBLIC const char *dns_strtype(enum dns_type, void *, size_t); -#define dns_strtype3(a, b, c) dns_strtype((a), (b), (c)) -#define dns_strtype1(a) dns_strtype((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) -#define dns_strtype(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strtype, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) - -DNS_PUBLIC enum dns_type dns_itype(const char *); - -DNS_PUBLIC const char *dns_stropcode(enum dns_opcode); - -DNS_PUBLIC enum dns_opcode dns_iopcode(const char *); - -DNS_PUBLIC const char *dns_strrcode(enum dns_rcode); - -DNS_PUBLIC enum dns_rcode dns_ircode(const char *); - - -/* - * A T O M I C I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -typedef unsigned long dns_atomic_t; - -typedef unsigned long dns_refcount_t; /* must be same value type as dns_atomic_t */ - - -/* - * C R Y P T O I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -typedef unsigned dns_random_f(void); - -DNS_PUBLIC dns_random_f **dns_random_p(void); - -#define dns_random (*dns_random_p()) /* was extern unsigned (*dns_random)(void) before 20160523 API */ - - -/* - * P A C K E T I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_header { - unsigned qid:16; - -#if (defined BYTE_ORDER && BYTE_ORDER == BIG_ENDIAN) || (defined __sun && defined _BIG_ENDIAN) - unsigned qr:1; - unsigned opcode:4; - unsigned aa:1; - unsigned tc:1; - unsigned rd:1; - - unsigned ra:1; - unsigned unused:3; - unsigned rcode:4; -#else - unsigned rd:1; - unsigned tc:1; - unsigned aa:1; - unsigned opcode:4; - unsigned qr:1; - - unsigned rcode:4; - unsigned unused:3; - unsigned ra:1; -#endif - - unsigned qdcount:16; - unsigned ancount:16; - unsigned nscount:16; - unsigned arcount:16; -}; /* struct dns_header */ - -#define DNS_HEADER(_p_) (&(_p_)->header) - - -#ifndef DNS_P_QBUFSIZ -#define DNS_P_QBUFSIZ dns_p_calcsize(256 + 4) -#endif - -#ifndef DNS_P_DICTSIZE -#define DNS_P_DICTSIZE 16 -#endif - -struct dns_packet { - unsigned short dict[DNS_P_DICTSIZE]; - - struct dns_p_memo { - struct dns_s_memo { - unsigned short base, end; - } qd, an, ns, ar; - - struct { - unsigned short p; - unsigned short maxudp; - unsigned ttl; - } opt; - } memo; - - struct { struct dns_packet *cqe_next, *cqe_prev; } cqe; - - size_t size, end; - - int:16; /* tcp padding */ - - DNS_PRAGMA_EXTENSION union { - struct dns_header header; - unsigned char data[1]; - }; -}; /* struct dns_packet */ - -#define dns_p_calcsize(n) (offsetof(struct dns_packet, data) + DNS_PP_MAX(12, (n))) - -#define dns_p_sizeof(P) dns_p_calcsize((P)->end) - -/** takes size of maximum desired payload */ -#define dns_p_new(n) (dns_p_init((struct dns_packet *)&(union { unsigned char b[dns_p_calcsize((n))]; struct dns_packet p; }){ { 0 } }, dns_p_calcsize((n)))) - -/** takes size of entire packet structure as allocated */ -DNS_PUBLIC struct dns_packet *dns_p_init(struct dns_packet *, size_t); - -/** takes size of maximum desired payload */ -DNS_PUBLIC struct dns_packet *dns_p_make(size_t, int *); - -DNS_PUBLIC int dns_p_grow(struct dns_packet **); - -DNS_PUBLIC struct dns_packet *dns_p_copy(struct dns_packet *, const struct dns_packet *); - -#define dns_p_opcode(P) (dns_header(P)->opcode) - -DNS_PUBLIC enum dns_rcode dns_p_rcode(struct dns_packet *); - -DNS_PUBLIC unsigned dns_p_count(struct dns_packet *, enum dns_section); - -DNS_PUBLIC int dns_p_push(struct dns_packet *, enum dns_section, void *, size_t, enum dns_type, enum dns_class, unsigned, void *); - -DNS_PUBLIC void dns_p_dictadd(struct dns_packet *, unsigned short); - -DNS_PUBLIC struct dns_packet *dns_p_merge(struct dns_packet *, enum dns_section, struct dns_packet *, enum dns_section, int *); - -DNS_PUBLIC void dns_p_dump(struct dns_packet *, FILE *); - -DNS_PUBLIC int dns_p_study(struct dns_packet *); - - -/* - * D O M A I N N A M E I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_D_MAXLABEL 63 /* + 1 '\0' */ -#define DNS_D_MAXNAME 255 /* + 1 '\0' */ - -#define DNS_D_ANCHOR 1 /* anchor domain w/ root "." */ -#define DNS_D_CLEAVE 2 /* cleave sub-domain */ -#define DNS_D_TRIM 4 /* remove superfluous dots */ - -#define dns_d_new3(a, b, f) dns_d_init(&(char[DNS_D_MAXNAME + 1]){ 0 }, DNS_D_MAXNAME + 1, (a), (b), (f)) -#define dns_d_new2(a, f) dns_d_new3((a), strlen((a)), (f)) -#define dns_d_new1(a) dns_d_new3((a), strlen((a)), DNS_D_ANCHOR) -#define dns_d_new(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_d_new, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) - -DNS_PUBLIC char *dns_d_init(void *, size_t, const void *, size_t, int); - -DNS_PUBLIC size_t dns_d_anchor(void *, size_t, const void *, size_t); - -DNS_PUBLIC size_t dns_d_cleave(void *, size_t, const void *, size_t); - -DNS_PUBLIC size_t dns_d_comp(void *, size_t, void *, size_t, struct dns_packet *, int *); - -DNS_PUBLIC size_t dns_d_expand(void *, size_t, unsigned short, struct dns_packet *, int *); - -DNS_PUBLIC unsigned short dns_d_skip(unsigned short, struct dns_packet *); - -DNS_PUBLIC int dns_d_push(struct dns_packet *, void *, size_t); - -DNS_PUBLIC size_t dns_d_cname(void *, size_t, const void *, size_t, struct dns_packet *, int *error); - - -/* - * R E S O U R C E R E C O R D I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_rr { - enum dns_section section; - - struct { - unsigned short p; - unsigned short len; - } dn; - - enum dns_type type; - enum dns_class class; - unsigned ttl; - - struct { - unsigned short p; - unsigned short len; - } rd; -}; /* struct dns_rr */ - - -DNS_PUBLIC int dns_rr_copy(struct dns_packet *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_rr_parse(struct dns_rr *, unsigned short, struct dns_packet *); - -DNS_PUBLIC unsigned short dns_rr_skip(unsigned short, struct dns_packet *); - -DNS_PUBLIC int dns_rr_cmp(struct dns_rr *, struct dns_packet *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC size_t dns_rr_print(void *, size_t, struct dns_rr *, struct dns_packet *, int *); - - -#define dns_rr_i_new(P, ...) \ - dns_rr_i_init(&dns_quietinit((struct dns_rr_i){ 0, __VA_ARGS__ }), (P)) - -struct dns_rr_i { - enum dns_section section; - const void *name; - enum dns_type type; - enum dns_class class; - const void *data; - - int follow; - - int (*sort)(); - unsigned args[2]; - - struct { - unsigned short next; - unsigned short count; - - unsigned exec; - unsigned regs[2]; - } state, saved; -}; /* struct dns_rr_i */ - -DNS_PUBLIC int dns_rr_i_packet(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); - -DNS_PUBLIC int dns_rr_i_order(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); - -DNS_PUBLIC int dns_rr_i_shuffle(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); - -DNS_PUBLIC struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *, struct dns_packet *); - -#define dns_rr_i_save(i) ((i)->saved = (i)->state) -#define dns_rr_i_rewind(i) ((i)->state = (i)->saved) -#define dns_rr_i_count(i) ((i)->state.count) - -DNS_PUBLIC unsigned dns_rr_grep(struct dns_rr *, unsigned, struct dns_rr_i *, struct dns_packet *, int *); - -#define dns_rr_foreach_(rr, P, ...) \ - for (struct dns_rr_i DNS_PP_XPASTE(i, __LINE__) = *dns_rr_i_new((P), __VA_ARGS__); dns_rr_grep((rr), 1, &DNS_PP_XPASTE(i, __LINE__), (P), &(int){ 0 }); ) - -#define dns_rr_foreach(...) dns_rr_foreach_(__VA_ARGS__) - - -/* - * A R E S O U R C E R E C O R D - */ - -struct dns_a { - struct in_addr addr; -}; /* struct dns_a */ - -DNS_PUBLIC int dns_a_parse(struct dns_a *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_a_push(struct dns_packet *, struct dns_a *); - -DNS_PUBLIC int dns_a_cmp(const struct dns_a *, const struct dns_a *); - -DNS_PUBLIC size_t dns_a_print(void *, size_t, struct dns_a *); - -DNS_PUBLIC size_t dns_a_arpa(void *, size_t, const struct dns_a *); - - -/* - * AAAA R E S O U R C E R E C O R D - */ - -struct dns_aaaa { - struct in6_addr addr; -}; /* struct dns_aaaa */ - -DNS_PUBLIC int dns_aaaa_parse(struct dns_aaaa *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_aaaa_push(struct dns_packet *, struct dns_aaaa *); - -DNS_PUBLIC int dns_aaaa_cmp(const struct dns_aaaa *, const struct dns_aaaa *); - -DNS_PUBLIC size_t dns_aaaa_print(void *, size_t, struct dns_aaaa *); - -DNS_PUBLIC size_t dns_aaaa_arpa(void *, size_t, const struct dns_aaaa *); - - -/* - * MX R E S O U R C E R E C O R D - */ - -struct dns_mx { - unsigned short preference; - char host[DNS_D_MAXNAME + 1]; -}; /* struct dns_mx */ - -DNS_PUBLIC int dns_mx_parse(struct dns_mx *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_mx_push(struct dns_packet *, struct dns_mx *); - -DNS_PUBLIC int dns_mx_cmp(const struct dns_mx *, const struct dns_mx *); - -DNS_PUBLIC size_t dns_mx_print(void *, size_t, struct dns_mx *); - -DNS_PUBLIC size_t dns_mx_cname(void *, size_t, struct dns_mx *); - - -/* - * NS R E S O U R C E R E C O R D - */ - -struct dns_ns { - char host[DNS_D_MAXNAME + 1]; -}; /* struct dns_ns */ - -DNS_PUBLIC int dns_ns_parse(struct dns_ns *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_ns_push(struct dns_packet *, struct dns_ns *); - -DNS_PUBLIC int dns_ns_cmp(const struct dns_ns *, const struct dns_ns *); - -DNS_PUBLIC size_t dns_ns_print(void *, size_t, struct dns_ns *); - -DNS_PUBLIC size_t dns_ns_cname(void *, size_t, struct dns_ns *); - - -/* - * CNAME R E S O U R C E R E C O R D - */ - -struct dns_cname { - char host[DNS_D_MAXNAME + 1]; -}; /* struct dns_cname */ - -DNS_PUBLIC int dns_cname_parse(struct dns_cname *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_cname_push(struct dns_packet *, struct dns_cname *); - -DNS_PUBLIC int dns_cname_cmp(const struct dns_cname *, const struct dns_cname *); - -DNS_PUBLIC size_t dns_cname_print(void *, size_t, struct dns_cname *); - -DNS_PUBLIC size_t dns_cname_cname(void *, size_t, struct dns_cname *); - - -/* - * SOA R E S O U R C E R E C O R D - */ - -struct dns_soa { - char mname[DNS_D_MAXNAME + 1]; - char rname[DNS_D_MAXNAME + 1]; - unsigned serial, refresh, retry, expire, minimum; -}; /* struct dns_soa */ - -DNS_PUBLIC int dns_soa_parse(struct dns_soa *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_soa_push(struct dns_packet *, struct dns_soa *); - -DNS_PUBLIC int dns_soa_cmp(const struct dns_soa *, const struct dns_soa *); - -DNS_PUBLIC size_t dns_soa_print(void *, size_t, struct dns_soa *); - - -/* - * PTR R E S O U R C E R E C O R D - */ - -struct dns_ptr { - char host[DNS_D_MAXNAME + 1]; -}; /* struct dns_ptr */ - -DNS_PUBLIC int dns_ptr_parse(struct dns_ptr *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_ptr_push(struct dns_packet *, struct dns_ptr *); - -DNS_PUBLIC int dns_ptr_cmp(const struct dns_ptr *, const struct dns_ptr *); - -DNS_PUBLIC size_t dns_ptr_print(void *, size_t, struct dns_ptr *); - -DNS_PUBLIC size_t dns_ptr_cname(void *, size_t, struct dns_ptr *); - -DNS_PUBLIC size_t dns_ptr_qname(void *, size_t, int, void *); - - -/* - * SRV R E S O U R C E R E C O R D - */ - -struct dns_srv { - unsigned short priority; - unsigned short weight; - unsigned short port; - char target[DNS_D_MAXNAME + 1]; -}; /* struct dns_srv */ - -DNS_PUBLIC int dns_srv_parse(struct dns_srv *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_srv_push(struct dns_packet *, struct dns_srv *); - -DNS_PUBLIC int dns_srv_cmp(const struct dns_srv *, const struct dns_srv *); - -DNS_PUBLIC size_t dns_srv_print(void *, size_t, struct dns_srv *); - -DNS_PUBLIC size_t dns_srv_cname(void *, size_t, struct dns_srv *); - - -/* - * OPT R E S O U R C E R E C O R D - */ - -#ifndef DNS_OPT_MINDATA -#define DNS_OPT_MINDATA 256 -#endif - -#define DNS_OPT_DNSSEC 0x8000 - -struct dns_opt { - enum dns_rcode rcode; - unsigned char version; - unsigned short flags; - - union { - unsigned short maxsize; /* deprecated as confusing */ - unsigned short maxudp; /* maximum UDP payload size */ - }; - - size_t size, len; - unsigned char data[DNS_OPT_MINDATA]; -}; /* struct dns_opt */ - -#define DNS_OPT_INIT(opt) { .size = sizeof (*opt) - offsetof(struct dns_opt, data) } - -DNS_PUBLIC struct dns_opt *dns_opt_init(struct dns_opt *, size_t); - -DNS_PUBLIC int dns_opt_parse(struct dns_opt *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_opt_push(struct dns_packet *, struct dns_opt *); - -DNS_PUBLIC int dns_opt_cmp(const struct dns_opt *, const struct dns_opt *); - -DNS_PUBLIC size_t dns_opt_print(void *, size_t, struct dns_opt *); - -DNS_PUBLIC unsigned int dns_opt_ttl(const struct dns_opt *); - -DNS_PUBLIC unsigned short dns_opt_class(const struct dns_opt *); - -DNS_PUBLIC dns_error_t dns_opt_data_push(struct dns_opt *, unsigned char, unsigned short, const void *); - - -/* - * SSHFP R E S O U R C E R E C O R D - */ - -struct dns_sshfp { - enum dns_sshfp_key { - DNS_SSHFP_RSA = 1, - DNS_SSHFP_DSA = 2, - } algo; - - enum dns_sshfp_digest { - DNS_SSHFP_SHA1 = 1, - } type; - - union { - unsigned char sha1[20]; - } digest; -}; /* struct dns_sshfp */ - -DNS_PUBLIC int dns_sshfp_parse(struct dns_sshfp *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_sshfp_push(struct dns_packet *, struct dns_sshfp *); - -DNS_PUBLIC int dns_sshfp_cmp(const struct dns_sshfp *, const struct dns_sshfp *); - -DNS_PUBLIC size_t dns_sshfp_print(void *, size_t, struct dns_sshfp *); - - -/* - * TXT R E S O U R C E R E C O R D - */ - -#ifndef DNS_TXT_MINDATA -#define DNS_TXT_MINDATA 1024 -#endif - -struct dns_txt { - size_t size, len; - unsigned char data[DNS_TXT_MINDATA]; -}; /* struct dns_txt */ - -DNS_PUBLIC struct dns_txt *dns_txt_init(struct dns_txt *, size_t); - -DNS_PUBLIC int dns_txt_parse(struct dns_txt *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_txt_push(struct dns_packet *, struct dns_txt *); - -DNS_PUBLIC int dns_txt_cmp(const struct dns_txt *, const struct dns_txt *); - -DNS_PUBLIC size_t dns_txt_print(void *, size_t, struct dns_txt *); - - -/* - * ANY R E S O U R C E R E C O R D - */ - -union dns_any { - struct dns_a a; - struct dns_aaaa aaaa; - struct dns_mx mx; - struct dns_ns ns; - struct dns_cname cname; - struct dns_soa soa; - struct dns_ptr ptr; - struct dns_srv srv; - struct dns_opt opt; - struct dns_sshfp sshfp; - struct dns_txt txt, spf, rdata; -}; /* union dns_any */ - -#define DNS_ANY_INIT(any) { .rdata = { .size = sizeof *(any) - offsetof(struct dns_txt, data) } } - -DNS_PUBLIC union dns_any *dns_any_init(union dns_any *, size_t); - -DNS_PUBLIC int dns_any_parse(union dns_any *, struct dns_rr *, struct dns_packet *); - -DNS_PUBLIC int dns_any_push(struct dns_packet *, union dns_any *, enum dns_type); - -DNS_PUBLIC int dns_any_cmp(const union dns_any *, enum dns_type, const union dns_any *, enum dns_type); - -DNS_PUBLIC size_t dns_any_print(void *, size_t, union dns_any *, enum dns_type); - -DNS_PUBLIC size_t dns_any_cname(void *, size_t, union dns_any *, enum dns_type); - - -/* - * H O S T S I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_hosts; - -DNS_PUBLIC struct dns_hosts *dns_hosts_open(int *); - -DNS_PUBLIC void dns_hosts_close(struct dns_hosts *); - -DNS_PUBLIC dns_refcount_t dns_hosts_acquire(struct dns_hosts *); - -DNS_PUBLIC dns_refcount_t dns_hosts_release(struct dns_hosts *); - -DNS_PUBLIC struct dns_hosts *dns_hosts_mortal(struct dns_hosts *); - -DNS_PUBLIC struct dns_hosts *dns_hosts_local(int *); - -DNS_PUBLIC int dns_hosts_loadfile(struct dns_hosts *, FILE *); - -DNS_PUBLIC int dns_hosts_loadpath(struct dns_hosts *, const char *); - -DNS_PUBLIC int dns_hosts_dump(struct dns_hosts *, FILE *); - -DNS_PUBLIC int dns_hosts_insert(struct dns_hosts *, int, const void *, const void *, _Bool); - -DNS_PUBLIC struct dns_packet *dns_hosts_query(struct dns_hosts *, struct dns_packet *, int *); - - -/* - * R E S O L V . C O N F I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_resolv_conf { - struct sockaddr_storage nameserver[3]; - - char search[4][DNS_D_MAXNAME + 1]; - - /* (f)ile, (b)ind, (c)ache */ - char lookup[4 * (1 + (4 * 2))]; - - /* getaddrinfo family by preference order ("inet4", "inet6") */ - int family[3]; - - struct { - _Bool edns0; - - unsigned ndots; - - unsigned timeout; - - unsigned attempts; - - _Bool rotate; - - _Bool recurse; - - _Bool smart; - - enum { - DNS_RESCONF_TCP_ENABLE, - DNS_RESCONF_TCP_ONLY, - DNS_RESCONF_TCP_DISABLE, - } tcp; - } options; - - struct sockaddr_storage iface; - - struct { /* PRIVATE */ - dns_atomic_t refcount; - } _; -}; /* struct dns_resolv_conf */ - -DNS_PUBLIC struct dns_resolv_conf *dns_resconf_open(int *); - -DNS_PUBLIC void dns_resconf_close(struct dns_resolv_conf *); - -DNS_PUBLIC dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *); - -DNS_PUBLIC dns_refcount_t dns_resconf_release(struct dns_resolv_conf *); - -DNS_PUBLIC struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *); - -DNS_PUBLIC struct dns_resolv_conf *dns_resconf_local(int *); - -DNS_PUBLIC struct dns_resolv_conf *dns_resconf_root(int *); - -DNS_PUBLIC int dns_resconf_pton(struct sockaddr_storage *, const char *); - -DNS_PUBLIC int dns_resconf_loadfile(struct dns_resolv_conf *, FILE *); - -DNS_PUBLIC int dns_resconf_loadpath(struct dns_resolv_conf *, const char *); - -DNS_PUBLIC int dns_nssconf_loadfile(struct dns_resolv_conf *, FILE *); - -DNS_PUBLIC int dns_nssconf_loadpath(struct dns_resolv_conf *, const char *); - -DNS_PUBLIC int dns_resconf_dump(struct dns_resolv_conf *, FILE *); - -DNS_PUBLIC int dns_nssconf_dump(struct dns_resolv_conf *, FILE *); - -DNS_PUBLIC int dns_resconf_setiface(struct dns_resolv_conf *, const char *, unsigned short); - -typedef unsigned long dns_resconf_i_t; - -DNS_PUBLIC size_t dns_resconf_search(void *, size_t, const void *, size_t, struct dns_resolv_conf *, dns_resconf_i_t *); - - -/* - * H I N T S E R V E R I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_hints; - -DNS_PUBLIC struct dns_hints *dns_hints_open(struct dns_resolv_conf *, int *); - -DNS_PUBLIC void dns_hints_close(struct dns_hints *); - -DNS_PUBLIC dns_refcount_t dns_hints_acquire(struct dns_hints *); - -DNS_PUBLIC dns_refcount_t dns_hints_release(struct dns_hints *); - -DNS_PUBLIC struct dns_hints *dns_hints_mortal(struct dns_hints *); - -DNS_PUBLIC int dns_hints_insert(struct dns_hints *, const char *, const struct sockaddr *, unsigned); - -DNS_PUBLIC unsigned dns_hints_insert_resconf(struct dns_hints *, const char *, struct dns_resolv_conf *, int *); - -DNS_PUBLIC struct dns_hints *dns_hints_local(struct dns_resolv_conf *, int *); - -DNS_PUBLIC struct dns_hints *dns_hints_root(struct dns_resolv_conf *, int *); - -DNS_PUBLIC struct dns_packet *dns_hints_query(struct dns_hints *, struct dns_packet *, int *); - -DNS_PUBLIC int dns_hints_dump(struct dns_hints *, FILE *); - - -struct dns_hints_i { - const char *zone; - - struct { - unsigned next; - unsigned seed; - } state; -}; /* struct dns_hints_i */ - -#define dns_hints_i_new(...) (&(struct dns_hints_i){ __VA_ARGS__ }) - -DNS_PUBLIC unsigned dns_hints_grep(struct sockaddr **, socklen_t *, unsigned, struct dns_hints_i *, struct dns_hints *); - - -/* - * C A C H E I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_cache { - void *state; - - dns_refcount_t (*acquire)(struct dns_cache *); - dns_refcount_t (*release)(struct dns_cache *); - - struct dns_packet *(*query)(struct dns_packet *, struct dns_cache *, int *); - - int (*submit)(struct dns_packet *, struct dns_cache *); - int (*check)(struct dns_cache *); - struct dns_packet *(*fetch)(struct dns_cache *, int *); - - int (*pollfd)(struct dns_cache *); - short (*events)(struct dns_cache *); - void (*clear)(struct dns_cache *); - - union { - long i; - void *p; - } arg[3]; - - struct { /* PRIVATE */ - dns_atomic_t refcount; - } _; -}; /* struct dns_cache */ - - -DNS_PUBLIC struct dns_cache *dns_cache_init(struct dns_cache *); - -DNS_PUBLIC void dns_cache_close(struct dns_cache *); - - -/* - * A P P L I C A T I O N I N T E R F A C E - * - * Options to change the behavior of the API. Applies across all the - * different components. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_OPTS_INITIALIZER_ { 0, 0 }, 0 -#define DNS_OPTS_INITIALIZER { DNS_OPTS_INITIALIZER_ } -#define DNS_OPTS_INIT(...) { DNS_OPTS_INITIALIZER_, __VA_ARGS__ } - -#define dns_opts(...) (&dns_quietinit((struct dns_options)DNS_OPTS_INIT(__VA_ARGS__))) - -struct dns_options { - /* - * If the callback closes *fd, it must set it to -1. Otherwise, the - * descriptor is queued and lazily closed at object destruction or - * by an explicit call to _clear(). This allows safe use of - * kqueue(2), epoll(2), et al -style persistent events. - */ - struct { - void *arg; - int (*cb)(int *fd, void *arg); - } closefd; - - /* bitmask for _events() routines */ - enum dns_events { - DNS_SYSPOLL, - DNS_LIBEVENT, - } events; -}; /* struct dns_options */ - - -/* - * S T A T S I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_stat { - size_t queries; - - struct { - struct { - size_t count, bytes; - } sent, rcvd; - } udp, tcp; -}; /* struct dns_stat */ - - -/* - * S O C K E T I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_socket; - -DNS_PUBLIC struct dns_socket *dns_so_open(const struct sockaddr *, int, const struct dns_options *, int *error); - -DNS_PUBLIC void dns_so_close(struct dns_socket *); - -DNS_PUBLIC void dns_so_reset(struct dns_socket *); - -DNS_PUBLIC unsigned short dns_so_mkqid(struct dns_socket *so); - -DNS_PUBLIC struct dns_packet *dns_so_query(struct dns_socket *, struct dns_packet *, struct sockaddr *, int *); - -DNS_PUBLIC int dns_so_submit(struct dns_socket *, struct dns_packet *, struct sockaddr *); - -DNS_PUBLIC int dns_so_check(struct dns_socket *); - -DNS_PUBLIC struct dns_packet *dns_so_fetch(struct dns_socket *, int *); - -DNS_PUBLIC time_t dns_so_elapsed(struct dns_socket *); - -DNS_PUBLIC void dns_so_clear(struct dns_socket *); - -DNS_PUBLIC int dns_so_events(struct dns_socket *); - -DNS_PUBLIC int dns_so_pollfd(struct dns_socket *); - -DNS_PUBLIC int dns_so_poll(struct dns_socket *, int); - -DNS_PUBLIC const struct dns_stat *dns_so_stat(struct dns_socket *); - - -/* - * R E S O L V E R I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_resolver; - -DNS_PUBLIC struct dns_resolver *dns_res_open(struct dns_resolv_conf *, struct dns_hosts *hosts, struct dns_hints *, struct dns_cache *, const struct dns_options *, int *); - -DNS_PUBLIC struct dns_resolver *dns_res_stub(const struct dns_options *, int *); - -DNS_PUBLIC void dns_res_reset(struct dns_resolver *); - -DNS_PUBLIC void dns_res_close(struct dns_resolver *); - -DNS_PUBLIC dns_refcount_t dns_res_acquire(struct dns_resolver *); - -DNS_PUBLIC dns_refcount_t dns_res_release(struct dns_resolver *); - -DNS_PUBLIC struct dns_resolver *dns_res_mortal(struct dns_resolver *); - -DNS_PUBLIC int dns_res_submit(struct dns_resolver *, const char *, enum dns_type, enum dns_class); - -DNS_PUBLIC int dns_res_submit2(struct dns_resolver *, const char *, size_t, enum dns_type, enum dns_class); - -DNS_PUBLIC int dns_res_check(struct dns_resolver *); - -DNS_PUBLIC struct dns_packet *dns_res_fetch(struct dns_resolver *, int *); - -DNS_PUBLIC time_t dns_res_elapsed(struct dns_resolver *); - -DNS_PUBLIC void dns_res_clear(struct dns_resolver *); - -DNS_PUBLIC int dns_res_events(struct dns_resolver *); - -DNS_PUBLIC int dns_res_pollfd(struct dns_resolver *); - -DNS_PUBLIC time_t dns_res_timeout(struct dns_resolver *); - -DNS_PUBLIC int dns_res_poll(struct dns_resolver *, int); - -DNS_PUBLIC struct dns_packet *dns_res_query(struct dns_resolver *, const char *, enum dns_type, enum dns_class, int, int *); - -DNS_PUBLIC const struct dns_stat *dns_res_stat(struct dns_resolver *); - -DNS_PUBLIC void dns_res_sethints(struct dns_resolver *, struct dns_hints *); - - -/* - * A D D R I N F O I N T E R F A C E - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -struct dns_addrinfo; - -DNS_PUBLIC struct dns_addrinfo *dns_ai_open(const char *, const char *, enum dns_type, const struct addrinfo *, struct dns_resolver *, int *); - -DNS_PUBLIC void dns_ai_close(struct dns_addrinfo *); - -DNS_PUBLIC int dns_ai_nextent(struct addrinfo **, struct dns_addrinfo *); - -DNS_PUBLIC size_t dns_ai_print(void *, size_t, struct addrinfo *, struct dns_addrinfo *); - -DNS_PUBLIC time_t dns_ai_elapsed(struct dns_addrinfo *); - -DNS_PUBLIC void dns_ai_clear(struct dns_addrinfo *); - -DNS_PUBLIC int dns_ai_events(struct dns_addrinfo *); - -DNS_PUBLIC int dns_ai_pollfd(struct dns_addrinfo *); - -DNS_PUBLIC time_t dns_ai_timeout(struct dns_addrinfo *); - -DNS_PUBLIC int dns_ai_poll(struct dns_addrinfo *, int); - -DNS_PUBLIC const struct dns_stat *dns_ai_stat(struct dns_addrinfo *); - - -/* - * U T I L I T Y I N T E R F A C E S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -DNS_PUBLIC size_t dns_strlcpy(char *, const char *, size_t); - -DNS_PUBLIC size_t dns_strlcat(char *, const char *, size_t); - -// add by zsx, 2017.12.20 -void set_read_timeout(int timeo); -int get_read_timeout(void); -// end add by zsx - -/* - * M A C R O M A G I C S - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#define DNS_PP_MIN(a, b) (((a) < (b))? (a) : (b)) -#define DNS_PP_MAX(a, b) (((a) > (b))? (a) : (b)) -#define DNS_PP_NARG_(a, b, c, d, e, f, g, h, i, j, k, N,...) N -#define DNS_PP_NARG(...) DNS_PP_NARG_(__VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#define DNS_PP_CALL(F, ...) F(__VA_ARGS__) -#define DNS_PP_PASTE(x, y) x##y -#define DNS_PP_XPASTE(x, y) DNS_PP_PASTE(x, y) -#define DNS_PP_STRINGIFY_(s) #s -#define DNS_PP_STRINGIFY(s) DNS_PP_STRINGIFY_(s) -#define DNS_PP_D1 0 -#define DNS_PP_D2 1 -#define DNS_PP_D3 2 -#define DNS_PP_D4 3 -#define DNS_PP_D5 4 -#define DNS_PP_D6 5 -#define DNS_PP_D7 6 -#define DNS_PP_D8 7 -#define DNS_PP_D9 8 -#define DNS_PP_D10 9 -#define DNS_PP_D11 10 -#define DNS_PP_DEC(N) DNS_PP_XPASTE(DNS_PP_D, N) - -#endif /* SYS_UNIX */ -#endif /* DNS_H */ diff --git a/lib_fiber/c/src/event.c b/lib_fiber/c/src/event.c index f0d889cbc..1f7911e1d 100644 --- a/lib_fiber/c/src/event.c +++ b/lib_fiber/c/src/event.c @@ -74,7 +74,8 @@ EVENT *event_create(int size) #endif #ifdef HAS_EPOLL - ring_init(&ev->epoll_list); + ev->epoll_list = timer_cache_create(); + ring_init(&ev->epoll_ready); #endif return ev; } @@ -89,14 +90,13 @@ acl_handle_t event_handle(EVENT *ev) return ev->handle(ev); } -ssize_t event_size(EVENT *ev) -{ - return ev->setsize; -} - void event_free(EVENT *ev) { timer_cache_free(ev->poll_list); +#ifdef HAS_EPOLL + timer_cache_free(ev->epoll_list); +#endif + ev->free(ev); } @@ -253,7 +253,8 @@ int event_add_read(EVENT *ev, FILE_EVENT *fe, event_proc *proc) } if (fe->fd >= (socket_t) ev->setsize) { - msg_error("fd: %d >= setsize: %d", fe->fd, (int) ev->setsize); + msg_error("%s(%d): fd=%d >= setsize=%d", __FUNCTION__, + __LINE__, fe->fd, (int) ev->setsize); acl_fiber_set_error(ERANGE); return 0; } @@ -296,7 +297,8 @@ int event_add_write(EVENT *ev, FILE_EVENT *fe, event_proc *proc) } if (fe->fd >= (socket_t) ev->setsize) { - msg_error("fd: %d >= setsize: %d", fe->fd, (int) ev->setsize); + msg_error("%s(%d): fd=%d >= setsize=%d", __FUNCTION__, + __LINE__, fe->fd, (int) ev->setsize); acl_fiber_set_error(ERANGE); return 0; } @@ -410,7 +412,7 @@ static void event_process_poll(EVENT *ev) RING_ITER iter; RING *head; POLL_EVENT *pe; - long long now = event_get_stamp(ev); + long long now = event_get_stamp(ev); TIMER_CACHE_NODE *node = avl_first(&ev->poll_list->tree), *next; /* Check and call all the pe's callback which was timeout except the @@ -442,35 +444,29 @@ static void event_process_poll(EVENT *ev) #ifdef HAS_EPOLL static void event_process_epoll(EVENT *ev) { -#if 0 - while (1) { - EPOLL_EVENT *ee; - RING *head = ring_pop_head(&ev->epoll_list); - if (head == NULL) { - break; + RING_ITER iter; + RING *head; + EPOLL_EVENT *ee; + long long now = event_get_stamp(ev); + TIMER_CACHE_NODE *node = avl_first(&ev->epoll_list->tree), *next; + + while (node && node->expire >= 0 && node->expire <= now) { + next = AVL_NEXT(&ev->epoll_list->tree, node); + + ring_foreach(iter, &node->ring) { + ee = TO_APPL(iter.ptr, EPOLL_EVENT, me); + ee->proc(ev, ee); } + + node = next; + } + + while ((head = ring_pop_head(&ev->epoll_ready)) != NULL) { ee = TO_APPL(head, EPOLL_EVENT, me); ee->proc(ev, ee); } -#else - EPOLL_EVENT *ee; - RING *next, *curr; - long long now; - now = event_get_stamp(ev); - - for (next = ring_succ(&ev->epoll_list); next != &ev->epoll_list;) { - ee = ring_to_appl(next, EPOLL_EVENT, me); - if (ee->nready != 0 || (ee->expire >= 0 && now >= ee->expire)) { - curr = next; - next = ring_succ(next); - ring_detach(curr); - ee->proc(ev, ee); - } else { - next = ring_succ(next); - } - } -#endif + ring_init(&ev->epoll_ready); } #endif diff --git a/lib_fiber/c/src/event.h b/lib_fiber/c/src/event.h index ac055dc4b..15cc2899d 100644 --- a/lib_fiber/c/src/event.h +++ b/lib_fiber/c/src/event.h @@ -150,12 +150,6 @@ struct FILE_EVENT { }; #ifdef HAS_POLL -struct POLLFD { - FILE_EVENT *fe; - POLL_EVENT *pe; - struct pollfd *pfd; -}; - struct POLL_EVENT { RING me; @@ -169,24 +163,16 @@ struct POLL_EVENT { #endif #ifdef HAS_EPOLL -struct EPOLL_CTX { - int fd; - int op; - int mask; - int rmask; - FILE_EVENT *fe; - EPOLL_EVENT *ee; - epoll_data_t data; -}; + +typedef struct EPOLL EPOLL; struct EPOLL_EVENT { RING me; ACL_FIBER *fiber; + EPOLL *epoll; + epoll_proc *proc; - size_t nfds; long long expire; - EPOLL_CTX **fds; - int epfd; struct epoll_event *events; int maxevents; @@ -212,7 +198,8 @@ struct EVENT { #endif #ifdef HAS_EPOLL - RING epoll_list; + TIMER_CACHE *epoll_list; + RING epoll_ready; #endif unsigned waiter; @@ -242,7 +229,6 @@ void event_set(int event_mode); EVENT *event_create(int size); const char *event_name(EVENT *ev); acl_handle_t event_handle(EVENT *ev); -ssize_t event_size(EVENT *ev); void event_free(EVENT *ev); void event_close(EVENT *ev, FILE_EVENT *fe); long long event_set_stamp(EVENT *ev); diff --git a/lib_fiber/c/src/event/event_epoll.c b/lib_fiber/c/src/event/event_epoll.c index 50ce35c5c..cae0f1734 100644 --- a/lib_fiber/c/src/event/event_epoll.c +++ b/lib_fiber/c/src/event/event_epoll.c @@ -316,6 +316,14 @@ EVENT *event_epoll_create(int size) hook_init(); } + // Limit the max events buff maybe a good idea to impromve performance + // for handling large fds. Because epoll uses robin round to handle + // all the ready fds, so we needn't worry about the starvation of the + // left ready fds. + if (size <= 0 || size > 100) { + size = 100; + } + ep->events = (struct epoll_event *) mem_malloc(sizeof(struct epoll_event) * size); ep->size = size; @@ -325,7 +333,7 @@ EVENT *event_epoll_create(int size) ep->w_ready = array_create(100); #endif - ep->epfd = __sys_epoll_create(1024); + ep->epfd = __sys_epoll_create(size); assert(ep->epfd >= 0); ep->event.name = epoll_name; diff --git a/lib_fiber/c/src/fiber.c b/lib_fiber/c/src/fiber.c index 6a8dc9c91..f6621f6e8 100644 --- a/lib_fiber/c/src/fiber.c +++ b/lib_fiber/c/src/fiber.c @@ -36,7 +36,14 @@ typedef struct THREAD { unsigned idgen; int count; size_t switched; + size_t switched_old; int nlocal; + +#ifdef SHARE_STACK + char *stack_buff; + size_t stack_size; + size_t stack_dlen; +#endif } THREAD; static THREAD *__main_fiber = NULL; @@ -45,6 +52,10 @@ static __thread int __scheduled = 0; static __thread int __schedule_auto = 0; __thread int var_hook_sys_api = 0; +#ifdef SHARE_STACK +static size_t __shared_stack_size = 1024000; +#endif + #ifdef SYS_UNIX static FIBER_ALLOC_FN __fiber_alloc_fn = fiber_unix_alloc; static FIBER_ORIGIN_FN __fiber_origin_fn = fiber_unix_origin; @@ -52,8 +63,8 @@ static FIBER_ORIGIN_FN __fiber_origin_fn = fiber_unix_origin; static FIBER_ALLOC_FN __fiber_alloc_fn = fiber_win_alloc; static FIBER_ORIGIN_FN __fiber_origin_fn = fiber_win_origin; #else -static ACL_FIBER *fiber_dummy_alloc(void(*start_fn)(ACL_FIBER*) fiber_unused, - size_t size fiber_unused) +static ACL_FIBER *fiber_dummy_alloc(ACL_FIBER_ATTR *attr fiber_unused, + void(*start_fn)(ACL_FIBER*) fiber_unused, size_t size fiber_unused) { msg_fatal("unknown OS"); return NULL; @@ -98,13 +109,13 @@ static void thread_free(void *ctx) return; } - /* free fiber object in the dead fibers link */ + /* Free fiber object in the dead fibers link */ while ((head = ring_pop_head(&__thread_fiber->dead))) { fiber = RING_TO_APPL(head, ACL_FIBER, me); fiber_free(fiber); } - /* free all possible aliving fiber object */ + /* Free all possible aliving fiber object */ for (i = 0; i < __thread_fiber->slot; i++) { fiber_free(__thread_fiber->fibers[i]); } @@ -113,6 +124,10 @@ static void thread_free(void *ctx) mem_free(tf->fibers); } +#ifdef SHARE_STACK + mem_free(tf->stack_buff); +#endif + tf->original->free_fn(tf->original); mem_free(tf); @@ -165,6 +180,12 @@ static void fiber_check(void) __thread_fiber->count = 0; __thread_fiber->nlocal = 0; +#ifdef SHARE_STACK + __thread_fiber->stack_size = __shared_stack_size; + __thread_fiber->stack_buff = mem_malloc(__thread_fiber->stack_size); + __thread_fiber->stack_dlen = 0; +#endif + ring_init(&__thread_fiber->ready); ring_init(&__thread_fiber->dead); @@ -176,6 +197,40 @@ static void fiber_check(void) } } +ACL_FIBER *fiber_origin(void) +{ + return __thread_fiber->original; +} + +#ifdef SHARE_STACK + +char *fiber_share_stack_addr(void) +{ + return __thread_fiber->stack_buff; +} + +char *fiber_share_stack_bottom(void) +{ + return __thread_fiber->stack_buff + __thread_fiber->stack_size; +} + +size_t fiber_share_stack_size(void) +{ + return __thread_fiber->stack_size; +} + +size_t fiber_share_stack_dlen(void) +{ + return __thread_fiber->stack_dlen; +} + +void fiber_share_stack_set_dlen(size_t dlen) +{ + __thread_fiber->stack_dlen = dlen; +} + +#endif + #ifdef HOOK_ERRNO static void fiber_init(void) @@ -205,7 +260,7 @@ static void fiber_init(void) #endif } -/* see /usr/include/bits/errno.h for __errno_location */ +/* See /usr/include/bits/errno.h for __errno_location */ #ifdef ANDROID volatile int* __errno(void) #else @@ -356,7 +411,7 @@ static void fiber_swap(ACL_FIBER *from, ACL_FIBER *to) size_t slot = from->slot; int n = ring_size(&__thread_fiber->dead); - /* if the cached dead fibers reached the limit, + /* If the cached dead fibers reached the limit, * some will be freed */ if (n > MAX_CACHE) { @@ -474,14 +529,14 @@ void acl_fiber_signal(ACL_FIBER *fiber, int signum) fiber->signum = signum; - if (fiber == curr) { // just return if kill myself + if (fiber == curr) { // Just return if kill myself return; } ring_detach(&curr->me); ring_detach(&fiber->me); - /* add the current fiber and signed fiber in the head of the ready */ + /* Add the current fiber and signed fiber in the head of the ready */ #if 0 fiber_ready(fiber); fiber_yield(); @@ -537,25 +592,32 @@ void acl_fiber_ready(ACL_FIBER *fiber) int acl_fiber_yield(void) { - size_t n; - if (ring_size(&__thread_fiber->ready) == 0) { return 0; } - n = __thread_fiber->switched; + __thread_fiber->switched_old = __thread_fiber->switched; __thread_fiber->running->status = FIBER_STATUS_NONE; acl_fiber_ready(__thread_fiber->running); acl_fiber_switch(); - // when switched overflows, it will be set to 0, then n saved last + // When switched overflows, it will be set to 0, then n saved last // switched's value will larger than switched, so we need to use // abs function to avoiding this problem + if (__thread_fiber->switched == 0) { + return (int) (__thread_fiber->switched - + __thread_fiber->switched_old - 1); + } else { + return (int) (__thread_fiber->switched_old - + __thread_fiber->switched - 1); + } +/* #if defined(__APPLE__) || defined(SYS_WIN) || defined(ANDROID) - return (int) (__thread_fiber->switched - n - 1); + return (int) (__thread_fiber->switched - __thread_fiber->switched_old - 1); #else - return (int) abs(__thread_fiber->switched - n - 1); + return (int) abs(__thread_fiber->switched - __thread_fiber->switched_old - 1); #endif +*/ } unsigned acl_fiber_ndead(void) @@ -647,7 +709,7 @@ ACL_FIBER *acl_fiber_alloc(size_t size, void **pptr) } static ACL_FIBER *fiber_alloc(void (*fn)(ACL_FIBER *, void *), - void *arg, size_t size) + void *arg, const ACL_FIBER_ATTR *attr) { ACL_FIBER *fiber = NULL; RING *head; @@ -656,17 +718,17 @@ static ACL_FIBER *fiber_alloc(void (*fn)(ACL_FIBER *, void *), #define APPL RING_TO_APPL - /* try to reuse the fiber memory in dead queue */ + /* Try to reuse the fiber memory in dead queue */ head = ring_pop_head(&__thread_fiber->dead); if (head == NULL) { - fiber = __fiber_alloc_fn(fiber_start, size); + fiber = __fiber_alloc_fn(fiber_start, attr); fbase_init(&fiber->base, FBASE_F_FIBER); } else { fiber = APPL(head, ACL_FIBER, me); } __thread_fiber->idgen++; - if (__thread_fiber->idgen == 0) { /* overflow ? */ + if (__thread_fiber->idgen == 0) { /* Overflow ? */ __thread_fiber->idgen++; } @@ -675,12 +737,13 @@ static ACL_FIBER *fiber_alloc(void (*fn)(ACL_FIBER *, void *), fiber->signum = 0; fiber->fn = fn; fiber->arg = arg; + fiber->oflag = attr ? attr->oflag : 0; fiber->flag = 0; fiber->status = FIBER_STATUS_NONE; fiber->waiting = NULL; ring_init(&fiber->holding); - fiber->init_fn(fiber, size); + fiber->init_fn(fiber, attr ? attr->stack_size : 128000); return fiber; } @@ -690,10 +753,40 @@ void acl_fiber_schedule_init(int on) __schedule_auto = on; } +void acl_fiber_attr_init(ACL_FIBER_ATTR *attr) +{ + attr->oflag = 0; + attr->stack_size = 128000; +} + +void acl_fiber_attr_setstacksize(ACL_FIBER_ATTR *attr, size_t size) +{ + attr->stack_size = size; +} + +void acl_fiber_attr_setsharestack(ACL_FIBER_ATTR *attr, int on) +{ + if (on) { + attr->oflag |= ACL_FIBER_ATTR_SHARE_STACK; + } else { + attr->oflag &= ~ACL_FIBER_ATTR_SHARE_STACK; + } +} + ACL_FIBER *acl_fiber_create(void (*fn)(ACL_FIBER *, void *), void *arg, size_t size) { - ACL_FIBER *fiber = fiber_alloc(fn, arg, size); + ACL_FIBER_ATTR attr; + + acl_fiber_attr_init(&attr); + attr.stack_size = size; + return acl_fiber_create2(&attr, fn, arg); +} + +ACL_FIBER *acl_fiber_create2(const ACL_FIBER_ATTR *attr, + void (*fn)(ACL_FIBER *, void *), void *arg) +{ + ACL_FIBER *fiber = fiber_alloc(fn, arg, attr); __thread_fiber->count++; @@ -714,6 +807,11 @@ ACL_FIBER *acl_fiber_create(void (*fn)(ACL_FIBER *, void *), return fiber; } +int acl_fiber_use_share_stack(const ACL_FIBER *fiber) +{ + return fiber->oflag & ACL_FIBER_ATTR_SHARE_STACK ? 1 : 0; +} + unsigned int acl_fiber_id(const ACL_FIBER *fiber) { return fiber ? fiber->id : 0; @@ -733,6 +831,25 @@ int acl_fiber_status(const ACL_FIBER *fiber) return fiber ? fiber->status : 0; } +void acl_fiber_set_shared_stack_size(size_t size) +{ + if (size >= 1024) { +#ifdef SHARE_STACK + __shared_stack_size = size; +#endif + } +} + +size_t acl_fiber_get_shared_stack_size(void) +{ +#if defined(SHARE_STACK) + return __shared_stack_size; +#else + return 0; +#endif +} + + void acl_fiber_schedule_set_event(int event_mode) { event_set(event_mode); @@ -778,7 +895,7 @@ void acl_fiber_schedule(void) __thread_fiber->running = NULL; } - /* release dead fiber */ + /* Release dead fiber */ while ((head = ring_pop_head(&__thread_fiber->dead)) != NULL) { fiber = RING_TO_APPL(head, ACL_FIBER, me); fiber_free(fiber); diff --git a/lib_fiber/c/src/fiber.h b/lib_fiber/c/src/fiber.h index 0edab398f..ebed99668 100644 --- a/lib_fiber/c/src/fiber.h +++ b/lib_fiber/c/src/fiber.h @@ -50,17 +50,18 @@ struct ACL_FIBER { int errnum; int sys; int signum; + unsigned int oflag; unsigned int flag; - RING holding; - ACL_FIBER_MUTEX *waiting; - #define FIBER_F_SAVE_ERRNO (unsigned) 1 << 0 #define FIBER_F_KILLED (unsigned) 1 << 1 #define FIBER_F_CLOSED (unsigned) 1 << 2 #define FIBER_F_SIGNALED (unsigned) 1 << 3 #define FIBER_F_CANCELED (FIBER_F_KILLED | FIBER_F_CLOSED | FIBER_F_SIGNALED) + RING holding; + ACL_FIBER_MUTEX *waiting; + FIBER_LOCAL **locals; int nlocal; @@ -76,9 +77,19 @@ struct ACL_FIBER { /* in fiber.c */ extern __thread int var_hook_sys_api; + FIBER_BASE *fbase_alloc(void); void fbase_free(FIBER_BASE *fbase); void fiber_free(ACL_FIBER *fiber); +ACL_FIBER *fiber_origin(void); + +#ifdef SHARE_STACK +char *fiber_share_stack_addr(void); +char *fiber_share_stack_bottom(void); +size_t fiber_share_stack_size(void); +size_t fiber_share_stack_dlen(void); +void fiber_share_stack_set_dlen(size_t dlen); +#endif /* in fbase.c */ void fbase_event_open(FIBER_BASE *fbase); @@ -124,13 +135,15 @@ int epoll_event_close(int epfd); /* in fiber/fiber_unix.c */ #ifdef SYS_UNIX ACL_FIBER *fiber_unix_origin(void); -ACL_FIBER *fiber_unix_alloc(void (*start_fn)(ACL_FIBER *), size_t size); +ACL_FIBER *fiber_unix_alloc(void (*start_fn)(ACL_FIBER *), + const ACL_FIBER_ATTR *attr); #endif /* in fiber/fiber_win.c */ #ifdef SYS_WIN ACL_FIBER *fiber_win_origin(void); -ACL_FIBER *fiber_win_alloc(void (*start_fn)(ACL_FIBER *), size_t size); +ACL_FIBER *fiber_win_alloc(void (*start_fn)(ACL_FIBER *), + const ACL_FIBER_ATTR *attr); #endif #endif diff --git a/lib_fiber/c/src/fiber/fiber_unix.c b/lib_fiber/c/src/fiber/fiber_unix.c index 51bab1029..f1b30cdf7 100644 --- a/lib_fiber/c/src/fiber/fiber_unix.c +++ b/lib_fiber/c/src/fiber/fiber_unix.c @@ -45,9 +45,14 @@ typedef struct FIBER_UNIX { # endif size_t size; char *buff; + size_t dlen; } FIBER_UNIX; #if defined(USE_BOOST_JMP) +# if defined(SHARE_STACK) +# error "Not support shared stack when using boost jmp!" +# endif + typedef struct { FIBER_UNIX *from; FIBER_UNIX *to; @@ -64,18 +69,64 @@ static void swap_fcontext(FIBER_UNIX *from, FIBER_UNIX *to) jmp = (s_jump_t*) trans.data; jmp->from->fcontext = trans.fctx; } + +#endif + +#if defined(SHARE_STACK) + +static void fiber_stack_save(FIBER_UNIX *curr, const char *stack_top) +{ + curr->dlen = fiber_share_stack_bottom() - stack_top; + if (curr->dlen > curr->size) { + stack_free(curr->buff); + curr->buff = (char *) stack_alloc(curr->dlen); + curr->size = curr->dlen; + } + memcpy(curr->buff, stack_top, curr->dlen); +} + +static void fiber_stack_restore(FIBER_UNIX *curr) +{ + // After coming back, the current fiber's stack should be + // restored and copied from its private memory to the shared + // stack running memory. + if (curr->dlen > 0) { + char *bottom = fiber_share_stack_bottom(); + memcpy(bottom - curr->dlen, curr->buff, curr->dlen); + fiber_share_stack_set_dlen(curr->dlen); + } + // else if from->dlen == 0, the fiber must be the origin fiber + // that its fiber id should be 0. +} + #endif static void fiber_unix_swap(FIBER_UNIX *from, FIBER_UNIX *to) { + // The shared stack mode isn't supported in USE_BOOST_JMP current, + // which may be supported in future. + // If the fiber isn't in exiting status, and not the origin fiber, + // the fiber's running stack should be copied from the shared running + // stack to the fiber's private memory. +#if defined(SHARE_STACK) + if (from->fiber.oflag & ACL_FIBER_ATTR_SHARE_STACK + && from->fiber.status != FIBER_STATUS_EXITING + && from->fiber.id > 0) { + + char stack_top = 0; + fiber_stack_save(from, &stack_top); + } +#endif + #if defined(USE_BOOST_JMP) swap_fcontext(from, to); #elif defined(USE_JMP) - /* use setcontext() for the initial jump, as it allows us to set up + + /* Use setcontext() for the initial jump, as it allows us to set up * a stack, but continue with longjmp() as it's much faster. */ if (SETJMP(&from->env) == 0) { - /* context just be used once for set up a stack, which will + /* Context just be used once for set up a stack, which will * be freed in fiber_start. The context in __thread_fiber * was set NULL. */ @@ -85,12 +136,24 @@ static void fiber_unix_swap(FIBER_UNIX *from, FIBER_UNIX *to) LONGJMP(&to->env); } } -#else +#else // Use the default context swap API if (swapcontext(from->context, to->context) < 0) { msg_fatal("%s(%d), %s: swapcontext error %s", __FILE__, __LINE__, __FUNCTION__, last_serror()); } #endif + +#if defined(SHARE_STACK) + { + FIBER_UNIX *curr = (FIBER_UNIX *) acl_fiber_running(); + + if (curr->fiber.oflag & ACL_FIBER_ATTR_SHARE_STACK + && curr->fiber.status != FIBER_STATUS_EXITING) { + + fiber_stack_restore(curr); + } + } +#endif } static void fiber_unix_free(ACL_FIBER *fiber) @@ -111,13 +174,16 @@ static void fiber_unix_free(ACL_FIBER *fiber) } #if defined(USE_BOOST_JMP) + static void fiber_unix_start(transfer_t arg) { s_jump_t *jmp = (s_jump_t*) arg.data; jmp->from->fcontext = arg.fctx; jmp->to->fiber.start_fn(&jmp->to->fiber); } + #else + union cc_arg { void *p; @@ -135,7 +201,7 @@ static void fiber_unix_start(unsigned int x, unsigned int y) fb = (FIBER_UNIX *)arg.p; #ifdef USE_JMP - /* when using setjmp/longjmp, the context just be used only once */ + /* When using setjmp/longjmp, the context just be used only once */ if (fb->context != NULL) { stack_free(fb->context); fb->context = NULL; @@ -143,18 +209,20 @@ static void fiber_unix_start(unsigned int x, unsigned int y) #endif fb->fiber.start_fn(&fb->fiber); } -#endif // USE_BOOST_JMP + +#endif // !USE_BOOST_JMP static void fiber_unix_init(ACL_FIBER *fiber, size_t size) { FIBER_UNIX *fb = (FIBER_UNIX *) fiber; #if !defined(USE_BOOST_JMP) + FIBER_UNIX *origin; union cc_arg carg; sigset_t zero; #endif if (fb->size < size) { - /* if using realloc, real memory will be used, when we first + /* If using realloc, real memory will be used, when we first * free and malloc again, then we'll just use virtual memory, * because memcpy will be called in realloc. */ @@ -164,9 +232,22 @@ static void fiber_unix_init(ACL_FIBER *fiber, size_t size) } #if defined(USE_BOOST_JMP) +# if defined(SHARE_STACK) + if (fb->fiber.oflag & ACL_FIBER_ATTR_SHARE_STACK) { + fb->stack = fiber_share_stack_bottom(); + fb->fcontext = make_fcontext(fb->stack, + fiber_share_stack_size(), + (void(*)(transfer_t)) fiber_unix_start); + } else { + fb->stack = fb->buff + fb->size; + fb->fcontext = make_fcontext(fb->stack, fb->size, + (void(*)(transfer_t)) fiber_unix_start); + } +# else fb->stack = fb->buff + fb->size; fb->fcontext = make_fcontext(fb->stack, fb->size, (void(*)(transfer_t)) fiber_unix_start); +# endif #else carg.p = fiber; @@ -189,15 +270,28 @@ static void fiber_unix_init(ACL_FIBER *fiber, size_t size) __FILE__, __LINE__, __FUNCTION__, last_serror()); } - fb->context->uc_stack.ss_sp = fb->buff + 8; - fb->context->uc_stack.ss_size = fb->size - 64; - fb->context->uc_link = NULL; +#if defined(SHARE_STACK) + if (fb->fiber.oflag & ACL_FIBER_ATTR_SHARE_STACK) { + fb->context->uc_stack.ss_sp = fiber_share_stack_addr(); + fb->context->uc_stack.ss_size = fiber_share_stack_size(); + } else { + fb->context->uc_stack.ss_sp = fb->buff; + fb->context->uc_stack.ss_size = fb->size; + } +#else + fb->context->uc_stack.ss_sp = fb->buff; + fb->context->uc_stack.ss_size = fb->size; +#endif + + origin = (FIBER_UNIX*) fiber_origin(); + fb->context->uc_link = origin->context; + makecontext(fb->context, (void(*)(void)) fiber_unix_start, 2, carg.i[0], carg.i[1]); #endif #ifdef USE_VALGRIND - /* avoid the valgrind warning */ + /* Avoid the valgrind warning */ # if defined(USE_BOOST_JMP) fb->vid = VALGRIND_STACK_REGISTER(fb->buff, fb->stack); # else @@ -208,13 +302,16 @@ static void fiber_unix_init(ACL_FIBER *fiber, size_t size) #endif } -ACL_FIBER *fiber_unix_alloc(void (*start_fn)(ACL_FIBER *), size_t size) +ACL_FIBER *fiber_unix_alloc(void (*start_fn)(ACL_FIBER *), + const ACL_FIBER_ATTR *attr) { FIBER_UNIX *fb = (FIBER_UNIX *) mem_calloc(1, sizeof(*fb)); + size_t size = attr ? attr->stack_size : 128000; - /* no using calloc just avoiding using real memory */ + /* No using calloc just avoiding using real memory */ fb->buff = (char *) stack_alloc(size); fb->size = size; + fb->fiber.oflag = attr ? attr->oflag : 0; fb->fiber.init_fn = fiber_unix_init; fb->fiber.free_fn = fiber_unix_free; fb->fiber.swap_fn = (void (*)(ACL_FIBER*, ACL_FIBER*))fiber_unix_swap; @@ -236,12 +333,12 @@ ACL_FIBER *fiber_unix_origin(void) fb->fiber.start_fn = NULL; #elif defined(USE_JMP) - /* set context NULL when using setjmp that setcontext will not be + /* Set context NULL when using setjmp that setcontext will not be * called in fiber_swap. */ fb->context = NULL; #else - fb->context = (ucontext_t *) stack_calloc(sizeof(ucontext_t)); + fb->context = (ucontext_t *) stack_alloc(sizeof(ucontext_t)); #endif fb->fiber.free_fn = fiber_unix_free; fb->fiber.swap_fn = (void (*)(ACL_FIBER*, ACL_FIBER*)) fiber_unix_swap; diff --git a/lib_fiber/c/src/fiber/fiber_win.c b/lib_fiber/c/src/fiber/fiber_win.c index e424ce63e..1418300b4 100644 --- a/lib_fiber/c/src/fiber/fiber_win.c +++ b/lib_fiber/c/src/fiber/fiber_win.c @@ -62,9 +62,11 @@ static void fiber_win_init(FIBER_WIN *fb, size_t size) } } -ACL_FIBER *fiber_win_alloc(void (*start_fn)(ACL_FIBER *), size_t size) +ACL_FIBER *fiber_win_alloc(void (*start_fn)(ACL_FIBER *), + const ACL_FIBER_ATTR *attr) { FIBER_WIN *fb = (FIBER_WIN *) mem_calloc(1, sizeof(*fb)); + size_t size = attr ? attr->stack_size : 128000; fb->fiber.init_fn = (void (*)(ACL_FIBER*, size_t)) fiber_win_init; fb->fiber.free_fn = fiber_win_free; diff --git a/lib_fiber/c/src/fiber_io.c b/lib_fiber/c/src/fiber_io.c index 9ef5c6ac2..39b894f01 100644 --- a/lib_fiber/c/src/fiber_io.c +++ b/lib_fiber/c/src/fiber_io.c @@ -94,6 +94,8 @@ static void thread_init(void) static pthread_once_t __once_control = PTHREAD_ONCE_INIT; +// Notice: don't write log here to avoid recursive calling when user call +// acl_fiber_msg_register() to hook the log process. void fiber_io_check(void) { if (__thread_fiber != NULL) { @@ -109,8 +111,9 @@ void fiber_io_check(void) } if (pthread_once(&__once_control, thread_init) != 0) { - msg_fatal("%s(%d), %s: pthread_once error %s", + printf("%s(%d), %s: pthread_once error %s\r\n", __FILE__, __LINE__, __FUNCTION__, last_serror()); + abort(); } var_maxfd = open_limit(0); @@ -118,8 +121,6 @@ void fiber_io_check(void) var_maxfd = MAXFD; } - msg_info("%s(%d): maxfd=%d", __FUNCTION__, __LINE__, var_maxfd); - __thread_fiber = (FIBER_TLS *) mem_malloc(sizeof(FIBER_TLS)); __thread_fiber->event = event_create(var_maxfd); __thread_fiber->ev_fiber = acl_fiber_create(fiber_io_loop, @@ -140,7 +141,8 @@ void fiber_io_check(void) __main_fiber = __thread_fiber; atexit(fiber_io_main_free); } else if (pthread_setspecific(__fiber_key, __thread_fiber) != 0) { - msg_fatal("pthread_setspecific error!"); + printf("pthread_setspecific error!\r\n"); + abort(); } } @@ -568,8 +570,12 @@ static int fiber_file_del(FILE_EVENT *fe) void fiber_file_free(FILE_EVENT *fe) { - fiber_file_del(fe); - file_event_free(fe); + if (fiber_file_del(fe) == 0) { + file_event_free(fe); + } else { + // xxx: What happened? + msg_error("Some error happened for fe=%p, fd=%d", fe, fe->fd); + } } void fiber_file_close(FILE_EVENT *fe) diff --git a/lib_fiber/c/src/hook/dns_init.c.bak b/lib_fiber/c/src/hook/dns_init.c.bak deleted file mode 100644 index 9d58c2f60..000000000 --- a/lib_fiber/c/src/hook/dns_init.c.bak +++ /dev/null @@ -1,70 +0,0 @@ -#include "stdafx.h" -#include "dns/dns.h" -#include "common/pthread_patch.h" -#include "hook.h" - -#ifdef SYS_UNIX - -struct dns_resolv_conf *var_dns_conf = NULL; -struct dns_hosts *var_dns_hosts = NULL; -struct dns_hints *var_dns_hints = NULL; - -void fiber_dns_set_read_wait(int timeout) -{ - set_read_timeout(timeout); -} - -static void dns_on_exit(void) -{ - if (var_dns_conf) { - dns_resconf_close(var_dns_conf); - var_dns_conf = NULL; - } - - if (var_dns_hosts) { - dns_hosts_close(var_dns_hosts); - var_dns_hosts = NULL; - } - - if (var_dns_hints) { - dns_hints_close(var_dns_hints); - var_dns_hints = NULL; - } -} - -void fiber_dns_init(void) -{ -#ifdef SYS_WIN - static pthread_mutex_t __lock; -#elif defined(SYS_UNIX) - static pthread_mutex_t __lock = PTHREAD_MUTEX_INITIALIZER; -#endif - static int __called = 0; - int err; - - (void) pthread_mutex_lock(&__lock); - - if (__called) { - (void) pthread_mutex_unlock(&__lock); - return; - } - - __called++; - - err = 0; - var_dns_conf = dns_resconf_local(&err); - assert(var_dns_conf && err == 0); - var_dns_conf->options.timeout = 1000; - - var_dns_hosts = dns_hosts_local(&err); - assert(var_dns_hosts && err == 0); - - var_dns_hints = dns_hints_local(var_dns_conf, &err); - assert(var_dns_hints && err == 0); - - atexit(dns_on_exit); - - (void) pthread_mutex_unlock(&__lock); -} - -#endif diff --git a/lib_fiber/c/src/hook/epoll.c b/lib_fiber/c/src/hook/epoll.c index 44683c765..1d68a3716 100644 --- a/lib_fiber/c/src/hook/epoll.c +++ b/lib_fiber/c/src/hook/epoll.c @@ -10,32 +10,115 @@ /****************************************************************************/ -static EPOLL_EVENT *epfd_alloc(void) -{ - EPOLL_EVENT *ee = mem_calloc(1, sizeof(EPOLL_EVENT)); - int maxfd = open_limit(0); +struct EPOLL_CTX { + int fd; + int op; + int mask; + int rmask; + FILE_EVENT *fe; + EPOLL_EVENT *ee; + epoll_data_t data; +}; - if (maxfd <= 0) { - msg_fatal("%s(%d), %s: open_limit error %s", - __FILE__, __LINE__, __FUNCTION__, last_serror()); +/** + * All EPOLL_EVENT owned by its fiber are assosiate with the same one epoll fd. + * one epoll fd -|- one EPOLL -|- fiber EPOLL_EVENT + * |- fiber EPOLL_EVENT + * |- ... + * |- fiber EPOLL_EVENT -|- socket EPOLL_CTX + * |- socket EPOLL_CTX + * |- socket EPOLL_CTX + * |- ... + */ +struct EPOLL { + int epfd; + EPOLL_CTX **fds; + size_t nfds; + + // Store all EPOLL_EVENT, every fiber should use its own EPOLL_EVENT, + // Because in some case, one thread maybe have many fibers but it maybe + // use only one epoll fd to handle IO events, see acl_read_epoll_wait() + // in lib_acl/src/stdlib/iostuff/acl_read_wait.c. + HTABLE *ep_events; +}; + +/****************************************************************************/ + +#ifdef SYS_WIN +# define SNPRINTF _snprintf +#else +# define SNPRINTF snprintf +#endif + +static void epoll_event_free(EPOLL_EVENT *ee) +{ + mem_free(ee); +} + +static void fiber_on_exit(void *ctx) +{ + EPOLL_EVENT *ee = (EPOLL_EVENT*) ctx, *tmp; + ACL_FIBER *curr = acl_fiber_running(); + char key[32]; + + assert(curr); + + // If the epoll in ee has been set NULL in epoll_free(), the EPOLL + // must have been freed and the associated epoll fd must also have + // been closed, so we just only free the ee here. + if (ee->epoll == NULL) { + epoll_event_free(ee); + return; } - ++maxfd; - ee->fds = (EPOLL_CTX **) mem_malloc(maxfd * sizeof(EPOLL_CTX *)); - ee->nfds = maxfd; + SNPRINTF(key, sizeof(key), "%u", curr->id); + tmp = (EPOLL_EVENT *) htable_find(ee->epoll->ep_events, key); + + if (tmp == NULL) { + msg_fatal("%s(%d), %s: not found ee=%p, curr fiber=%d," + " ee fiber=%d", __FILE__, __LINE__, __FUNCTION__, + ee, acl_fiber_id(curr), acl_fiber_id(ee->fiber)); + } + + assert(tmp == ee); + htable_delete(ee->epoll->ep_events, key, NULL); + epoll_event_free(ee); +} + +static __thread int __local_key; + +static EPOLL_EVENT *epoll_event_alloc(void) +{ + // One EPOLL_EVENT can be owned by one fiber and be stored in the + // fiber's local store, so the EPOLL_EVENT can be used repeated by + // its owner fiber, and can be freed when the fiber is exiting. + + EPOLL_EVENT *ee = (EPOLL_EVENT*) acl_fiber_get_specific(__local_key); + if (ee) { + return ee; + } + + ee = mem_calloc(1, sizeof(EPOLL_EVENT)); + acl_fiber_set_specific(&__local_key, ee, fiber_on_exit); + + ring_init(&ee->me); + ee->fiber = acl_fiber_running(); return ee; } +/****************************************************************************/ + static ARRAY *__main_epfds = NULL; static __thread ARRAY *__epfds = NULL; static pthread_key_t __once_key; static pthread_once_t __once_control = PTHREAD_ONCE_INIT; +static void epoll_free(EPOLL *ep); + static void thread_free(void *ctx fiber_unused) { - size_t j; ITER iter; if (__epfds == NULL) { @@ -47,20 +130,13 @@ static void thread_free(void *ctx fiber_unused) } foreach(iter, __epfds) { - EPOLL_EVENT *ee = (EPOLL_EVENT *) iter.data; + EPOLL *ep = (EPOLL *) iter.data; - for (j = 0; j < ee->nfds; j++) { - if (ee->fds[j] != NULL) { - mem_free(ee->fds[j]); - } - } - - if (ee->epfd >= 0 && (*sys_close)(ee->epfd) < 0) { + if (ep->epfd >= 0 && (*sys_close)(ep->epfd) < 0) { fiber_save_errno(acl_fiber_last_error()); } - mem_free(ee->fds); - mem_free(ee); + epoll_free(ep); } array_free(__epfds, NULL); @@ -83,17 +159,21 @@ static void thread_init(void) } } -static EPOLL_EVENT *epoll_event_create(int epfd) +static EPOLL *epoll_alloc(int epfd) { - EPOLL_EVENT *ee = NULL; - size_t i; + EPOLL *ep; + int maxfd = open_limit(0), i; - /* using thread specific to store the epoll handles for each thread*/ + if (maxfd <= 0) { + msg_fatal("%s(%d), %s: open_limit error %s", + __FILE__, __LINE__, __FUNCTION__, last_serror()); + } + + /* Using thread local to store the epoll handles for each thread. */ if (__epfds == NULL) { if (pthread_once(&__once_control, thread_init) != 0) { msg_fatal("%s(%d), %s: pthread_once error %s", - __FILE__, __LINE__, __FUNCTION__, - last_serror()); + __FILE__, __LINE__, __FUNCTION__, last_serror()); } __epfds = array_create(5); @@ -105,25 +185,106 @@ static EPOLL_EVENT *epoll_event_create(int epfd) } } - ee = epfd_alloc(); - array_append(__epfds, ee); + ep = mem_malloc(sizeof(EPOLL)); + array_append(__epfds, ep); - /* duplicate the current thread's epoll fd, so we can assosiate the + /* Duplicate the current thread's epoll fd, so we can assosiate the * connection handles with one epoll fd for the current thread, and - * use one epoll fd for each thread to handle all fds + * use one epoll fd for each thread to handle all fds. */ - ee->epfd = dup(epfd); + ep->epfd = dup(epfd); - for (i = 0; i < ee->nfds; i++) { - ee->fds[i] = NULL; + ep->nfds = maxfd; + ep->fds = (EPOLL_CTX **) mem_malloc(maxfd * sizeof(EPOLL_CTX *)); + for (i = 0; i < maxfd; i++) { + ep->fds[i] = NULL; } - return ee; + ep->ep_events = htable_create(100); + return ep; } -static EPOLL_EVENT *epoll_event_find(int epfd) +static void epoll_free(EPOLL *ep) { ITER iter; + size_t i; + + // Walk through all EPOLL_EVENT stored in ep_events, and just set their + // epoll variable to NULL, because they will be freed in fiber_on_exit() + // when the fiber the EPOLL_EVENT belonging to is exiting. + + foreach(iter, ep->ep_events) { + EPOLL_EVENT *ee = (EPOLL_EVENT *) iter.data; + ee->epoll = NULL; + } + + htable_free(ep->ep_events, NULL); + + for (i = 0; i < ep->nfds; i++) { + if (ep->fds[i] != NULL) { + mem_free(ep->fds[i]); + } + } + + mem_free(ep->fds); + mem_free(ep); +} + +int epoll_event_close(int epfd) +{ + EVENT *ev; + int sys_epfd; + EPOLL *ep = NULL; + int pos = -1; + ITER iter; + + if (__epfds == NULL || epfd < 0) { + return -1; + } + + foreach(iter, __epfds) { + EPOLL *tmp = (EPOLL *) iter.data; + if (tmp->epfd == epfd) { + ep = tmp; + pos = iter.i; + break; + } + } + + if (ep == NULL) { + return -1; + } + + ev = fiber_io_event(); + assert(ev); + + sys_epfd = event_handle(ev); + assert(sys_epfd >= 0); + + // We can't close the epfd same as the internal fiber event's fd. + // Because we've alloced a new fd as a duplication of internal epfd + // in epoll_alloc by calling sys API dup(), the epfd here shouldn't + // be same as the internal epfd. + + if (epfd == sys_epfd) { + msg_error("%s(%d): can't close the event sys_epfd=%d", + __FUNCTION__, __LINE__, epfd); + return -1; + } + + epoll_free(ep); + array_delete(__epfds, pos, NULL); + + return (*sys_close)(epfd); +} + +static EPOLL_EVENT *epoll_event_find(int epfd, int create) +{ + ACL_FIBER *curr = acl_fiber_running(); + EPOLL *ep = NULL; + EPOLL_EVENT *ee; + char key[32]; + ITER iter; if (__epfds == NULL) { msg_error("%s(%d), %s: __epfds NULL", @@ -132,58 +293,43 @@ static EPOLL_EVENT *epoll_event_find(int epfd) } foreach(iter, __epfds) { - EPOLL_EVENT *ee = (EPOLL_EVENT *) iter.data; - if (ee->epfd == epfd) { - return ee; - } - } - - return NULL; -} - -int epoll_event_close(int epfd) -{ - ITER iter; - EPOLL_EVENT *ee = NULL; - int pos = -1; - size_t i; - - if (__epfds == NULL || epfd < 0) { - return -1; - } - - foreach(iter, __epfds) { - EPOLL_EVENT *e = (EPOLL_EVENT *) iter.data; - if (e->epfd == epfd) { - ee = e; - pos = iter.i; + EPOLL *tmp = (EPOLL *) iter.data; + if (tmp->epfd == epfd) { + ep = tmp; break; } } - if (ee == NULL) { - return -1; + if (ep == NULL) { + msg_error("%s(%d, %s: not found epfd=%d", + __FILE__, __LINE__, __FUNCTION__, epfd); + return NULL; } - for (i = 0; i < ee->nfds; i++) { - if (ee->fds[i] != NULL) { - mem_free(ee->fds[i]); - } + SNPRINTF(key, sizeof(key), "%u", curr->id); + ee = (EPOLL_EVENT *) htable_find(ep->ep_events, key); + if (ee != NULL) { + ee->epoll = ep; + return ee; } - mem_free(ee->fds); - mem_free(ee); - array_delete(__epfds, pos, NULL); + if (create) { + ee = epoll_event_alloc(); + ee->epoll = ep; + htable_enter(ep->ep_events, key, ee); - return (*sys_close)(epfd); + return ee; + } else { + return NULL; + } } /****************************************************************************/ int epoll_create(int size fiber_unused) { - EPOLL_EVENT *ee; EVENT *ev; + EPOLL *ep; int epfd; if (sys_epoll_create == NULL) { @@ -195,9 +341,9 @@ int epoll_create(int size fiber_unused) } ev = fiber_io_event(); + assert(ev); - /* get the current thread's epoll fd */ - + // Get the current thread's epoll fd. epfd = event_handle(ev); if (epfd < 0) { msg_error("%s(%d), %s: invalid event_handle %d", @@ -205,8 +351,10 @@ int epoll_create(int size fiber_unused) return epfd; } - ee = epoll_event_create(epfd); - return ee->epfd; + // The epoll fd will be duplicated in the below function, and the new + // fd will be returned to the caller. + ep = epoll_alloc(epfd); + return ep->epfd; } #ifdef EPOLL_CLOEXEC @@ -224,104 +372,146 @@ int epoll_create1(int flags) } #endif -static void read_callback(EVENT *ev fiber_unused, FILE_EVENT *fe) +static void read_callback(EVENT *ev, FILE_EVENT *fe) { - EPOLL_CTX *epx = fe->epx; - EPOLL_EVENT *ee = epx->ee; + EPOLL_CTX *epx = fe->epx; + EPOLL_EVENT *ee; + EPOLL *ep; - assert(ee); + assert(epx); assert(epx->mask & EVENT_READ); + ee = epx->ee; + assert(ee); + + ep = ee->epoll; + assert(ep); + + // If the ready count exceeds the maxevents been set which limits the + // the buffer space to hold the the ready fds, we just return to let + // the left ready fds keeped in system buffer, and hope they'll be + // handled in the next epoll_wait(). if (ee->nready >= ee->maxevents) { return; } ee->events[ee->nready].events |= EPOLLIN; - memcpy(&ee->events[ee->nready].data, &ee->fds[epx->fd]->data, - sizeof(ee->fds[epx->fd]->data)); + memcpy(&ee->events[ee->nready].data, &ep->fds[epx->fd]->data, + sizeof(ep->fds[epx->fd]->data)); + + if (ee->nready == 0) { + timer_cache_remove(ev->epoll_list, ee->expire, &ee->me); + ring_prepend(&ev->epoll_ready, &ee->me); + } + if (!(ee->events[ee->nready].events & EPOLLOUT)) { ee->nready++; } + SET_READABLE(fe); } static void write_callback(EVENT *ev fiber_unused, FILE_EVENT *fe) { - EPOLL_CTX *epx = fe->epx; - EPOLL_EVENT *ee = epx->ee; + EPOLL_CTX *epx = fe->epx; + EPOLL_EVENT *ee; + EPOLL *ep; - assert(ee); + assert(epx); assert(epx->mask & EVENT_WRITE); + + ee = epx->ee; + assert(ee); + + ep = ee->epoll; + assert(ep); + if (ee->nready >= ee->maxevents) { return; } ee->events[ee->nready].events |= EPOLLOUT; - memcpy(&ee->events[ee->nready].data, &ee->fds[epx->fd]->data, - sizeof(ee->fds[epx->fd]->data)); + memcpy(&ee->events[ee->nready].data, &ep->fds[epx->fd]->data, + sizeof(ep->fds[epx->fd]->data)); + + if (ee->nready == 0) { + timer_cache_remove(ev->epoll_list, ee->expire, &ee->me); + ring_prepend(&ev->epoll_ready, &ee->me); + } + if (!(ee->events[ee->nready].events & EPOLLIN)) { ee->nready++; } + SET_WRITABLE(fe); } static void epoll_ctl_add(EVENT *ev, EPOLL_EVENT *ee, struct epoll_event *event, int fd, int op) { - if (ee->fds[fd] == NULL) { - ee->fds[fd] = (EPOLL_CTX *) mem_malloc(sizeof(EPOLL_CTX)); + EPOLL *ep = ee->epoll; + + if (ep->fds[fd] == NULL) { + ep->fds[fd] = (EPOLL_CTX *) mem_malloc(sizeof(EPOLL_CTX)); } - ee->fds[fd]->fd = fd; - ee->fds[fd]->op = op; - ee->fds[fd]->mask = EVENT_NONE; - ee->fds[fd]->rmask = EVENT_NONE; - ee->fds[fd]->ee = ee; - ee->fds[fd]->fe->epx = ee->fds[fd]; + ep->fds[fd]->fd = fd; + ep->fds[fd]->op = op; + ep->fds[fd]->mask = EVENT_NONE; + ep->fds[fd]->rmask = EVENT_NONE; + ep->fds[fd]->ee = ee; - memcpy(&ee->fds[fd]->data, &event->data, sizeof(event->data)); + memcpy(&ep->fds[fd]->data, &event->data, sizeof(event->data)); if (event->events & EPOLLIN) { - ee->fds[fd]->mask |= EVENT_READ; - ee->fds[fd]->fe = fiber_file_open_read(fd); - event_add_read(ev, ee->fds[fd]->fe, read_callback); - SET_READWAIT(ee->fds[fd]->fe); + ep->fds[fd]->mask |= EVENT_READ; + ep->fds[fd]->fe = fiber_file_open_read(fd); + ep->fds[fd]->fe->epx = ep->fds[fd]; + + event_add_read(ev, ep->fds[fd]->fe, read_callback); + SET_READWAIT(ep->fds[fd]->fe); } + if (event->events & EPOLLOUT) { - ee->fds[fd]->mask |= EVENT_WRITE; - ee->fds[fd]->fe = fiber_file_open_write(fd); - event_add_write(ev, ee->fds[fd]->fe, write_callback); - SET_WRITEWAIT(ee->fds[fd]->fe); + ep->fds[fd]->mask |= EVENT_WRITE; + ep->fds[fd]->fe = fiber_file_open_write(fd); + ep->fds[fd]->fe->epx = ep->fds[fd]; + + event_add_write(ev, ep->fds[fd]->fe, write_callback); + SET_WRITEWAIT(ep->fds[fd]->fe); } } static void epoll_ctl_del(EVENT *ev, EPOLL_EVENT *ee, int fd) { - if (ee->fds[fd]->mask & EVENT_READ) { - event_del_read(ev, ee->fds[fd]->fe); - CLR_READWAIT(ee->fds[fd]->fe); - } - if (ee->fds[fd]->mask & EVENT_WRITE) { - event_del_write(ev, ee->fds[fd]->fe); - CLR_WRITEWAIT(ee->fds[fd]->fe); + EPOLL *ep = ee->epoll; + + if (ep->fds[fd]->mask & EVENT_READ) { + event_del_read(ev, ep->fds[fd]->fe); + CLR_READWAIT(ep->fds[fd]->fe); } - ee->fds[fd]->fd = -1; - ee->fds[fd]->op = 0; - ee->fds[fd]->mask = EVENT_NONE; - ee->fds[fd]->rmask = EVENT_NONE; - ee->fds[fd]->fe->epx = NULL; - ee->fds[fd]->fe = NULL; - memset(&ee->fds[fd]->data, 0, sizeof(ee->fds[fd]->data)); + if (ep->fds[fd]->mask & EVENT_WRITE) { + event_del_write(ev, ep->fds[fd]->fe); + CLR_WRITEWAIT(ep->fds[fd]->fe); + } - mem_free(ee->fds[fd]); - ee->fds[fd] = NULL; + ep->fds[fd]->fd = -1; + ep->fds[fd]->op = 0; + ep->fds[fd]->mask = EVENT_NONE; + ep->fds[fd]->rmask = EVENT_NONE; + ep->fds[fd]->fe->epx = NULL; + ep->fds[fd]->fe = NULL; + memset(&ep->fds[fd]->data, 0, sizeof(ep->fds[fd]->data)); + + mem_free(ep->fds[fd]); + ep->fds[fd] = NULL; } int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { - EPOLL_EVENT *ee; EVENT *ev; + EPOLL_EVENT *ee; if (sys_epoll_ctl == NULL) { hook_once(); @@ -331,13 +521,15 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) return sys_epoll_ctl ? (*sys_epoll_ctl)(epfd, op, fd, event) : -1; } - ee = epoll_event_find(epfd); + ee = epoll_event_find(epfd, 1); if (ee == NULL) { msg_error("%s(%d), %s: not exist epfd=%d", __FILE__, __LINE__, __FUNCTION__, epfd); return -1; } + assert(ee->epoll); + ev = fiber_io_event(); if (op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD) { @@ -346,10 +538,8 @@ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) msg_error("%s(%d), %s: invalid op %d, fd %d", __FILE__, __LINE__, __FUNCTION__, op, fd); return -1; - } else if (ee->fds[fd] != NULL) { + } else if (ee->epoll->fds[fd] != NULL) { epoll_ctl_del(ev, ee, fd); - CLR_READWAIT(ee->fds[fd]->fe); - CLR_WRITEWAIT(ee->fds[fd]->fe); } else { msg_error("%s(%d), %s: invalid fd=%d", __FILE__, __LINE__, __FUNCTION__, fd); @@ -398,7 +588,8 @@ int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) } if (!var_hook_sys_api) { - return sys_epoll_wait ? (*sys_epoll_wait)(epfd, events, maxevents, timeout) : -1; + return sys_epoll_wait ? (*sys_epoll_wait) + (epfd, events, maxevents, timeout) : -1; } ev = fiber_io_event(); @@ -408,7 +599,7 @@ int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) return -1; } - ee = epoll_event_find(epfd); + ee = epoll_event_find(epfd, 0); if (ee == NULL) { msg_error("%s(%d), %s: not exist epfd %d", __FILE__, __LINE__, __FUNCTION__, epfd); @@ -417,37 +608,51 @@ int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) ee->events = events; ee->maxevents = maxevents; - ee->nready = 0; ee->fiber = acl_fiber_running(); ee->proc = epoll_callback; + ee->nready = 0; old_timeout = ev->timeout; event_epoll_set(ev, ee, timeout); ev->waiter++; while (1) { - ring_prepend(&ev->epoll_list, &ee->me); + timer_cache_add(ev->epoll_list, ee->expire, &ee->me); fiber_io_inc(); ee->fiber->status = FIBER_STATUS_EPOLL_WAIT; acl_fiber_switch(); + + if (ee->nready == 0) { + timer_cache_remove(ev->epoll_list, ee->expire, &ee->me); + } + ev->timeout = old_timeout; - ev->timeout = -1; if (acl_fiber_killed(ee->fiber)) { - ring_detach(&ee->me); + acl_fiber_set_error(ee->fiber->errnum); + if (ee->nready == 0) { + ee->nready = -1; + } + msg_info("%s(%d), %s: fiber-%u was killed", __FILE__, __LINE__, __FUNCTION__, acl_fiber_id(ee->fiber)); break; } + + if (timer_cache_size(ev->epoll_list) == 0) { + ev->timeout = -1; + } + if (ee->nready != 0 || timeout == 0) { break; } now = event_get_stamp(ev); if (ee->expire > 0 && now >= ee->expire) { + acl_fiber_set_error(FIBER_ETIMEDOUT); break; } } diff --git a/lib_fiber/c/src/hook/poll.c b/lib_fiber/c/src/hook/poll.c index 2583db7cb..9d5db949c 100644 --- a/lib_fiber/c/src/hook/poll.c +++ b/lib_fiber/c/src/hook/poll.c @@ -7,6 +7,12 @@ #ifdef HAS_POLL +struct POLLFD { + FILE_EVENT *fe; + POLL_EVENT *pe; + struct pollfd *pfd; +}; + /****************************************************************************/ #define TO_APPL ring_to_appl @@ -49,8 +55,6 @@ static void read_callback(EVENT *ev, FILE_EVENT *fe) pfd->fe = NULL; } - assert(timer_cache_size(ev->poll_list) > 0); - /* * If any fe has been ready, the pe holding fe should be removed from * ev->poll_list to avoid to be called in timeout process. @@ -63,6 +67,7 @@ static void read_callback(EVENT *ev, FILE_EVENT *fe) timer_cache_remove(ev->poll_list, pfd->pe->expire, &pfd->pe->me); ring_prepend(&ev->poll_ready, &pfd->pe->me); } + pfd->pe->nready++; SET_READABLE(fe); } @@ -99,12 +104,11 @@ static void write_callback(EVENT *ev, FILE_EVENT *fe) pfd->fe = NULL; } - assert(timer_cache_size(ev->poll_list) > 0); - if (pfd->pe->nready == 0) { timer_cache_remove(ev->poll_list, pfd->pe->expire, &pfd->pe->me); ring_prepend(&ev->poll_ready, &pfd->pe->me); } + pfd->pe->nready++; SET_WRITABLE(fe); } @@ -201,6 +205,7 @@ static POLLFD *pollfd_alloc(POLL_EVENT *pe, struct pollfd *fds, nfds_t nfds) #endif pfds[i].pe = pe; pfds[i].pfd = &fds[i]; + pfds[i].pfd->revents = 0; SET_POLLING(pfds[i].fe); } @@ -212,14 +217,74 @@ static void pollfd_free(POLLFD *pfds) mem_free(pfds); } +#ifdef SHARE_STACK + +typedef struct pollfds { + struct pollfd *fds; + nfds_t nfds; + size_t size; +} pollfds; + +static void fiber_on_exit(void *ctx) +{ + pollfds *pfds = (pollfds *) ctx; + + mem_free(pfds->fds); + mem_free(pfds); +} + +static __thread int __local_key; + +static pollfds *pollfds_save(const struct pollfd *fds, nfds_t nfds) +{ + pollfds *pfds = (pollfds *) acl_fiber_get_specific(__local_key); + + if (pfds == NULL) { + pfds = (pollfds *) mem_malloc(sizeof(pollfds)); + pfds->size = nfds + 1; + pfds->fds = mem_malloc(sizeof(struct pollfds) * pfds->size); + acl_fiber_set_specific(&__local_key, pfds, fiber_on_exit); + } else if (pfds->size < (size_t) nfds) { + mem_free(pfds->fds); + pfds->size = nfds + 1; + pfds->fds = mem_malloc(sizeof(struct pollfd) * pfds->size); + } else { + pfds->nfds = nfds; + } + + pfds->nfds = nfds; + memcpy(pfds->fds, fds, sizeof(struct pollfd) * nfds); + return pfds; +} + +static void pollfds_copy(const pollfds *pfds, struct pollfd *fds) +{ + memcpy(fds, pfds->fds, sizeof(struct pollfd) * pfds->nfds); +} + +#endif // SHARE_STACK + #define MAX_TIMEOUT 200000000 int WINAPI acl_fiber_poll(struct pollfd *fds, nfds_t nfds, int timeout) { long long now; - POLL_EVENT pe; EVENT *ev; - int old_timeout; + int old_timeout, nready; + ACL_FIBER *curr; + +#ifdef SHARE_STACK + // In shared stack mode, we should use heap memory for pe to hold + // all the fds, because the pe will be operated by the event fiber, + // but the shared stack can be used by only one fiber, the stack + // memory of the current fiber will be occupied by the other fiber + // after switching the other fiber. So, we use heap memory to hold + // pe to avoid stack memory collision. + pollfds *pfds; + POLL_EVENT pevent, *pe; +#else + POLL_EVENT pevent, *pe; +#endif if (sys_poll == NULL) { hook_once(); @@ -229,65 +294,93 @@ int WINAPI acl_fiber_poll(struct pollfd *fds, nfds_t nfds, int timeout) return sys_poll ? (*sys_poll)(fds, nfds, timeout) : -1; } + curr = acl_fiber_running(); + if (timeout < 0) { timeout = MAX_TIMEOUT; } - ev = fiber_io_event(); - pe.fds = pollfd_alloc(&pe, fds, nfds); - pe.nfds = nfds; - pe.fiber = acl_fiber_running(); - pe.proc = poll_callback; - pe.nready = 0; - + ev = fiber_io_event(); old_timeout = ev->timeout; - poll_event_set(ev, &pe, timeout); + +#ifdef SHARE_STACK + if (curr->oflag & ACL_FIBER_ATTR_SHARE_STACK) { + pfds = pollfds_save(fds, nfds); + pe = (POLL_EVENT *) mem_malloc(sizeof(POLL_EVENT)); + pe->fds = pollfd_alloc(pe, pfds->fds, nfds); + } else { + pfds = NULL; + pe = &pevent; + pe->fds = pollfd_alloc(pe, fds, nfds); + } +#else + pe = &pevent; + pe->fds = pollfd_alloc(pe, fds, nfds); +#endif + + pe->nfds = nfds; + pe->fiber = curr; + pe->proc = poll_callback; + + poll_event_set(ev, pe, timeout); ev->waiter++; while (1) { - timer_cache_add(ev->poll_list, pe.expire, &pe.me); - pe.nready = 0; + timer_cache_add(ev->poll_list, pe->expire, &pe->me); + pe->nready = 0; fiber_io_inc(); - pe.fiber->status = FIBER_STATUS_POLL_WAIT; + pe->fiber->status = FIBER_STATUS_POLL_WAIT; acl_fiber_switch(); - if (pe.nready == 0) { - timer_cache_remove(ev->poll_list, pe.expire, &pe.me); + if (pe->nready == 0) { + timer_cache_remove(ev->poll_list, pe->expire, &pe->me); } ev->timeout = old_timeout; - if (acl_fiber_killed(pe.fiber)) { - acl_fiber_set_error(pe.fiber->errnum); - pe.nready = -1; + if (acl_fiber_killed(pe->fiber)) { + acl_fiber_set_error(pe->fiber->errnum); + if (pe->nready == 0) { + pe->nready = -1; + } msg_info("%s(%d), %s: fiber-%u was killed, %s, timeout=%d", __FILE__, __LINE__, __FUNCTION__, - acl_fiber_id(pe.fiber), last_serror(), timeout); + acl_fiber_id(pe->fiber), last_serror(), timeout); break; } if (timer_cache_size(ev->poll_list) == 0) { ev->timeout = -1; } - if (pe.nready != 0 || timeout == 0) { + + if (pe->nready != 0 || timeout == 0) { break; } now = event_get_stamp(ev); - if (pe.expire > 0 && now >= pe.expire) { + if (pe->expire > 0 && now >= pe->expire) { acl_fiber_set_error(FIBER_ETIMEDOUT); break; } } - poll_event_clean(ev, &pe); - pollfd_free(pe.fds); + poll_event_clean(ev, pe); + pollfd_free(pe->fds); ev->waiter--; - return pe.nready; + nready = pe->nready; + +#ifdef SHARE_STACK + if (curr->oflag & ACL_FIBER_ATTR_SHARE_STACK) { + mem_free(pe); + pollfds_copy(pfds, fds); + } +#endif + + return nready; } #ifdef SYS_UNIX diff --git a/lib_fiber/cpp/include/fiber/fiber.hpp b/lib_fiber/cpp/include/fiber/fiber.hpp index d30a1cee2..8ab38d2af 100644 --- a/lib_fiber/cpp/include/fiber/fiber.hpp +++ b/lib_fiber/cpp/include/fiber/fiber.hpp @@ -35,8 +35,10 @@ public: * Э̣Ȼص run ص running Ϊ true ʱ * ֹ start * @param stack_size {size_t} Э̶ջС + * @param Ƿùջʽ(Ҫùջʽڱ libfiber.a + * ʱ뿪 SHARE_STACK ) */ - void start(size_t stack_size = 320000); + void start(size_t stack_size = 320000, bool share_stack = false); /** * ڱЭʱô˺֪ͨЭ˳ @@ -194,6 +196,18 @@ public: */ static void set_non_blocking(bool yes); + /** + * ùջģʽùջĴС,ڲȱʡֵΪ 1024000 ֽ + * @param size {size_t} ջڴС + */ + static void set_shared_stack_size(size_t size); + + /** + * ùջģʽ»ùջС + * @return {size_t} 0 ʾδùջʽ + */ + static size_t get_shared_stack_size(void); + /** * ʽñʹ acl IO Э̻ UNIX ƽ̨²ʽ * ñΪڲԶ HOOK IO API @@ -238,6 +252,7 @@ public: */ static void fiber_create(void (*fn)(ACL_FIBER*, void*), void* ctx, size_t size); + protected: /** * 麯ʵֱͨ start Э̺󣬱 diff --git a/lib_fiber/cpp/src/fiber.cpp b/lib_fiber/cpp/src/fiber.cpp index a3c5ba865..4bb0ab7aa 100644 --- a/lib_fiber/cpp/src/fiber.cpp +++ b/lib_fiber/cpp/src/fiber.cpp @@ -102,6 +102,16 @@ void fiber::set_non_blocking(bool yes) acl_fiber_set_non_blocking(yes ? 1 : 0); } +void fiber::set_shared_stack_size(size_t size) +{ + acl_fiber_set_shared_stack_size(size); +} + +size_t fiber::get_shared_stack_size(void) +{ + return acl_fiber_get_shared_stack_size(); +} + ACL_FIBER *fiber::get_fiber(void) const { return f_; @@ -150,13 +160,18 @@ void fiber::run(void) __FILE__, __LINE__, __FUNCTION__); } -void fiber::start(size_t stack_size /* = 64000 */) +void fiber::start(size_t stack_size /* 64000 */, bool share_stack /* false */) { if (f_ != NULL) { acl_msg_fatal("%s(%d), %s: fiber-%u, already running!", __FILE__, __LINE__, __FUNCTION__, self()); } - acl_fiber_create(fiber_callback, this, stack_size); + ACL_FIBER_ATTR attr; + acl_fiber_attr_init(&attr); + acl_fiber_attr_setstacksize(&attr, stack_size); + acl_fiber_attr_setsharestack(&attr, share_stack ? 1 : 0); + + acl_fiber_create2(&attr, fiber_callback, this); } void fiber::fiber_callback(ACL_FIBER *f, void *ctx) diff --git a/lib_fiber/cpp/src/fiber_server.cpp b/lib_fiber/cpp/src/fiber_server.cpp index a6ce64492..31a0413d8 100644 --- a/lib_fiber/cpp/src/fiber_server.cpp +++ b/lib_fiber/cpp/src/fiber_server.cpp @@ -67,8 +67,10 @@ static ACL_CONFIG_STR_TABLE __conf_str_tab[] = { }; static int acl_var_fiber_quick_abort; +static int acl_var_fiber_share_stack; static ACL_CONFIG_BOOL_TABLE __conf_bool_tab[] = { { "fiber_quick_abort", 1, &acl_var_fiber_quick_abort }, + { "fiber_share_stack", 0, &acl_var_fiber_share_stack }, { 0, 0, 0 }, }; @@ -141,6 +143,11 @@ static void thread_fiber_accept(ACL_FIBER *fiber, void *ctx) static socket_t __max_fd = 0, __last_fd = 0; ACL_VSTREAM *sstream = (ACL_VSTREAM *) ctx, *cstream; char ip[64]; + ACL_FIBER_ATTR attr; + + acl_fiber_attr_init(&attr); + acl_fiber_attr_setstacksize(&attr, acl_var_fiber_stack_size); + acl_fiber_attr_setsharestack(&attr, acl_var_fiber_share_stack ? 1 : 0); while (1) { cstream = acl_vstream_accept(sstream, ip, sizeof(ip)); @@ -150,8 +157,7 @@ static void thread_fiber_accept(ACL_FIBER *fiber, void *ctx) __max_fd = __last_fd; } - acl_fiber_create(fiber_client, cstream, - acl_var_fiber_stack_size); + acl_fiber_create2(&attr, fiber_client, cstream); continue; } @@ -401,6 +407,11 @@ static int main_dispatch_receive(int dispatch_fd) char buf[256], remote[256], local[256]; int fd = -1, ret; ACL_VSTREAM *cstream; + ACL_FIBER_ATTR attr; + + acl_fiber_attr_init(&attr); + acl_fiber_attr_setstacksize(&attr, acl_var_fiber_stack_size); + acl_fiber_attr_setsharestack(&attr, acl_var_fiber_share_stack ? 1 : 0); ret = acl_read_fd(dispatch_fd, buf, sizeof(buf) - 1, &fd); if (ret < 0 || fd < 0) { @@ -422,7 +433,7 @@ static int main_dispatch_receive(int dispatch_fd) acl_vstream_set_peer(cstream, remote); } - acl_fiber_create(fiber_client, cstream, acl_var_fiber_stack_size); + acl_fiber_create2(&attr, fiber_client, cstream); return 0; } diff --git a/lib_fiber/cpp/src/master_fiber.cpp b/lib_fiber/cpp/src/master_fiber.cpp index 0fa6c47b1..241d3f949 100644 --- a/lib_fiber/cpp/src/master_fiber.cpp +++ b/lib_fiber/cpp/src/master_fiber.cpp @@ -108,14 +108,16 @@ void master_fiber::service_on_accept(void* ctx, ACL_VSTREAM *client) master_fiber* mf = (master_fiber *) ctx; acl_assert(mf != NULL); - socket_stream stream; - if (!stream.open(client)) { + socket_stream* stream = new socket_stream; + if (!stream->open(client)) { logger_error("open stream error(%s)", acl_last_serror()); + delete stream; return; } - mf->on_accept(stream); - stream.unbind(); + mf->on_accept(*stream); + stream->unbind(); + delete stream; } void master_fiber::thread_init(void* ctx) diff --git a/lib_fiber/samples-c++1x/fiber/main.cpp b/lib_fiber/samples-c++1x/fiber/main.cpp index 77edd1873..8f045f8e6 100644 --- a/lib_fiber/samples-c++1x/fiber/main.cpp +++ b/lib_fiber/samples-c++1x/fiber/main.cpp @@ -100,6 +100,7 @@ static void usage(const char* procname) int main(int argc, char *argv[]) { int ch, n = 10; + size_t shared_size; acl::acl_cpp_init(); acl::log::stdout_open(true); @@ -118,8 +119,14 @@ int main(int argc, char *argv[]) } go fiber1; - go fiber_wait4thread; - go fiber_wait4fiber; + + shared_size = acl::fiber::get_shared_stack_size(); + if (shared_size == 0) { + // These two fibers can't running in shared stack mode. + + go fiber_wait4thread; + go fiber_wait4fiber; + } go[=] { fiber2(n, "hello world"); diff --git a/lib_fiber/samples-c++1x/fiber/valgrind.sh b/lib_fiber/samples-c++1x/fiber/valgrind.sh new file mode 100755 index 000000000..cc8944d6c --- /dev/null +++ b/lib_fiber/samples-c++1x/fiber/valgrind.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +#valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes --max-stackframe=3426305034400000 -v ./fiber -n 10 -m 20 +valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./fiber diff --git a/lib_fiber/samples-c++1x/fiber_tbox/Makefile b/lib_fiber/samples-c++1x/fiber_tbox/Makefile new file mode 100644 index 000000000..023e1a738 --- /dev/null +++ b/lib_fiber/samples-c++1x/fiber_tbox/Makefile @@ -0,0 +1,3 @@ +include ../Makefile_cpp.in +CFLAGS += -std=c++11 +PROG = fiber_tbox diff --git a/lib_fiber/samples-c++1x/fiber_tbox/main.cpp b/lib_fiber/samples-c++1x/fiber_tbox/main.cpp new file mode 100644 index 000000000..6dba33848 --- /dev/null +++ b/lib_fiber/samples-c++1x/fiber_tbox/main.cpp @@ -0,0 +1,249 @@ +#include "stdafx.h" +#include +#include +#include +#include "../stamp.h" + +static void box_wakeup(acl::fiber_tbox& box) +{ + box.push(NULL); +} + +static void box_wait(acl::fiber_tbox& box) +{ + box.pop(); +} + +static void test_in_shared_stack(size_t cocurrent, size_t n, size_t& count, + bool interactive) +{ + go[&] { + size_t total = cocurrent * n; + acl::fiber_tbox* box = new acl::fiber_tbox; + acl::fiber_tbox* box1; + if (interactive) { + box1 = new acl::fiber_tbox; + } else { + box1 = NULL; + } + + for (size_t j = 0; j < cocurrent; j++) { + go[=] { + for (size_t i = 0; i < n; i++) { + if (box1) { + box_wait(*box1); + } + + box_wakeup(*box); + } + }; + } + + for (size_t i = 0; i < total; i++) { + if (box1) { + box_wakeup(*box1); + } + + box_wait(*box); + count++; + } + + delete box; + delete box1; + printf(">>> wait over, count=%zd\r\n", count); + }; +} + +static void test_in_shared_stack_thread(size_t cocurrent, size_t n, + size_t& count, bool interactive) +{ + go[&] { + size_t total = cocurrent * n; + acl::fiber_tbox* box = new acl::fiber_tbox; + acl::fiber_tbox* box1; + + if (interactive) { + box1 = new acl::fiber_tbox; + } else { + box1 = NULL; + } + + for (size_t j = 0; j < cocurrent; j++) { + std::thread thread([=] { + for (size_t i = 0; i < n; i++) { + if (box1) { + box_wait(*box1); + } + + box_wakeup(*box); + } + }); + thread.detach(); + } + + for (size_t i = 0; i < total; i++) { + if (box1) { + box_wakeup(*box1); + } + + box_wait(*box); + count++; + } + + delete box; + delete box1; + + printf(">>> wait over, count=%zd\r\n", count); + }; +} + +static void test_in_private_stack(size_t cocurrent, size_t n, size_t& count, + bool interactive) +{ + size_t total = cocurrent * n; + + go[&] { + acl::fiber_tbox box; + acl::fiber_tbox box1; + + for (size_t j = 0; j < cocurrent; j++) { + go[&] { + for (size_t i = 0; i < n; i++) { + if (interactive) { + box_wait(box1); + } + + box_wakeup(box); + } + }; + } + + for (size_t i = 0; i < total; i++) { + if (interactive) { + box_wakeup(box1); + } + + box_wait(box); + count++; + } + + printf(">>> wait over, count=%zd\r\n", count); + }; +} + +static void test_in_private_stack_thread(size_t cocurrent, size_t n, + size_t& count, bool interactive) +{ + go[&] { + size_t total = cocurrent * n; + acl::fiber_tbox box; + acl::fiber_tbox box1; + + for (size_t j = 0; j < cocurrent; j++) { + std::thread thread([&] { + for (size_t i = 0; i < n; i++) { + if (interactive) { + box_wait(box1); + } + + box_wakeup(box); + } + }); + thread.detach(); + } + + for (size_t i = 0; i < total; i++) { + if (interactive) { + box_wakeup(box1); + } + + box_wait(box); + count++; + } + + printf(">>> wait over, count=%zd\r\n", count); + }; +} +static void usage(const char* procname) +{ + printf("usage: %s -h [help]\r\n" + " -c cocurrent\r\n" + " -n loop_count\r\n" + " -T [if use_thread]\r\n" + " -B [if interactive]\r\n" + , procname); +} + +int main(int argc, char *argv[]) +{ + int ch, n = 1, cocurrent = 1; + bool use_shared_stack = false, use_thread = false, interactive = false; + + acl::acl_cpp_init(); + acl::log::stdout_open(true); + + while ((ch = getopt(argc, argv, "hc:n:STB")) > 0) { + switch (ch) { + case 'h': + usage(argv[0]); + return 0; + case 'c': + cocurrent = atoi(optarg); + break; + case 'n': + n = atoi(optarg); + break; + case 'S': + use_shared_stack = true; + break; + case 'T': + use_thread = true; + break; + case 'B': + interactive = true; + break; + default: + break; + } + } + + size_t shared_stack_size = acl::fiber::get_shared_stack_size(); + + printf(">>> shared_stack_size: %zd\r\n", shared_stack_size); + + struct timeval begin; + gettimeofday(&begin, NULL); + + size_t count = 0; + + if (use_shared_stack > 0) { + if (use_thread) { + test_in_shared_stack_thread(cocurrent, n, count, + interactive); + } else { + test_in_shared_stack(cocurrent, n, count, interactive); + } + } else { + if (shared_stack_size > 0) { + printf(">>> This will be crashed in shared stack!\r\n"); + } + + if (use_thread) { + test_in_private_stack_thread(cocurrent, n, count, + interactive); + } else { + test_in_private_stack(cocurrent, n, count, interactive); + } + } + + acl::fiber::schedule(); + + struct timeval end; + gettimeofday(&end, NULL); + + double cost = stamp_sub(&end, &begin); + double speed = (count * 1000) / (cost > 0 ? cost : 0.1); + printf(">>> Total count=%zd, cost=%.2f ms, speed=%.2f qps\r\n", + count, cost, speed); + return 0; +} diff --git a/lib_fiber/samples-c++1x/fiber_tbox/stdafx.cpp b/lib_fiber/samples-c++1x/fiber_tbox/stdafx.cpp new file mode 100644 index 000000000..a27b824da --- /dev/null +++ b/lib_fiber/samples-c++1x/fiber_tbox/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/lib_fiber/samples-c++1x/fiber_tbox/stdafx.h b/lib_fiber/samples-c++1x/fiber_tbox/stdafx.h new file mode 100644 index 000000000..101513001 --- /dev/null +++ b/lib_fiber/samples-c++1x/fiber_tbox/stdafx.h @@ -0,0 +1,19 @@ +// stdafx.h : ׼ϵͳļİļ +// dzõĵĿضİļ +// + +#pragma once + + +// TODO: ڴ˴óҪĸͷļ + +#include "lib_acl.h" +#include "acl_cpp/lib_acl.hpp" +#include "fiber/fiber.hpp" + +#include "fiber/go_fiber.hpp" + +#ifdef WIN32 +#define snprintf _snprintf +#endif + diff --git a/lib_fiber/samples-c++1x/fiber_tbox/valgrind.sh b/lib_fiber/samples-c++1x/fiber_tbox/valgrind.sh new file mode 100755 index 000000000..5372db3d2 --- /dev/null +++ b/lib_fiber/samples-c++1x/fiber_tbox/valgrind.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +#valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes --max-stackframe=3426305034400000 -v ./fiber -n 10 -m 20 +valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./fiber_tbox -S -c 100 -n 100 diff --git a/lib_fiber/samples-c++1x/go_fiber/Makefile b/lib_fiber/samples-c++1x/go_fiber/Makefile new file mode 100644 index 000000000..251550ef2 --- /dev/null +++ b/lib_fiber/samples-c++1x/go_fiber/Makefile @@ -0,0 +1,3 @@ +include ../Makefile_cpp.in +CFLAGS += -std=c++11 +PROG = go_fiber diff --git a/lib_fiber/samples-c++1x/go_fiber/main.cpp b/lib_fiber/samples-c++1x/go_fiber/main.cpp new file mode 100644 index 000000000..f3745265e --- /dev/null +++ b/lib_fiber/samples-c++1x/go_fiber/main.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include +#include + +static void fiber1(void) +{ + printf("in fiber: %d\r\n", acl::fiber::self()); +} + +static void fiber2(int n, const char* s) +{ + printf("in fiber: %d, n: %d, s: %s\r\n", acl::fiber::self(), n, s); +} + +static void fiber3(acl::string& buf) +{ + printf("in fiber: %d, buf: %s\r\n", acl::fiber::self(), buf.c_str()); + buf = "world"; +} + +static void fiber4(const acl::string& buf) +{ + printf("in fiber: %d, buf: %s\r\n", acl::fiber::self(), buf.c_str()); +} + +////////////////////////////////////////////////////////////////////////////// + +static void usage(const char* procname) +{ + printf("usage: %s -h [help] -n fibers_count\r\n", procname); +} + +int main(int argc, char *argv[]) +{ + int ch, n = 10; + + acl::acl_cpp_init(); + acl::log::stdout_open(true); + + while ((ch = getopt(argc, argv, "hn:")) > 0) { + switch (ch) { + case 'h': + usage(argv[0]); + return 0; + case 'n': + n = atoi(optarg); + break; + default: + break; + } + } + + go fiber1; + + go[=] { + fiber2(n, "hello world"); + }; + + acl::string buf("hello"); + + go[&] { + fiber3(buf); + }; + + go[&] { + fiber4(buf); + }; + + go[=] { + fiber4(buf); + }; + + acl::fiber::schedule(); + return 0; +} diff --git a/lib_fiber/samples-c++1x/go_fiber/stdafx.cpp b/lib_fiber/samples-c++1x/go_fiber/stdafx.cpp new file mode 100644 index 000000000..a27b824da --- /dev/null +++ b/lib_fiber/samples-c++1x/go_fiber/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/lib_fiber/samples-c++1x/go_fiber/stdafx.h b/lib_fiber/samples-c++1x/go_fiber/stdafx.h new file mode 100644 index 000000000..101513001 --- /dev/null +++ b/lib_fiber/samples-c++1x/go_fiber/stdafx.h @@ -0,0 +1,19 @@ +// stdafx.h : ׼ϵͳļİļ +// dzõĵĿضİļ +// + +#pragma once + + +// TODO: ڴ˴óҪĸͷļ + +#include "lib_acl.h" +#include "acl_cpp/lib_acl.hpp" +#include "fiber/fiber.hpp" + +#include "fiber/go_fiber.hpp" + +#ifdef WIN32 +#define snprintf _snprintf +#endif + diff --git a/lib_fiber/samples-c++1x/go_fiber/valgrind.sh b/lib_fiber/samples-c++1x/go_fiber/valgrind.sh new file mode 100755 index 000000000..c4189c1fa --- /dev/null +++ b/lib_fiber/samples-c++1x/go_fiber/valgrind.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +#valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes --max-stackframe=3426305034400000 -v ./fiber -n 10 -m 20 +valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./go_fiber diff --git a/lib_fiber/samples-c++1x/stamp.h b/lib_fiber/samples-c++1x/stamp.h new file mode 100644 index 000000000..a35632eda --- /dev/null +++ b/lib_fiber/samples-c++1x/stamp.h @@ -0,0 +1,28 @@ +#ifndef __STAMP_INCLUDE_H__ +#define __STAMP_INCLUDE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +static double stamp_sub(const struct timeval *from, const struct timeval *sub) +{ + struct timeval res; + + memcpy(&res, from, sizeof(struct timeval)); + + res.tv_usec -= sub->tv_usec; + if (res.tv_usec < 0) { + --res.tv_sec; + res.tv_usec += 1000000; + } + res.tv_sec -= sub->tv_sec; + + return (res.tv_sec * 1000.0 + res.tv_usec/1000.0); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib_fiber/samples/fiber/main.c b/lib_fiber/samples/fiber/main.c index c0944d312..c61cab8f2 100644 --- a/lib_fiber/samples/fiber/main.c +++ b/lib_fiber/samples/fiber/main.c @@ -11,14 +11,15 @@ static int __max_loop = 1000; static int __max_fiber = 1000; static int __display = 0; -static int __stack_size = 64000; static __thread struct timeval __begin; -static __thread int __left_fiber = 1000; +static __thread long long __count = 0; + +#define DUMMY_SIZE 512000 static void stack_dummy(ACL_FIBER *fiber acl_unused) { - char buf[81920]; + char buf[DUMMY_SIZE]; memset(buf, 0, sizeof(buf)); } @@ -26,51 +27,63 @@ static void stack_dummy(ACL_FIBER *fiber acl_unused) static void fiber_main(ACL_FIBER *fiber, void *ctx acl_unused) { int i; + size_t shared_stack_size = acl_fiber_get_shared_stack_size(); - if (0) + printf("\r\nshared_stack_size=%zd\r\n\r\n", shared_stack_size); + + if (acl_fiber_use_share_stack(fiber) && shared_stack_size > DUMMY_SIZE) { stack_dummy(fiber); + } errno = acl_fiber_errno(fiber); + for (i = 0; i < __max_loop; i++) { + if (__count < 10) { + printf("fiber-%d, run, begin to yield\r\n", + acl_fiber_id(fiber)); + } + acl_fiber_yield(); - if (!__display) - continue; - if (i <= 2) - printf("fiber-%d, errno: %d\r\n", + if (__count++ < 10) { + printf("fiber-%d, wakeup errno: %d\r\n", acl_fiber_id(fiber), errno); + printf("---------------------------------------\r\n"); + } } - if (--__left_fiber == 0) { - long long count = __max_fiber * __max_loop; - struct timeval end; - double spent; - - gettimeofday(&end, NULL); - spent = stamp_sub(&end, &__begin); - printf("fibers: %d, count: %lld, spent: %.2f, speed: %.2f\r\n", - __max_fiber, count, spent, - (count * 1000) / (spent > 0 ? spent : 1)); - } + printf("%s: fiber-%d exiting, count=%lld ...\r\n", + __FUNCTION__, acl_fiber_id(fiber), __count); } -static void *thread_main(void *ctx acl_unused) +static void *thread_main(void *ctx) { + ACL_FIBER_ATTR *attr = (ACL_FIBER_ATTR *) ctx; int i; - gettimeofday(&__begin, NULL); - __left_fiber = __max_fiber; - - for (i = 0; i < __max_fiber; i++) - acl_fiber_create(fiber_main, NULL, __stack_size); + for (i = 0; i < __max_fiber; i++) { + acl_fiber_create2(attr, fiber_main, NULL); + } acl_fiber_schedule(); printf("thread: %lu\r\n", (unsigned long) acl_pthread_self()); - return NULL; } +static void calc_speed(void) +{ + long long count = __max_fiber * __max_loop; + struct timeval end; + double spent, speed; + + gettimeofday(&end, NULL); + spent = stamp_sub(&end, &__begin); + speed = (count * 1000) / (spent > 0 ? spent : 1); + printf("\r\nfibers: %d, count: %lld, spent: %.2f, speed: %.2f\r\n", + __max_fiber, count, spent, speed); +} + static void usage(const char *procname) { printf("usage: %s -h [help]\r\n" @@ -78,7 +91,9 @@ static void usage(const char *procname) " -c max_fiber\r\n" " -t max_threads\r\n" " -d stack_size\r\n" - " -e [if display]\r\n", procname); + " -e [if display]\r\n" + " -S [if use shared stack]\r\n" + , procname); } int main(int argc, char *argv[]) @@ -86,8 +101,12 @@ int main(int argc, char *argv[]) int ch, i, nthreads = 1; acl_pthread_attr_t attr; acl_pthread_t *tids; + ACL_FIBER_ATTR fiber_attr; + size_t stack_size = 64000; - while ((ch = getopt(argc, argv, "hn:c:t:d:e")) > 0) { + acl_fiber_attr_init(&fiber_attr); + + while ((ch = getopt(argc, argv, "hn:c:t:ed:S")) > 0) { switch (ch) { case 'h': usage(argv[0]); @@ -104,7 +123,11 @@ int main(int argc, char *argv[]) nthreads = 1; break; case 'd': - __stack_size = atoi(optarg); + stack_size = (size_t) atoi(optarg); + acl_fiber_attr_setstacksize(&fiber_attr, stack_size); + break; + case 'S': + acl_fiber_attr_setsharestack(&fiber_attr, 1); break; case 'e': __display = 1; @@ -114,14 +137,23 @@ int main(int argc, char *argv[]) } } + acl_fiber_set_shared_stack_size(8000000); + acl_pthread_attr_init(&attr); tids = (acl_pthread_t *) acl_mycalloc(nthreads, sizeof(acl_pthread_t)); - for (i = 0; i < nthreads; i++) - acl_pthread_create(&tids[i], &attr, thread_main, NULL); + gettimeofday(&__begin, NULL); - for (i = 0; i < nthreads; i++) + for (i = 0; i < nthreads; i++) { + acl_pthread_create(&tids[i], &attr, thread_main, &fiber_attr); + } + + for (i = 0; i < nthreads; i++) { acl_pthread_join(tids[i], NULL); + } + + calc_speed(); + printf("All fiber threads exited now ...\r\n"); acl_myfree(tids); diff --git a/lib_fiber/samples/fiber/valgrind.sh b/lib_fiber/samples/fiber/valgrind.sh index 696368204..bb972305d 100755 --- a/lib_fiber/samples/fiber/valgrind.sh +++ b/lib_fiber/samples/fiber/valgrind.sh @@ -1,4 +1,4 @@ #!/bin/sh #valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes --max-stackframe=3426305034400000 -v ./fiber -n 10 -m 20 -valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./fiber -n 1 -m 2 +valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./fiber -c 100 -n 100 -S diff --git a/lib_fiber/samples/httpd/main.cpp b/lib_fiber/samples/httpd/main.cpp index eac028fd5..e55b2ae48 100644 --- a/lib_fiber/samples/httpd/main.cpp +++ b/lib_fiber/samples/httpd/main.cpp @@ -3,7 +3,7 @@ #include #include "http_servlet.h" -#define STACK_SIZE 320000 +static int STACK_SIZE = 128000; static int __rw_timeout = 0; static int __schedule_event = FIBER_EVENT_KERNEL; @@ -11,31 +11,31 @@ static void http_server(ACL_FIBER *, void *ctx) { acl::socket_stream *conn = (acl::socket_stream *) ctx; + acl::memcache_session *session = new acl::memcache_session("127.0.0.1:11211"); + http_servlet *servlet = new http_servlet(conn, session); + servlet->setLocalCharset("gb2312"); + //printf("start one http_server\r\n"); - acl::memcache_session session("127.0.0.1:11211"); - http_servlet servlet(conn, &session); - servlet.setLocalCharset("gb2312"); - - while (true) - { - if (servlet.doRun() == false) + while (true) { + if (servlet->doRun() == false) { break; + } } printf("close one connection: %d, %s\r\n", conn->sock_handle(), acl::last_serror()); + delete session; + delete servlet; delete conn; } static void fiber_accept(ACL_FIBER *, void *ctx) { acl::server_socket* server = (acl::server_socket *) ctx; - while (true) - { + while (true) { acl::socket_stream* client = server->accept(); - if (client == NULL) - { + if (client == NULL) { printf("accept failed: %s\r\n", acl::last_serror()); break; } @@ -59,11 +59,10 @@ public: thread_server(const char* addr) { - server_inner_ = new acl::server_socket(acl::OPEN_FLAG_REUSEPORT); - if (server_inner_->open(addr) == false) - { - printf("open %s error %s\r\n", - addr, acl::last_serror()); + server_inner_ = new acl::server_socket(acl::OPEN_FLAG_REUSEPORT, 128); + if (server_inner_->open(addr) == false) { + printf("%s(%d): open %s error %s\r\n", __FUNCTION__, + __LINE__, addr, acl::last_serror()); exit (1); } @@ -91,6 +90,7 @@ static void usage(const char* procname) " -e event\r\n" " -R reuse_port\r\n" " -t threads\r\n" + " -z stack_size[default: 128000]\r\n" " -r rw_timeout\r\n", procname); } @@ -100,10 +100,8 @@ int main(int argc, char *argv[]) int ch, nthreads = 2; bool reuse_port = false; - while ((ch = getopt(argc, argv, "hs:r:t:Re:")) > 0) - { - switch (ch) - { + while ((ch = getopt(argc, argv, "hs:r:t:Re:z:")) > 0) { + switch (ch) { case 'h': usage(argv[0]); return 0; @@ -120,12 +118,17 @@ int main(int argc, char *argv[]) reuse_port = true; break; case 'e': - if (strcasecmp(optarg, "kernel") == 0) + if (strcasecmp(optarg, "kernel") == 0) { __schedule_event = FIBER_EVENT_KERNEL; - else if (strcasecmp(optarg, "poll") == 0) - __schedule_event = FIBER_EVENT_SELECT; - else if (strcasecmp(optarg, "select") == 0) + } else if (strcasecmp(optarg, "poll") == 0) { __schedule_event = FIBER_EVENT_POLL; + } else if (strcasecmp(optarg, "select") == 0) { + __schedule_event = FIBER_EVENT_SELECT; + } + break; + case 'z': + STACK_SIZE = atoi(optarg); + break; default: break; } @@ -136,26 +139,25 @@ int main(int argc, char *argv[]) acl::server_socket server; - if (!reuse_port) - { - if (server.open(addr) == false) - { - printf("open %s error\r\n", addr.c_str()); + if (!reuse_port) { + if (server.open(addr) == false) { + printf("%s(%d): open %s error %s\r\n", __FUNCTION__, + __LINE__, addr.c_str(), acl::last_serror()); exit (1); - } - else + } else { printf("open %s ok\r\n", addr.c_str()); + } } std::vector threads; - for (int i = 0; i < nthreads; i++) - { + for (int i = 0; i < nthreads; i++) { acl::thread* thr; - if (reuse_port) + if (reuse_port) { thr = new thread_server(addr); - else + } else { thr = new thread_server(server); + } threads.push_back(thr); thr->set_detachable(false); thr->start(); @@ -163,8 +165,7 @@ int main(int argc, char *argv[]) } for (std::vector::iterator it = threads.begin(); - it != threads.end(); ++it) - { + it != threads.end(); ++it) { (*it)->wait(); delete *it; } diff --git a/lib_fiber/samples/httpd3/main.cpp b/lib_fiber/samples/httpd3/main.cpp index d8fe7829a..09352b50d 100644 --- a/lib_fiber/samples/httpd3/main.cpp +++ b/lib_fiber/samples/httpd3/main.cpp @@ -9,26 +9,46 @@ #define STACK_SIZE 320000 static void client_callback(int type, ACL_EVENT *event, - ACL_VSTREAM *cstream, void *ctx); + ACL_VSTREAM *conn, void *ctx); -static int __rw_timeout = 0; -static int __stop = 0; -static int __real_http = 0; -static int __use_kernel = 0; +static int __rw_timeout = 0; +static int __stop = 0; +static int __real_http = 0; +static int __use_kernel = 0; +static int __accept_alone = 0; -static int http_demo(ACL_VSTREAM *cstream, const char* res, size_t len) +static int http_demo(ACL_VSTREAM *conn, const char* res, size_t len) { char buf[8192]; int ret; - cstream->rw_timeout = __rw_timeout; + conn->rw_timeout = __rw_timeout; while (1) { - ret = acl_vstream_gets_nonl(cstream, buf, sizeof(buf) - 1); + ret = acl_vstream_gets_nonl(conn, buf, sizeof(buf) -1); if (ret == ACL_VSTREAM_EOF) { printf("gets error: %s\r\n", acl_last_serror()); return -1; } + if (ret > 0) { + buf[ret] = 0; + if (strcasecmp(buf, "stop") == 0) { + __stop = 1; + } + break; + } + } + + while (1) { + ret = acl_vstream_gets_nonl(conn, buf, sizeof(buf) - 1); + if (ret == ACL_VSTREAM_EOF) { + printf("gets error: %s\r\n", acl_last_serror()); + return -1; + } + + if (ret == 0) { + break; + } buf[ret] = 0; if (strcasecmp(buf, "stop") == 0) { @@ -36,12 +56,9 @@ static int http_demo(ACL_VSTREAM *cstream, const char* res, size_t len) printf("----stop now----\r\n"); break; } - - if (ret == 0) - break; } - if (acl_vstream_writen(cstream, res, len) == ACL_VSTREAM_EOF) { + if (acl_vstream_writen(conn, res, len) == ACL_VSTREAM_EOF) { printf("write error\r\n"); return -1; } @@ -51,8 +68,8 @@ static int http_demo(ACL_VSTREAM *cstream, const char* res, size_t len) static void echo_client(ACL_FIBER *fiber acl_unused, void *ctx) { - ACL_VSTREAM *cstream = (ACL_VSTREAM *) ctx; - ACL_EVENT *event = (ACL_EVENT *) cstream->context; + ACL_VSTREAM *conn = (ACL_VSTREAM *) ctx; + ACL_EVENT *event = (ACL_EVENT *) conn->context; const char* res = "HTTP/1.1 200 OK\r\n" "Date: Tue, 31 May 2016 14:20:28 GMT\r\n" "Server: acl\r\n" @@ -63,23 +80,23 @@ static void echo_client(ACL_FIBER *fiber acl_unused, void *ctx) "hello world!"; size_t len = strlen(res); - if (http_demo(cstream, res, len) < 0 || __stop) { - acl_vstream_close(cstream); + if (http_demo(conn, res, len) < 0 || __stop) { + acl_vstream_close(conn); return; } //printf("%s: %d: call acl_event_enable_read again, fd: %d\r\n", - // __FUNCTION__, __LINE__, ACL_VSTREAM_SOCK(cstream)); - acl_event_enable_read(event, cstream, 120, client_callback, NULL); + // __FUNCTION__, __LINE__, ACL_VSTREAM_SOCK(conn)); + acl_event_enable_read(event, conn, 120, client_callback, NULL); } static void http_server(ACL_FIBER *, void *ctx) { - ACL_VSTREAM *cstream = (ACL_VSTREAM *) ctx; - ACL_EVENT *event = (ACL_EVENT *) cstream->context; + ACL_VSTREAM *client = (ACL_VSTREAM *) ctx; + ACL_EVENT *event = (ACL_EVENT *) client->context; acl::socket_stream conn; - conn.open(cstream); + conn.open(client); acl::memcache_session session("127.0.0.1:11211"); http_servlet servlet(&conn, &session); servlet.setLocalCharset("gb2312"); @@ -89,35 +106,56 @@ static void http_server(ACL_FIBER *, void *ctx) } conn.unbind(); - acl_event_enable_read(event, cstream, 120, client_callback, NULL); + acl_event_enable_read(event, client, 120, client_callback, NULL); } static void client_callback(int type acl_unused, ACL_EVENT *event, - ACL_VSTREAM *cstream, void *ctx acl_unused) + ACL_VSTREAM *conn, void *ctx acl_unused) { - cstream->context = event; - acl_event_disable_readwrite(event, cstream); + conn->context = event; + acl_event_disable_readwrite(event, conn); - if (__real_http) - acl_fiber_create(http_server, cstream, 320000); - else - acl_fiber_create(echo_client, cstream, 160000); + if (__real_http) { + acl_fiber_create(http_server, conn, 128000); + } else { + acl_fiber_create(echo_client, conn, 64000); + } +} + +static void fiber_accept(ACL_FIBER *fiber acl_unused, void *ctx) +{ + ACL_VSTREAM *sstream = (ACL_VSTREAM *) ctx; + ACL_EVENT *event = (ACL_EVENT *) sstream->context; + + while (true) { + char ip[64]; + ACL_VSTREAM *conn = acl_vstream_accept(sstream, ip, sizeof(ip)); + if (conn == NULL) { + printf("accept error %s\r\n", acl::last_serror()); + break; + } + + printf(">>>%s: accept one, fd: %d\r\n", + __FUNCTION__, ACL_VSTREAM_SOCK(conn)); + acl_event_enable_read(event, conn, 120, client_callback, NULL); + } + + acl_fiber_schedule_stop(); } static void listen_callback(int type acl_unused, ACL_EVENT *event, ACL_VSTREAM *sstream, void *ctx acl_unused) { char ip[64]; - ACL_VSTREAM *cstream = acl_vstream_accept(sstream, ip, sizeof(ip)); + ACL_VSTREAM *conn = acl_vstream_accept(sstream, ip, sizeof(ip)); - if (cstream == NULL) { + if (conn== NULL) { printf("accept error %s\r\n", acl_last_serror()); return; } - printf(">>>accept one, fd: %d\r\n", ACL_VSTREAM_SOCK(cstream)); - - acl_event_enable_read(event, cstream, 120, client_callback, NULL); + printf(">>>accept one, fd: %d\r\n", ACL_VSTREAM_SOCK(conn)); + acl_event_enable_read(event, conn, 120, client_callback, NULL); } static void fiber_event(ACL_FIBER *fiber acl_unused, void *ctx) @@ -125,13 +163,18 @@ static void fiber_event(ACL_FIBER *fiber acl_unused, void *ctx) ACL_VSTREAM *sstream = (ACL_VSTREAM *) ctx; ACL_EVENT *event; - if (__use_kernel) + if (__use_kernel) { event = acl_event_new(ACL_EVENT_KERNEL, 0, 1, 0); - else + } else { event = acl_event_new(ACL_EVENT_POLL, 0, 1, 0); + } - printf(">>>enable read fd: %d\r\n", ACL_VSTREAM_SOCK(sstream)); - acl_event_enable_listen(event, sstream, 0, listen_callback, NULL); + if (__accept_alone) { + sstream->context = event; + acl_fiber_create(fiber_accept, sstream, STACK_SIZE); + } else { + acl_event_enable_listen(event, sstream, 0, listen_callback, NULL); + } while (!__stop) { acl_event_loop(event); @@ -146,8 +189,10 @@ static void fiber_event(ACL_FIBER *fiber acl_unused, void *ctx) static void usage(const char *procname) { printf("usage: %s -h [help] -s listen_addr\r\n" + " -e fiber_event_type[kernel|poll|select]\r\n" " -r rw_timeout\r\n" " -R [use real http]\r\n" + " -A [use fiber to accept]\r\n" " -k [use kernel event]\r\n", procname); } @@ -155,12 +200,14 @@ int main(int argc, char *argv[]) { char addr[64]; ACL_VSTREAM *sstream; - int ch; + int ch, fiber_event_type = FIBER_EVENT_KERNEL; acl::log::stdout_open(true); + acl_fiber_msg_stdout_enable(1); + snprintf(addr, sizeof(addr), "%s", "127.0.0.1:9001"); - while ((ch = getopt(argc, argv, "hs:r:Rk")) > 0) { + while ((ch = getopt(argc, argv, "hs:r:RAke:")) > 0) { switch (ch) { case 'h': usage(argv[0]); @@ -174,14 +221,29 @@ int main(int argc, char *argv[]) case 'R': __real_http = 1; break; + case 'A': + __accept_alone = 1; + break; case 'k': __use_kernel = 1; break; + case 'e': + if (strcasecmp(optarg, "kernel") == 0) { + fiber_event_type = FIBER_EVENT_KERNEL; + } else if (strcasecmp(optarg, "poll") == 0) { + fiber_event_type = FIBER_EVENT_POLL; + } else if (strcasecmp(optarg, "select") == 0) { + fiber_event_type = FIBER_EVENT_SELECT; + } default: break; } } + if (fiber_event_type != FIBER_EVENT_KERNEL) { + __use_kernel = 0; + } + sstream = acl_vstream_listen(addr, 128); if (sstream == NULL) { printf("acl_vstream_listen error %s\r\n", acl_last_serror()); @@ -194,7 +256,7 @@ int main(int argc, char *argv[]) acl_fiber_create(fiber_event, sstream, STACK_SIZE); printf("call fiber_schedule\r\n"); - acl_fiber_schedule(); + acl_fiber_schedule_with(fiber_event_type); return 0; } diff --git a/lib_fiber/samples/redis_pipeline/redis_pipeline.cpp b/lib_fiber/samples/redis_pipeline/redis_pipeline.cpp index 7329bce75..ca09e55f0 100644 --- a/lib_fiber/samples/redis_pipeline/redis_pipeline.cpp +++ b/lib_fiber/samples/redis_pipeline/redis_pipeline.cpp @@ -147,10 +147,7 @@ protected: void run(void) { bool ret; - acl::redis_key cmd_key; cmd_key.set_pipeline(&conns_); - - acl::redis_string cmd_string; cmd_string.set_pipeline(&conns_); for (int i = 0; i < n_; i++) { @@ -208,6 +205,9 @@ private: acl::redis_client_pipeline& conns_; acl::string cmd_; int n_; + + acl::redis_key cmd_key; + acl::redis_string cmd_string; }; class test_thread : public acl::thread @@ -370,6 +370,8 @@ int main(int argc, char* argv[]) // stop pipeline thread pipeline.stop_thread(); + printf("The pipeline thread has stopped!\r\n"); + #ifdef WIN32 printf("enter any key to exit\r\n"); getchar(); diff --git a/lib_fiber/samples/server/main.c b/lib_fiber/samples/server/main.c index a1deee685..5b3cee4f0 100644 --- a/lib_fiber/samples/server/main.c +++ b/lib_fiber/samples/server/main.c @@ -69,12 +69,26 @@ static void fiber_accept(ACL_FIBER *fiber acl_unused, void *ctx) for (;;) { ACL_VSTREAM *cstream = acl_vstream_accept(sstream, NULL, 0); + char buf[1024]; + int ret; + if (cstream == NULL) { printf("acl_vstream_accept error %s\r\n", acl_last_serror()); break; } + ret = acl_vstream_gets(cstream, buf, sizeof(buf) - 1); + if (ret == ACL_VSTREAM_EOF) { + printf("get first line error\r\n"); + acl_vstream_close(cstream); + continue; + } else if (acl_vstream_writen(cstream, buf, ret) != ret) { + printf("write error\r\n"); + acl_vstream_close(cstream); + continue; + } + //printf("accept one, fd: %d\r\n", ACL_VSTREAM_SOCK(cstream)); acl_fiber_create(echo_client, cstream, __stack_size); //printf("continue to accept\r\n"); diff --git a/lib_fiber/samples/server2/main.c b/lib_fiber/samples/server2/main.c index 4eb913979..0228b8402 100644 --- a/lib_fiber/samples/server2/main.c +++ b/lib_fiber/samples/server2/main.c @@ -34,7 +34,6 @@ static int __listen_port = 9001; static int __listen_qlen = 64; static int __rw_timeout = 0; static int __echo_data = 1; -static int __stack_size = 320000; static int check_read(int fd, int timeout) { @@ -51,12 +50,15 @@ static int check_read(int fd, int timeout) return -1; } - if (n == 0) + if (n == 0) { return 0; - if (pfd.revents & POLLIN) + } + if (pfd.revents & POLLIN) { return 1; - else + } else { + printf(">>>poll return n=%d read no ready,fd=%d, pfd=%p\n", n, fd, &pfd); return 0; + } } static void echo_client(ACL_FIBER *fiber acl_unused, void *ctx) @@ -129,12 +131,15 @@ static void echo_client(ACL_FIBER *fiber acl_unused, void *ctx) } } -static void fiber_accept(ACL_FIBER *fiber acl_unused, void *ctx acl_unused) +static void fiber_accept(ACL_FIBER *fiber acl_unused, void *ctx) { + ACL_FIBER_ATTR *attr = (ACL_FIBER_ATTR*) ctx; SOCKET lfd; int on = 1; struct sockaddr_in sa; + assert(attr); + memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(__listen_port); @@ -181,7 +186,7 @@ static void fiber_accept(ACL_FIBER *fiber acl_unused, void *ctx acl_unused) __nconnect++; printf("accept one, fd: %u, %p\r\n", cfd, pfd); - acl_fiber_create(echo_client, pfd, __stack_size); + acl_fiber_create2(attr, echo_client, pfd); } CLOSE(lfd); @@ -214,16 +219,20 @@ static void usage(const char *procname) " -r rw_timeout\r\n" " -q listen_queue\r\n" " -z stack_size\r\n" + " -Z [if use shared stack]\r\n" " -S [if using single IO, default: no]\r\n", procname); } int main(int argc, char *argv[]) { int ch, event_mode = FIBER_EVENT_KERNEL; + ACL_FIBER_ATTR fiber_attr; + int stack_size = 320000; + acl_fiber_attr_init(&fiber_attr); snprintf(__listen_ip, sizeof(__listen_ip), "%s", "127.0.0.1"); - while ((ch = getopt(argc, argv, "hs:p:r:q:Sz:e:")) > 0) { + while ((ch = getopt(argc, argv, "hs:p:r:q:Sz:Ze:")) > 0) { switch (ch) { case 'h': usage(argv[0]); @@ -244,7 +253,11 @@ int main(int argc, char *argv[]) __echo_data = 0; break; case 'z': - __stack_size = atoi(optarg); + stack_size = atoi(optarg); + acl_fiber_attr_setstacksize(&fiber_attr, stack_size); + break; + case 'Z': + acl_fiber_attr_setsharestack(&fiber_attr, 1); break; case 'e': if (strcasecmp(optarg, "select") == 0) @@ -270,7 +283,7 @@ int main(int argc, char *argv[]) #endif printf("%s: call fiber_creater\r\n", __FUNCTION__); - acl_fiber_create(fiber_accept, NULL, 327680); + acl_fiber_create(fiber_accept, &fiber_attr, 327680); #ifndef SCHEDULE_AUTO acl_fiber_create(fiber_memcheck, NULL, 640000); diff --git a/lib_fiber/samples/server2/valgrind.sh b/lib_fiber/samples/server2/valgrind.sh index 2f5c1aab9..c714448d3 100755 --- a/lib_fiber/samples/server2/valgrind.sh +++ b/lib_fiber/samples/server2/valgrind.sh @@ -2,4 +2,4 @@ #valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes --max-stackframe=3426305034400000 -v ./fiber -n 10 -m 20 #valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./server -e poll -valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./server +valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./server -r 10 diff --git a/lib_fiber/samples/server4/main.cpp b/lib_fiber/samples/server4/main.cpp index 10cc28671..686cebb83 100644 --- a/lib_fiber/samples/server4/main.cpp +++ b/lib_fiber/samples/server4/main.cpp @@ -1,31 +1,76 @@ #include "stdafx.h" #include #include +#include + +static bool stop = false; + +static void echo_client(acl::socket_stream* conn, acl::fiber_tbox *box) +{ + printf("fiber-%d running, sock fd=%d\r\n", + acl::fiber::self(), conn->sock_handle()); + + char buf[8192]; + while (true) { + int ret = conn->read(buf, sizeof(buf), false); + if (ret == -1) { + break; + } + buf[ret] = 0; + + if (strncasecmp(buf, "stop", 4) == 0) { + stop = true; + break; + } + if (conn->write(buf, ret) == -1) { + break; + } + if (strncasecmp(buf, "bye", 3) == 0) { + break; + } + } + + box->push(NULL); +} static void fiber_client(acl::socket_stream* conn) { - printf("fiber-%d running\r\n", acl::fiber::self()); + acl::fiber_tbox* box = new acl::fiber_tbox; char buf[8192]; - while (true) - { - int ret = conn->read(buf, sizeof(buf), false); - if (ret == -1) - break; - if (conn->write(buf, ret) == -1) - break; + int ret = conn->read(buf, sizeof(buf), false); + if (ret == -1) { + printf("read from fd=%d error\r\n", conn->sock_handle()); + delete conn; + return; + } + if (conn->write(buf, ret) == -1) { + printf("write to fd=%d error\r\n", conn->sock_handle()); + delete conn; + return; } + go[&] { + echo_client(conn, box); + }; + + box->pop(); + delete box; + + printf("%s: delete fd=%d\r\n", __FUNCTION__, conn->sock_handle()); delete conn; + + if (stop) { + printf("Stop fiber schedule now\r\n"); + acl::fiber::schedule_stop(); + } } static void fiber_server(acl::server_socket& ss) { - while (true) - { + while (true) { acl::socket_stream* conn = ss.accept(); - if (conn == NULL) - { + if (conn == NULL) { printf("accept error %s\r\n", acl::last_serror()); break; } @@ -51,10 +96,8 @@ int main(int argc, char *argv[]) acl::string addr("127.0.0.1:9006"); acl::log::stdout_open(true); - while ((ch = getopt(argc, argv, "hs:")) > 0) - { - switch (ch) - { + while ((ch = getopt(argc, argv, "hs:")) > 0) { + switch (ch) { case 'h': usage(argv[0]); return 0; @@ -67,8 +110,7 @@ int main(int argc, char *argv[]) } acl::server_socket ss; - if (ss.open(addr) == false) - { + if (ss.open(addr) == false) { printf("listen %s error %s\r\n", addr.c_str(), acl::last_serror()); return 1; } diff --git a/lib_fiber/samples/server5/main.cpp b/lib_fiber/samples/server5/main.cpp index df7ca278b..e0a7446d4 100644 --- a/lib_fiber/samples/server5/main.cpp +++ b/lib_fiber/samples/server5/main.cpp @@ -1,6 +1,9 @@ #include "stdafx.h" #include #include +#include + +static int __event_type = ACL_EVENT_KERNEL; static bool echo(acl::socket_stream& conn) { @@ -71,7 +74,7 @@ static void fiber_client(acl::socket_stream* conn) printf("fiber-%d running\r\n", acl::fiber::self()); bool stop = false; - ACL_EVENT *event = acl_event_new(ACL_EVENT_POLL, 0, 1, 0); + ACL_EVENT *event = acl_event_new(__event_type, 0, 1, 0); ACL_VSTREAM *cstream = conn->get_vstream(); cstream->context = conn; conn->set_ctx(&stop); @@ -105,7 +108,7 @@ static void fiber_server(acl::server_socket& ss) static void usage(const char* procname) { - printf("usage: %s -h [help] -s listen_addr\r\n", procname); + printf("usage: %s -h [help] -s listen_addr -e event_type[kernel|poll|select]\r\n", procname); } int main(int argc, char *argv[]) @@ -116,7 +119,7 @@ int main(int argc, char *argv[]) acl::string addr("127.0.0.1:9006"); acl::log::stdout_open(true); - while ((ch = getopt(argc, argv, "hs:")) > 0) { + while ((ch = getopt(argc, argv, "hs:e:")) > 0) { switch (ch) { case 'h': usage(argv[0]); @@ -124,6 +127,14 @@ int main(int argc, char *argv[]) case 's': addr = optarg; break; + case 'e': + if (strcasecmp(optarg, "poll") == 0) { + __event_type = ACL_EVENT_POLL; + } else if (strcasecmp(optarg, "select") == 0) { + __event_type = ACL_EVENT_SELECT; + } else { + __event_type = ACL_EVENT_KERNEL; + } default: break; } diff --git a/lib_fiber/samples/server5/stdafx.h b/lib_fiber/samples/server5/stdafx.h index cee9a8583..7dfe7cf1b 100644 --- a/lib_fiber/samples/server5/stdafx.h +++ b/lib_fiber/samples/server5/stdafx.h @@ -13,6 +13,7 @@ #include "lib_acl.h" #include "acl_cpp/lib_acl.hpp" #include "fiber/lib_fiber.hpp" +#include "fiber/go_fiber.hpp" #ifdef WIN32 #define snprintf _snprintf diff --git a/lib_fiber/samples/server6/Makefile b/lib_fiber/samples/server6/Makefile new file mode 100644 index 000000000..5ab377af6 --- /dev/null +++ b/lib_fiber/samples/server6/Makefile @@ -0,0 +1,2 @@ +include ../Makefile_cpp.in +PROG = server diff --git a/lib_fiber/samples/server6/main.cpp b/lib_fiber/samples/server6/main.cpp new file mode 100644 index 000000000..c4ef00cb8 --- /dev/null +++ b/lib_fiber/samples/server6/main.cpp @@ -0,0 +1,149 @@ +#include "stdafx.h" +#include +#include + +static bool tried = false, is_sleeping = false; +static acl::socket_stream* first = NULL, *second = NULL; + +static void fiber_client(acl::socket_stream* conn) +{ + conn->set_rw_timeout(20); + int fd = conn->sock_handle(); + + printf("fiber-%d running, fiber=%p\r\n", acl::fiber::self(), acl_fiber_running()); + + char buf[8192]; + while (true) { + printf(">>>%s: fiber=%p, begin read, fd=%d, %d\n", + __FUNCTION__, acl_fiber_running(), + conn->sock_handle(), fd); + + int ret = conn->read(buf, sizeof(buf), false); + + printf(">>>%s: fiber=%p, read=%d, fd=%d, %d\n", + __FUNCTION__, acl_fiber_running(), ret, + conn->sock_handle(), fd); + + if (ret == -1) { + printf("fiber-%d, read error %s from fd %d, %d, fiber=%p\r\n", + acl::fiber::self(), acl::last_serror(), + conn->sock_handle(), fd, acl_fiber_running()); + break; + } + + if (conn->write(buf, ret) == -1) { + printf("fiber-%d, write error %s to fd %d\r\n", + acl::fiber::self(), acl::last_serror(), + conn->sock_handle()); + break; + } + + if (conn == first) { + if (second) { + printf("---close another fd=%d, me fiber=%p\n", + second->sock_handle(), acl_fiber_running()); + int sock = second->sock_handle(); + if (is_sleeping) { + second->unbind_sock(); + } + close(sock); + //second->close(); + printf("---close another fd=%d ok, fiber=%p\n", + sock, acl_fiber_running()); + second = NULL; + } else { + printf("---another fd closed\r\n"); + } + } + + //continue; + + if (conn == second) { + time_t begin = time(NULL); + printf("second fiber-%d, %p, fd=%d sleep\n", + acl::fiber::self(), acl_fiber_running(), + conn->sock_handle()); + is_sleeping = true; + sleep(15); + is_sleeping = false; + printf("second fiber-%d, %p, fd=%d wakeup, diff=%ld second\n", + acl::fiber::self(), acl_fiber_running(), + conn->sock_handle(), time(NULL) - begin); + } + } + + printf("\r\n>>fiber=%p, delete conn, fd=%d\n", + acl_fiber_running(), conn->sock_handle()); + delete conn; + printf(">>fiber=%p, delete conn ok: %s\n\n", + acl_fiber_running(), acl::last_serror()); +} + +static void fiber_server(acl::server_socket& ss) +{ + while (true) { + acl::socket_stream* conn = ss.accept(); + if (conn == NULL) { + printf("accept error %s\r\n", acl::last_serror()); + break; + } + + printf("accept ok, fd: %d\r\n", conn->sock_handle()); + + if (first == NULL) { + first = conn; + } else if (second == NULL) { + if (!tried) { + second = conn; + tried = true; + } + } + + go[=] { + fiber_client(conn); + }; + } +} + +static void usage(const char* procname) +{ + printf("usage: %s -h [help] -s listen_addr\r\n", procname); +} + +int main(int argc, char *argv[]) +{ + int ch; + + acl::acl_cpp_init(); + acl::string addr("127.0.0.1:9006"); + acl::log::stdout_open(true); + acl::fiber::stdout_open(true); + + while ((ch = getopt(argc, argv, "hs:")) > 0) { + switch (ch) { + case 'h': + usage(argv[0]); + return 0; + case 's': + addr = optarg; + break; + default: + break; + } + } + + acl::server_socket ss; + if (!ss.open(addr)) { + printf("listen %s error %s\r\n", addr.c_str(), acl::last_serror()); + return 1; + } + printf("listen %s ok\r\n", addr.c_str()); + + go[&] { + fiber_server(ss); + }; + + acl::fiber::schedule(); // start fiber schedule + + return 0; +} diff --git a/lib_fiber/samples/server6/stdafx.cpp b/lib_fiber/samples/server6/stdafx.cpp new file mode 100644 index 000000000..a27b824da --- /dev/null +++ b/lib_fiber/samples/server6/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/lib_fiber/samples/server6/stdafx.h b/lib_fiber/samples/server6/stdafx.h new file mode 100644 index 000000000..ea39dfb29 --- /dev/null +++ b/lib_fiber/samples/server6/stdafx.h @@ -0,0 +1,22 @@ +// stdafx.h : ׼ϵͳļİļ +// dzõĵĿضİļ +// + +#pragma once + + +//#include +//#include + +// TODO: ڴ˴óҪĸͷļ + +#include "lib_acl.h" +#include "acl_cpp/lib_acl.hpp" +#include "fiber/fiber.hpp" +#include "fiber/libfiber.h" +#include "fiber/go_fiber.hpp" + +#ifdef WIN32 +#define snprintf _snprintf +#endif + diff --git a/lib_fiber/samples/server6/t.sh b/lib_fiber/samples/server6/t.sh new file mode 100755 index 000000000..6e4652794 --- /dev/null +++ b/lib_fiber/samples/server6/t.sh @@ -0,0 +1 @@ +valgrind --tool=memcheck --leak-check=yes --leak-check=full --show-reachable=yes -v ./server