├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── dnsbalancer.conf.sample ├── worker.h ├── context.h ├── evloop.h ├── dns.h ├── sys.h ├── cmake-scripts ├── generic.cmake └── ds_sys.cmake ├── README.md ├── handlers.h ├── ini.h ├── utils.h ├── dns.c ├── rb.h ├── ini.c ├── types.h ├── main.c ├── evloop.c ├── pfcq.h ├── worker.c ├── utils.c ├── context.c ├── handlers.c ├── pfcq.c ├── rb.c └── COPYING /.gitignore: -------------------------------------------------------------------------------- 1 | build_*/ 2 | dnsbalancer.conf 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | email: false 3 | 4 | sudo: required 5 | dist: trusty 6 | 7 | language: c 8 | 9 | compiler: 10 | - gcc 11 | 12 | addons: 13 | apt: 14 | packages: 15 | - cmake 16 | - libldns-dev 17 | - libunwind8-dev 18 | - libini-config-dev 19 | - libatomic-ops-dev 20 | 21 | before_install: 22 | - sudo ln -s /usr/lib/pkg-config/libldns.pc /usr/share/pkgconfig/ldns.pc 23 | 24 | script: 25 | - mkdir build 26 | - cd build 27 | - cmake -DCMAKE_BUILD_TYPE=Debug .. 28 | - cmake --build . 29 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(dnsbalancer C) 2 | 3 | cmake_minimum_required(VERSION 2.8.11) 4 | 5 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake-scripts) 6 | include(ds_sys) 7 | include(generic) 8 | 9 | add_executable(dnsbalancer 10 | context.c 11 | dns.c 12 | evloop.c 13 | handlers.c 14 | ini.c 15 | main.c 16 | pfcq.c 17 | rb.c 18 | utils.c 19 | worker.c) 20 | 21 | target_link_libraries(dnsbalancer 22 | pthread 23 | rt 24 | ${LIBLDNS_LIBRARIES} 25 | ${LIBUNWIND_LIBRARIES} 26 | ${LIBINI_CONFIG_LIBRARIES} 27 | ${LIBATOMIC_OPS_LIBRARIES} 28 | ${GB_LD_EXTRA}) 29 | 30 | install(TARGETS dnsbalancer 31 | RUNTIME DESTINATION bin) 32 | 33 | -------------------------------------------------------------------------------- /dnsbalancer.conf.sample: -------------------------------------------------------------------------------- 1 | [general] 2 | wrks=-1 3 | max_pkt_size=512 4 | req_ttl=5000 5 | gc_intvl=1000 6 | wdt_intvl=1000 7 | poll_timeo=100 8 | tk_intvl=1000 9 | 10 | [stats] 11 | addr=127.0.0.1/8083 12 | 13 | [fe_1] 14 | type=fe 15 | addr=127.0.0.1/53535 16 | dscp=0x12 17 | acts=acts_1 18 | 19 | [acts_1] 20 | type=act 21 | 0=0.0.0.0/0,*,*,^.*$,balance/rr/opendns_1|opendns_2 22 | 23 | [opendns_1] 24 | type=fwd 25 | addr=2620:0:ccc::2/53 26 | dscp=0x12 27 | wdt_dscp=0x12 28 | wdt_tries=3 29 | wdt_query=. IN SOA 30 | weight=1 31 | 32 | [opendns_2] 33 | type=fwd 34 | addr=2620:0:ccd::2/53 35 | dscp=0x12 36 | wdt_dscp=0x12 37 | wdt_tries=3 38 | wdt_query=. IN SOA 39 | weight=1 40 | -------------------------------------------------------------------------------- /worker.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "types.h" 24 | 25 | void* ds_wrk(void*); 26 | 27 | -------------------------------------------------------------------------------- /context.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "types.h" 24 | 25 | struct ds_ctx* ds_ctx_load(const char*) __attribute__((warn_unused_result)); 26 | void ds_ctx_unload(struct ds_ctx*); 27 | 28 | -------------------------------------------------------------------------------- /evloop.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "types.h" 24 | 25 | int ds_wrk_loop_handler(struct epoll_event, struct ds_wrk_ctx*); 26 | void ds_loop(ds_loop_handler_fn_t, struct ds_wrk_ctx*); 27 | 28 | -------------------------------------------------------------------------------- /dns.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "types.h" 24 | 25 | bool ds_tsk_buf_to_pkt(struct ds_wrk_tsk*) __attribute__((warn_unused_result)); 26 | int ds_tsk_buf_parse(struct ds_wrk_ctx*, struct ds_wrk_tsk*, 27 | enum ds_pkt_type) __attribute__((warn_unused_result)); 28 | void ds_tsk_get_fwd(struct ds_wrk_tsk*, struct rb_table*, struct ds_act_item*); 29 | 30 | -------------------------------------------------------------------------------- /sys.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #ifdef DS_HAVE_ATOMICS 24 | #include 25 | #else /* DS_HAVE_ATOMICS */ 26 | #include 27 | #endif /* DS_HAVE_ATOMICS */ 28 | 29 | #include 30 | #ifndef DS_HAVE_UAPI_EPOLLEXCLUSIVE 31 | #define EPOLLEXCLUSIVE (1u << 28) 32 | #endif /* DS_HAVE_UAPI_EPOLLEXCLUSIVE */ 33 | 34 | #ifndef DS_HAVE_UAPI_SO_REUSEPORT 35 | #define SO_REUSEPORT 15 36 | #endif /* DS_HAVE_UAPI_SO_REUSEPORT */ 37 | 38 | -------------------------------------------------------------------------------- /cmake-scripts/generic.cmake: -------------------------------------------------------------------------------- 1 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 2 | message(STATUS "Setting build type to 'Release' as none was specified.") 3 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) 4 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 5 | endif (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 6 | 7 | message(STATUS "This is ${CMAKE_BUILD_TYPE} build") 8 | 9 | if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") 10 | set(DS_CONFIG_FLAGS "${DS_CONFIG_FLAGS} -w3 -wd2102 -wd2330 -wd11074 -wd11076") 11 | else ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") 12 | if (NOT (CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.9)) 13 | set(DS_CONFIG_FLAGS "${DS_CONFIG_FLAGS} -fdiagnostics-color=always") 14 | endif (NOT (CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.9)) 15 | endif ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") 16 | 17 | set(CMAKE_C_FLAGS 18 | "${DS_CONFIG_FLAGS} -std=c11 -D_DEFAULT_SOURCE -pipe -W -Wall -Wextra -pedantic -Wwrite-strings -Winit-self -Wcast-qual -Wpointer-arith -Wstrict-aliasing -Wformat=2 -Wmissing-declarations -Wmissing-include-dirs -Wno-unused-parameter -Wuninitialized -Wold-style-definition -Wstrict-prototypes -Wmissing-prototypes") 19 | set(CMAKE_C_FLAGS_DEBUG "${DS_CONFIG_FLAGS} -O1 -g -ggdb -pg -mtune=generic -D_FORTIFY_SOURCE=2 -fstack-protector-all -DMODE_DEBUG") 20 | set(CMAKE_C_FLAGS_RELEASE "${DS_CONFIG_FLAGS} -O3 -march=native -mtune=native -D_FORTIFY_SOURCE=1 -fstack-protector-strong -DMODE_NORMAL") 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/pfactum/dnsbalancer.svg?branch=v0.2.x)](https://travis-ci.org/pfactum/dnsbalancer) 2 | 3 | dnsbalancer 4 | =========== 5 | 6 | Description 7 | ----------- 8 | 9 | Daemon to balance UDP DNS requests over DNS servers. 10 | 11 | Principles 12 | ---------- 13 | 14 | TBD. 15 | 16 | Building 17 | -------- 18 | 19 | ### Prerequisites 20 | 21 | * `Linux kernel` v4.5+ (with `SO_REUSEPORT` and `EPOLLEXCLUSIVE` support, tested with v4.13) 22 | * `gcc` (tested with v7.2.0), `clang` (tested with v5.0.0) or `icc` (tested with v18.0.0) 23 | * `pkg-config` (tested with v0.29.2) 24 | * `cmake` (tested with v3.9.5) 25 | * `ninja` (tested with v1.8.2) or `make` (tested with v4.2.1) 26 | * `LDNS` (tested with v1.7.0) 27 | * `libunwind` (tested with v1.2.1) 28 | * `libini_config` (tested with v1.3.0) 29 | * `libtcmalloc_minimal` (optional, tested with v2.5) 30 | * `libatomic_ops` (optional, required if only compiler does not support atomics, tested with v7.4.6) 31 | 32 | ### Compiling 33 | 34 | Create `build` folder, chdir to it, then run 35 | 36 | `cmake -G Ninja ..` 37 | 38 | or 39 | 40 | `cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..` 41 | 42 | to build app with debug info. Then just type `cmake --build .`. 43 | 44 | Configuration 45 | ------------- 46 | 47 | TBD. 48 | 49 | ### ACLs 50 | 51 | TBD. 52 | 53 | Usage 54 | ----- 55 | 56 | TBD. 57 | 58 | Distribution and Contribution 59 | ----------------------------- 60 | 61 | Distributed under terms and conditions of GNU GPL v3 (only). 62 | 63 | Developers: 64 | 65 | * Oleksandr Natalenko <oleksandr@natalenko.name> 66 | -------------------------------------------------------------------------------- /handlers.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "types.h" 24 | 25 | int ds_wrk_acpt_handler(struct ds_fe_sk*, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 26 | int ds_wrk_prep_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 27 | int ds_wrk_fwd_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 28 | int ds_wrk_obt_handler(struct ds_fwd_sk*, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 29 | int ds_wrk_rep_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 30 | int ds_wrk_exit_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 31 | int ds_wrk_gc_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 32 | int ds_wrk_wdt_req_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 33 | int ds_wrk_wdt_rep_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 34 | int ds_wrk_tk_handler(int, struct ds_wrk_ctx*) __attribute__((warn_unused_result)); 35 | 36 | -------------------------------------------------------------------------------- /cmake-scripts/ds_sys.cmake: -------------------------------------------------------------------------------- 1 | include(FindPkgConfig) 2 | if (PKG_CONFIG_FOUND) 3 | pkg_check_modules(LIBLDNS ldns) 4 | if (NOT LIBLDNS_FOUND) 5 | message(WARNING "ldns is not found, checking alternative name, libldns") 6 | pkg_check_modules(LIBLDNS REQUIRED libldns) 7 | endif (NOT LIBLDNS_FOUND) 8 | pkg_check_modules(LIBUNWIND REQUIRED libunwind) 9 | pkg_check_modules(LIBINI_CONFIG REQUIRED ini_config) 10 | if(NOT CMAKE_BUILD_TYPE MATCHES Debug) 11 | pkg_check_modules(LIBTCMALLOC_MINIMAL libtcmalloc_minimal) 12 | if (LIBTCMALLOC_MINIMAL_FOUND) 13 | set(GB_LD_EXTRA "${LIBTCMALLOC_MINIMAL_LIBRARIES}") 14 | endif (LIBTCMALLOC_MINIMAL_FOUND) 15 | endif (NOT CMAKE_BUILD_TYPE MATCHES Debug) 16 | else (PKG_CONFIG_FOUND) 17 | message(FATAL_ERROR "pkg-config is missing. Unable to continue. Please install it.") 18 | endif (PKG_CONFIG_FOUND) 19 | 20 | include(CheckSymbolExists) 21 | check_symbol_exists(EPOLLEXCLUSIVE "sys/epoll.h" DS_HAVE_UAPI_EPOLLEXCLUSIVE) 22 | if (NOT DS_HAVE_UAPI_EPOLLEXCLUSIVE) 23 | check_symbol_exists(EPOLLEXCLUSIVE "linux/eventpoll.h" DS_HAVE_KAPI_EPOLLEXCLUSIVE) 24 | endif (NOT DS_HAVE_UAPI_EPOLLEXCLUSIVE) 25 | check_symbol_exists(SO_REUSEPORT "netinet/in.h" DS_HAVE_UAPI_SO_REUSEPORT) 26 | 27 | include(CheckTypeSize) 28 | set(CMAKE_EXTRA_INCLUDE_FILES stdatomic.h) 29 | check_type_size("atomic_size_t" DS_ATOMIC_SIZE_T) 30 | set(CMAKE_EXTRA_INCLUDE_FILES) 31 | 32 | set(DS_CONFIG_FLAGS "") 33 | 34 | if (DS_HAVE_UAPI_EPOLLEXCLUSIVE) 35 | message(STATUS "EPOLLEXCLUSIVE from sys/epoll.h will be used") 36 | set(DS_CONFIG_FLAGS "${DS_CONFIG_FLAGS} -DDS_HAVE_UAPI_EPOLLEXCLUSIVE") 37 | elseif (DS_HAVE_KAPI_EPOLLEXCLUSIVE) 38 | message(WARNING "EPOLLEXCLUSIVE from linux/eventpoll.h will be used") 39 | set(DS_CONFIG_FLAGS "${DS_CONFIG_FLAGS} -DDS_HAVE_KAPI_EPOLLEXCLUSIVE") 40 | else (DS_HAVE_UAPI_EPOLLEXCLUSIVE) 41 | message(WARNING "EPOLLEXCLUSIVE is not available and will be defined manually. Application may not work.") 42 | endif (DS_HAVE_UAPI_EPOLLEXCLUSIVE) 43 | 44 | if (DS_HAVE_UAPI_SO_REUSEPORT) 45 | message(STATUS "SO_REUSEPORT from netinet/in.h will be used") 46 | set(DS_CONFIG_FLAGS "${DS_CONFIG_FLAGS} -DDS_HAVE_UAPI_SO_REUSEPORT") 47 | else (DS_HAVE_UAPI_SO_REUSEPORT) 48 | message(WARNING "SO_REUSEPORT is not available and will be definet manually. Application may not work.") 49 | endif (DS_HAVE_UAPI_SO_REUSEPORT) 50 | 51 | if (HAVE_DS_ATOMIC_SIZE_T) 52 | message(STATUS "Compiler atomics are available and will be used") 53 | set(DS_CONFIG_FLAGS "${DS_CONFIG_FLAGS} -DDS_HAVE_ATOMICS") 54 | else (HAVE_DS_ATOMIC_SIZE_T) 55 | message(STATUS "Compiler atomics are not available, atomic_ops.h will be used") 56 | pkg_check_modules(LIBATOMIC_OPS REQUIRED atomic_ops) 57 | endif (HAVE_DS_ATOMIC_SIZE_T) 58 | 59 | -------------------------------------------------------------------------------- /ini.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | 25 | #define DS_CFG_PARTS_DELIM "/" 26 | #define DS_CFG_LIST_DELIM "," 27 | #define DS_CFG_SUBLIST_DELIM "|" 28 | #define DS_CFG_SECTION_GENERAL "general" 29 | #define DS_CFG_KEY_WRKS "wrks" 30 | #define DS_CFG_DEFAULT_WRKS (-1) 31 | #define DS_CFG_KEY_REQ_TTL "req_ttl" 32 | #define DS_CFG_DEFAULT_REQ_TTL 5000 33 | #define DS_CFG_KEY_GC_INTVL "gc_intvl" 34 | #define DS_CFG_DEFAULT_GC_INTVL 1000 35 | #define DS_CFG_KEY_WDT_INTVL "wdt_intvl" 36 | #define DS_CFG_DEFAULT_WDT_INTVL 1000 37 | #define DS_CFG_KEY_POLL_TIMEO "poll_timeo" 38 | #define DS_CFG_DEFAULT_POLL_TIMEO 100 39 | #define DS_CFG_KEY_TYPE "type" 40 | #define DS_CFG_TYPE_FE "fe" 41 | #define DS_CFG_TYPE_FWD "fwd" 42 | #define DS_CFG_TYPE_ACT "act" 43 | #define DS_CFG_KEY_ADDR "addr" 44 | #define DS_CFG_KEY_REG_DSCP "dscp" 45 | #define DS_CFG_KEY_WDT_DSCP "wdt_dscp" 46 | #define DS_CFG_KEY_WDT_QUERY "wdt_query" 47 | #define DS_CFG_KEY_WDT_TRIES "wdt_tries" 48 | #define DS_CFG_DEFAULT_WDT_TRIES 3 49 | #define DS_CFG_KEY_MAX_PKT_SIZE "max_pkt_size" 50 | #define DS_CFG_DEFAULT_MAX_PKT_SIZE 512 51 | #define DS_CFG_KEY_TK_INTVL "tk_intvl" 52 | #define DS_CFG_DEFAULT_TK_INTVL 1000 53 | #define DS_CFG_KEY_ACTS "acts" 54 | #define DS_CFG_ACT_BALANCE "balance" 55 | #define DS_CFG_ACT_BALANCE_RR "rr" 56 | #define DS_CFG_ACT_BALANCE_STICKY "sticky" 57 | 58 | typedef struct collection_item ds_cfg_t; 59 | 60 | ds_cfg_t* ds_cfg_open(const char*, const char*) __attribute__((warn_unused_result)); 61 | void ds_cfg_close(ds_cfg_t*); 62 | uint64_t ds_cfg_get_u64(ds_cfg_t*, const char*, const char*, uint64_t) __attribute__((warn_unused_result)); 63 | int ds_cfg_get_int(ds_cfg_t*, const char*, const char*, int) __attribute__((warn_unused_result)); 64 | unsigned ds_cfg_get_uint(ds_cfg_t*, const char*, const char*, unsigned) __attribute__((warn_unused_result)); 65 | const char* ds_cfg_get_cstr(ds_cfg_t*, const char*, const char*) __attribute__((warn_unused_result)); 66 | const char* ds_cfg_try_get_cstr(ds_cfg_t*, const char*, const char*) __attribute__((warn_unused_result)); 67 | char** ds_cfg_get_keys(ds_cfg_t*, const char*, int*) __attribute__((warn_unused_result)); 68 | void ds_cfg_free_keys(char**); 69 | char** ds_cfg_get_sections(ds_cfg_t*, int*) __attribute__((warn_unused_result)); 70 | void ds_cfg_free_sections(char**); 71 | 72 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include "rb.h" 24 | 25 | #include "types.h" 26 | 27 | extern struct libavl_allocator ds_rb_allocator; 28 | 29 | void* ds_rb_malloc(struct libavl_allocator*, size_t) __attribute__((warn_unused_result)); 30 | void ds_rb_free(struct libavl_allocator*, void*); 31 | void ds_rb_item_free(void*, void*); 32 | void ds_tsk_free(struct ds_wrk_tsk*); 33 | void ds_rb_tsk_free(void*, void*); 34 | int ds_fe_sk_cmp(const void*, const void*, void*) __attribute__((warn_unused_result)); 35 | int ds_fwd_sk_cmp(const void*, const void*, void*) __attribute__((warn_unused_result)); 36 | int ds_tsk_cmp(const void*, const void*, void*) __attribute__((warn_unused_result)); 37 | void ds_epoll_add_fd(int, int, uint32_t); 38 | void ds_epoll_del_fd(int, int); 39 | void ds_produce_u64(int); 40 | void ds_consume_u64(int); 41 | int ds_try_consume_u64(int) __attribute__((warn_unused_result)); 42 | int ds_int_cmp(int, int) __attribute__((warn_unused_result)); 43 | int ds_ptr_cmp(void*, void*) __attribute__((warn_unused_result)); 44 | int ds_epoll_wait(int, struct epoll_event*, int, int) __attribute__((warn_unused_result)); 45 | ssize_t ds_read(int, void*, size_t) __attribute__((warn_unused_result)); 46 | ssize_t ds_write(int, const void*, size_t) __attribute__((warn_unused_result)); 47 | ssize_t ds_recvfrom(int, void* restrict, size_t, struct pfcq_net_addr*) __attribute__((warn_unused_result)); 48 | ssize_t ds_sendto(int, const void*, size_t, const struct pfcq_net_addr*) __attribute__((warn_unused_result)); 49 | ssize_t ds_recv(int, void*, size_t) __attribute__((warn_unused_result)); 50 | ssize_t ds_send(int, const void*, size_t) __attribute__((warn_unused_result)); 51 | int ds_eventfd(unsigned int, int) __attribute__((warn_unused_result)); 52 | int ds_epoll_create(void) __attribute__((warn_unused_result)); 53 | void ds_close(int); 54 | int ds_af_to_pf(sa_family_t) __attribute__((warn_unused_result)); 55 | int ds_socket(int, int, int) __attribute__((warn_unused_result)); 56 | void ds_setsockopt(int, int, int, const void*, socklen_t); 57 | void ds_bind(int, const struct pfcq_net_addr*); 58 | void ds_connect(int, const struct pfcq_net_addr*); 59 | void ds_inet_pton(const char*, in_port_t, struct pfcq_net_addr*); 60 | void ds_inet_vlsmton(const struct pfcq_net_addr*, const char*, struct pfcq_net_addr*); 61 | uint64_t ds_hash_address(struct pfcq_net_addr*) __attribute__((warn_unused_result)); 62 | void ds_set_sock_dscp(int, int, int); 63 | int ds_timerfd_create(int, int) __attribute__((warn_unused_result)); 64 | void ds_timerfd_settime(int, uint64_t); 65 | void ds_regcomp(regex_t*, const char*); 66 | 67 | -------------------------------------------------------------------------------- /dns.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include "pfcq.h" 22 | #include "utils.h" 23 | 24 | #include "dns.h" 25 | 26 | bool ds_tsk_buf_to_pkt(struct ds_wrk_tsk* _tsk) 27 | { 28 | if (unlikely(ldns_wire2pkt(&_tsk->pkt, (const uint8_t*)_tsk->buf, _tsk->buf_size) != LDNS_STATUS_OK)) 29 | return false; 30 | 31 | return true; 32 | } 33 | 34 | int ds_tsk_buf_parse(struct ds_wrk_ctx* _wrk_ctx, struct ds_wrk_tsk* _tsk, enum ds_pkt_type _pkt_type) 35 | { 36 | char* owner_str = NULL; 37 | ldns_rr* rr = NULL; 38 | ldns_rdf* owner = NULL; 39 | struct ds_fwd* fwd = NULL; 40 | 41 | rr = ldns_rr_list_rr(ldns_pkt_question(_tsk->pkt), 0); 42 | _tsk->rr_type = ldns_rr_get_type(rr); 43 | _tsk->rr_class = ldns_rr_get_class(rr); 44 | owner = ldns_rr_owner(rr); 45 | ldns_dname2canonical(owner); 46 | owner_str = ldns_rdf2str(owner); 47 | strncpy(_tsk->fqdn, owner_str, HOST_NAME_MAX); 48 | free(owner_str); 49 | 50 | switch (_pkt_type) 51 | { 52 | case DS_PKT_REQ: 53 | _tsk->orig_id = htons(ldns_pkt_id(_tsk->pkt)); 54 | switch (_tsk->type) 55 | { 56 | case DS_TSK_REG: 57 | fwd = _tsk->fwd; 58 | break; 59 | case DS_TSK_WDT: 60 | fwd = _tsk->fwd_sk->fwd; 61 | break; 62 | default: 63 | panic("Unknown task type"); 64 | break; 65 | } 66 | _tsk->subst_id = htons(pfcq_counter_get_inc_mod(&fwd->c_q_id, UINT16_MAX + 1, 1)); 67 | memcpy(_tsk->buf, &_tsk->subst_id, sizeof(uint16_t)); 68 | pfcq_counter_init(&_tsk->epoch); 69 | pfcq_counter_set(&_tsk->epoch, pfcq_counter_get(&_wrk_ctx->ctx->epoch)); 70 | break; 71 | case DS_PKT_REP: 72 | _tsk->subst_id = htons(ldns_pkt_id(_tsk->pkt)); 73 | break; 74 | default: 75 | panic("Unknown packet type"); 76 | break; 77 | } 78 | 79 | return 0; 80 | } 81 | 82 | void ds_tsk_get_fwd(struct ds_wrk_tsk* _tsk, struct rb_table* _fwd_sk_set, struct ds_act_item* _act_item) 83 | { 84 | struct ds_fwd* fwd = NULL; 85 | struct rb_traverser iter; 86 | struct ds_fwd_sk* cur_fwd_sk = NULL; 87 | size_t tries = 0; 88 | size_t tip = 0; 89 | 90 | switch (_act_item->act_balance_type) 91 | { 92 | case DS_ACT_BALANCE_RR: 93 | while (likely(tries++ < _act_item->act_balance_nfwds)) 94 | { 95 | int index = pfcq_counter_get_inc_mod(&_act_item->c_fwd, 96 | _act_item->act_balance_nfwds, 0); 97 | fwd = _act_item->act_balance_fwds[index]; 98 | if (likely(fwd->alive)) 99 | break; 100 | } 101 | break; 102 | case DS_ACT_BALANCE_STICKY: 103 | tip = (size_t)(ds_hash_address(&_tsk->addr) % _act_item->act_balance_nfwds); 104 | while (likely(tries++ < _act_item->act_balance_nfwds)) 105 | { 106 | int index = tip++ % _act_item->act_balance_nfwds; 107 | fwd = _act_item->act_balance_fwds[index]; 108 | if (likely(fwd->alive)) 109 | break; 110 | } 111 | break; 112 | default: 113 | panic("Unknown forwarding mode"); 114 | break; 115 | } 116 | 117 | if (unlikely(!fwd)) 118 | return; 119 | 120 | rb_t_init(&iter, _fwd_sk_set); 121 | cur_fwd_sk = rb_t_first(&iter, _fwd_sk_set); 122 | do { 123 | if (cur_fwd_sk->fwd == fwd) 124 | { 125 | _tsk->fwd_sk = cur_fwd_sk; 126 | break; 127 | } 128 | } while (likely((cur_fwd_sk = rb_t_next(&iter)) != NULL)); 129 | 130 | if (unlikely(!_tsk->fwd_sk)) 131 | return; 132 | 133 | _tsk->fwd = fwd; 134 | _tsk->fwd_sk_addr = _tsk->fwd_sk->fwd->addr; 135 | 136 | return; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /rb.h: -------------------------------------------------------------------------------- 1 | /* Produced by texiweb from libavl.w. */ 2 | 3 | /* libavl - library for manipulation of binary trees. 4 | Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | See the GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | 02111-1307, USA. 20 | 21 | The author may be contacted at on the Internet, or 22 | write to Ben Pfaff, Stanford University, Computer Science Dept., 353 23 | Serra Mall, Stanford CA 94305, USA. 24 | */ 25 | 26 | #ifndef RB_H 27 | #define RB_H 1 28 | 29 | #include 30 | 31 | /* Function types. */ 32 | typedef int rb_comparison_func(const void *, const void *, void *); 33 | typedef void rb_item_func(void *, void *); 34 | typedef void *rb_copy_func(void *, void *); 35 | 36 | #ifndef LIBAVL_ALLOCATOR 37 | #define LIBAVL_ALLOCATOR 38 | /* Memory allocator. */ 39 | struct libavl_allocator { 40 | void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); 41 | void (*libavl_free) (struct libavl_allocator *, void *libavl_block); 42 | }; 43 | #endif 44 | 45 | /* Default memory allocator. */ 46 | extern struct libavl_allocator rb_allocator_default; 47 | void *rb_malloc(struct libavl_allocator *, size_t); 48 | void rb_free(struct libavl_allocator *, void *); 49 | 50 | /* Maximum RB height. */ 51 | #ifndef RB_MAX_HEIGHT 52 | #define RB_MAX_HEIGHT 48 53 | #endif 54 | 55 | /* Tree data structure. */ 56 | struct rb_table { 57 | struct rb_node *rb_root; /* Tree's root. */ 58 | rb_comparison_func *rb_compare; /* Comparison function. */ 59 | void *rb_param; /* Extra argument to |rb_compare|. */ 60 | struct libavl_allocator *rb_alloc; /* Memory allocator. */ 61 | size_t rb_count; /* Number of items in tree. */ 62 | unsigned long rb_generation; /* Generation number. */ 63 | }; 64 | 65 | /* Color of a red-black node. */ 66 | enum rb_color { 67 | RB_BLACK, /* Black. */ 68 | RB_RED /* Red. */ 69 | }; 70 | 71 | /* A red-black tree node. */ 72 | struct rb_node { 73 | struct rb_node *rb_link[2]; /* Subtrees. */ 74 | void *rb_data; /* Pointer to data. */ 75 | unsigned char rb_color; /* Color. */ 76 | }; 77 | 78 | /* RB traverser structure. */ 79 | struct rb_traverser { 80 | struct rb_table *rb_table; /* Tree being traversed. */ 81 | struct rb_node *rb_node; /* Current node in tree. */ 82 | struct rb_node *rb_stack[RB_MAX_HEIGHT]; 83 | /* All the nodes above |rb_node|. */ 84 | size_t rb_height; /* Number of nodes in |rb_parent|. */ 85 | unsigned long rb_generation; /* Generation number. */ 86 | }; 87 | 88 | /* Table functions. */ 89 | struct rb_table *rb_create(rb_comparison_func *, void *, 90 | struct libavl_allocator *); 91 | struct rb_table *rb_copy(const struct rb_table *, rb_copy_func *, 92 | rb_item_func *, struct libavl_allocator *); 93 | void rb_destroy(struct rb_table *, rb_item_func *); 94 | void **rb_probe(struct rb_table *, void *); 95 | void *rb_insert(struct rb_table *, void *); 96 | void *rb_replace(struct rb_table *, void *); 97 | const void *rb_delete(struct rb_table *, const void *); 98 | void *rb_find(const struct rb_table *, const void *); 99 | void rb_assert_insert(struct rb_table *, void *); 100 | const void *rb_assert_delete(struct rb_table *, const void *); 101 | 102 | #define rb_count(table) ((size_t) (table)->rb_count) 103 | 104 | /* Table traverser functions. */ 105 | void rb_t_init(struct rb_traverser *, struct rb_table *); 106 | void *rb_t_first(struct rb_traverser *, struct rb_table *); 107 | void *rb_t_last(struct rb_traverser *, struct rb_table *); 108 | void *rb_t_find(struct rb_traverser *, struct rb_table *, void *); 109 | void *rb_t_insert(struct rb_traverser *, struct rb_table *, void *); 110 | void *rb_t_copy(struct rb_traverser *, const struct rb_traverser *); 111 | void *rb_t_next(struct rb_traverser *); 112 | void *rb_t_prev(struct rb_traverser *); 113 | void *rb_t_cur(struct rb_traverser *); 114 | void *rb_t_replace(struct rb_traverser *, void *); 115 | 116 | #endif /* rb.h */ 117 | -------------------------------------------------------------------------------- /ini.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include "pfcq.h" 22 | 23 | #include "ini.h" 24 | 25 | ds_cfg_t* ds_cfg_open(const char* _app_name, const char* _filepath) 26 | { 27 | ds_cfg_t* ret = NULL; 28 | 29 | if (unlikely(config_from_file(_app_name, _filepath, &ret, INI_STOP_ON_ANY, NULL))) 30 | stop("Unable to load config file"); 31 | 32 | return ret; 33 | } 34 | 35 | void ds_cfg_close(ds_cfg_t* _config) 36 | { 37 | free_ini_config(_config); 38 | 39 | return; 40 | } 41 | 42 | static ds_cfg_t* __ds_cfg_get_item(ds_cfg_t* _config, const char* _section, const char* _key) 43 | { 44 | ds_cfg_t* ret = NULL; 45 | 46 | if (unlikely(get_config_item(_section, _key, _config, &ret))) 47 | { 48 | inform("Section: %s, key: %s\n", _section, _key); 49 | stop("Unable to get item from config file"); 50 | } 51 | 52 | return ret; 53 | } 54 | 55 | uint64_t ds_cfg_get_u64(ds_cfg_t* _config, const char* _section, const char* _key, uint64_t _default) 56 | { 57 | int error = 0; 58 | uint64_t ret = 0; 59 | 60 | ret = get_uint64_config_value(__ds_cfg_get_item(_config, _section, _key), 1, _default, &error); 61 | if (unlikely(error)) 62 | { 63 | inform("Section: %s, key: %s\n", _section, _key); 64 | stop("Unable to get value from item in config file"); 65 | } 66 | 67 | return ret; 68 | } 69 | 70 | int ds_cfg_get_int(ds_cfg_t* _config, const char* _section, const char* _key, int _default) 71 | { 72 | int error = 0; 73 | int ret = 0; 74 | 75 | ret = get_int_config_value(__ds_cfg_get_item(_config, _section, _key), 1, _default, &error); 76 | if (unlikely(error)) 77 | { 78 | inform("Section: %s, key: %s\n", _section, _key); 79 | stop("Unable to get value from item in config file"); 80 | } 81 | 82 | return ret; 83 | } 84 | 85 | unsigned ds_cfg_get_uint(ds_cfg_t* _config, const char* _section, const char* _key, unsigned _default) 86 | { 87 | int error = 0; 88 | unsigned ret = 0; 89 | 90 | ret = get_unsigned_config_value(__ds_cfg_get_item(_config, _section, _key), 1, _default, &error); 91 | if (unlikely(error)) 92 | { 93 | inform("Section: %s, key: %s\n", _section, _key); 94 | stop("Unable to get value from item in config file"); 95 | } 96 | 97 | return ret; 98 | } 99 | 100 | const char* ds_cfg_get_cstr(ds_cfg_t* _config, const char* _section, const char* _key) 101 | { 102 | const char* ret = NULL; 103 | 104 | ret = ds_cfg_try_get_cstr(_config, _section, _key); 105 | if (unlikely(!ret)) 106 | { 107 | inform("Section: %s, key: %s\n", _section, _key); 108 | stop("Unable to get value from item in config file"); 109 | } 110 | 111 | return ret; 112 | } 113 | 114 | const char* ds_cfg_try_get_cstr(ds_cfg_t* _config, const char* _section, const char* _key) 115 | { 116 | int error = 0; 117 | const char* ret = NULL; 118 | 119 | ret = get_const_string_config_value(__ds_cfg_get_item(_config, _section, _key), &error); 120 | if (unlikely(error)) 121 | ret = NULL; 122 | 123 | return ret; 124 | } 125 | 126 | char** ds_cfg_get_keys(ds_cfg_t* _config, const char* _section, int* _size) 127 | { 128 | int error = 0; 129 | char** ret = NULL; 130 | 131 | ret = get_attribute_list(_config, _section, _size, &error); 132 | if (unlikely(error)) 133 | { 134 | inform("Section: %s\n", _section); 135 | stop("Unable to enumerate keys in section of config file"); 136 | } 137 | 138 | return ret; 139 | } 140 | 141 | void ds_cfg_free_keys(char** _keys) 142 | { 143 | free_attribute_list(_keys); 144 | 145 | return; 146 | } 147 | 148 | char** ds_cfg_get_sections(ds_cfg_t* _config, int* _size) 149 | { 150 | int error = 0; 151 | char** ret = NULL; 152 | 153 | ret = get_section_list(_config, _size, &error); 154 | if (unlikely(error)) 155 | { 156 | stop("Unable to enumerate sections in config file"); 157 | } 158 | 159 | return ret; 160 | } 161 | 162 | void ds_cfg_free_sections(char** _sections) 163 | { 164 | free_section_list(_sections); 165 | } 166 | 167 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "pfcq.h" 30 | #include "sys.h" 31 | 32 | enum ds_pkt_type 33 | { 34 | DS_PKT_UNK = 0, 35 | DS_PKT_REQ, 36 | DS_PKT_REP, 37 | }; 38 | 39 | enum ds_tsk_type 40 | { 41 | DS_TSK_UNK = 0, 42 | DS_TSK_REG, 43 | DS_TSK_WDT, 44 | }; 45 | 46 | enum ds_act_type 47 | { 48 | DS_ACT_UNK = 0, 49 | DS_ACT_BALANCE, 50 | }; 51 | 52 | enum ds_act_balance_type 53 | { 54 | DS_ACT_BALANCE_UNK = 0, 55 | DS_ACT_BALANCE_RR, 56 | DS_ACT_BALANCE_STICKY, 57 | }; 58 | 59 | struct ds_wrk_tsk 60 | { 61 | TAILQ_ENTRY(ds_wrk_tsk) tailq; 62 | 63 | bool redirected; // ] meta 64 | 65 | char* buf; // ] 66 | ssize_t buf_size; // | raw 67 | ldns_pkt* pkt; // ] 68 | 69 | uint16_t subst_id; // ] 70 | char fqdn[HOST_NAME_MAX]; // | 71 | ldns_rr_type rr_type; // | key 72 | ldns_rr_class rr_class; // | 73 | struct ds_fwd_sk* fwd_sk; // | 74 | struct pfcq_net_addr fwd_sk_addr; // ] 75 | 76 | struct pfcq_net_addr addr; // ] 77 | uint16_t orig_id; // | 78 | struct ds_fe_sk* orig_fe_sk; // | 79 | struct pfcq_net_addr orig_fe_addr; // | value 80 | struct ds_fwd* fwd; // | 81 | struct pfcq_counter epoch; // | 82 | enum ds_tsk_type type; // ] 83 | }; 84 | 85 | TAILQ_HEAD(ds_wrk_tsk_list, ds_wrk_tsk); 86 | 87 | struct ds_act_item 88 | { 89 | char* name; 90 | struct pfcq_net_addr addr; 91 | struct pfcq_net_addr mask; 92 | ldns_rr_class rr_class; 93 | ldns_rr_type rr_type; 94 | char* expr; 95 | regex_t regex; 96 | enum ds_act_type act_type; 97 | // TODO: 98 | enum ds_act_balance_type act_balance_type; 99 | size_t act_balance_nfwds; 100 | struct ds_fwd** act_balance_fwds; 101 | struct pfcq_counter c_fwd; 102 | }; 103 | 104 | struct ds_act 105 | { 106 | char* name; 107 | size_t nact_items; 108 | struct ds_act_item* act_items; 109 | }; 110 | 111 | struct ds_fe 112 | { 113 | char* name; 114 | struct pfcq_net_addr addr; 115 | int dscp; 116 | size_t nacts; 117 | struct ds_act* acts; 118 | }; 119 | 120 | struct ds_fwd 121 | { 122 | char* name; // ] meta 123 | 124 | struct pfcq_net_addr addr; // ] 125 | int reg_dscp; // | net 126 | int wdt_dscp; // ] 127 | 128 | struct pfcq_counter c_q_id; // ] DNS query ID 129 | 130 | char* wdt_query; // ] 131 | struct pfcq_counter wdt_pending; // | watchdog 132 | size_t wdt_tries; // | 133 | bool alive; // ] 134 | }; 135 | 136 | struct ds_fe_sk 137 | { 138 | int sk; 139 | struct ds_fe* fe; 140 | }; 141 | 142 | struct ds_fwd_sk 143 | { 144 | int sk; 145 | struct ds_fwd* fwd; 146 | }; 147 | 148 | struct ds_wrk_ctx 149 | { 150 | struct ds_ctx* ctx; // ] 151 | size_t index; // | meta 152 | pthread_t id; // ] 153 | 154 | int ready; 155 | 156 | struct rb_table* tracking; 157 | struct rb_table* fe_sk_set; 158 | struct rb_table* fwd_sk_set; 159 | struct rb_table* fwd_wdt_sk_set; 160 | int poll_timeo; 161 | int wrk_fd; 162 | 163 | int ev_prep_fd; 164 | int ev_fwd_fd; 165 | int ev_rep_fd; 166 | int ev_wdt_rep_fd; 167 | int ev_gc_fd; 168 | int ev_exit_fd; 169 | struct ds_wrk_tsk_list prep_queue; 170 | struct ds_wrk_tsk_list fwd_queue; 171 | struct ds_wrk_tsk_list rep_queue; 172 | pthread_spinlock_t rep_queue_lock; 173 | struct ds_wrk_tsk_list wdt_rep_queue; 174 | pthread_spinlock_t wdt_rep_queue_lock; 175 | }; 176 | 177 | struct ds_ctx 178 | { 179 | bool redirect; 180 | struct pfcq_counter c_redirect_wrk; 181 | struct ds_ctx* ctx_next; 182 | size_t max_pkt_size; 183 | struct ds_fe* fes; 184 | size_t nfes; 185 | struct ds_fwd* fwds; 186 | size_t nfwds; 187 | struct ds_act* acts; 188 | size_t nacts; 189 | int wdt_fd; 190 | int tk_fd; 191 | uint64_t req_ttl; 192 | uint64_t gc_intvl; 193 | struct ds_wrk_ctx** wrks; 194 | size_t nwrks; 195 | int poll_timeo; 196 | struct pfcq_counter epoch; 197 | uint64_t epoch_size; 198 | struct pfcq_counter in_flight; 199 | }; 200 | 201 | typedef int (*ds_loop_handler_fn_t)(struct epoll_event, struct ds_wrk_ctx*); 202 | 203 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /* 22 | * TODO: 23 | * - ACLs 24 | * - TCP? 25 | * - weighted RR distribution 26 | * - JSON stats over HTTP 27 | * - HTTP RPC 28 | * - https://tools.ietf.org/html/rfc7871 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "types.h" 36 | 37 | #include "context.h" 38 | #include "pfcq.h" 39 | #include "utils.h" 40 | #include "worker.h" 41 | 42 | #define DS_APP_NAME "dnsbalancer" 43 | #define DS_APP_VERSION "0.2.0" 44 | 45 | static void help(void) 46 | { 47 | message("Usage: dnsbalancer --help | --config= [--daemonize] [--verbose] [--debug]"); 48 | message(" [--syslog]"); 49 | message(""); 50 | message(" --config= specifies configuration file to use (mandatory)"); 51 | message(" --daemonize enables daemonization (preferred way to run on server)"); 52 | message(" --verbose enables verbose output"); 53 | message(" --debug enables debug output"); 54 | message(" works only if compiled with MODE=DEBUG, otherwise does nothing"); 55 | message(" --syslog logs everything to syslog instead of /dev/stderr"); 56 | message(" --help shows this help and exits"); 57 | message(" --version shows program version and exits"); 58 | message(""); 59 | message("Typical usage:"); 60 | message("dnsbalancer --config=/etc/dnsbalancer/dnsbalancer.conf --verbose --syslog"); 61 | } 62 | 63 | int main(int _argc, char** _argv) 64 | { 65 | int opts = 0; 66 | int daemonize = 0; 67 | int be_verbose = 0; 68 | int do_debug = 0; 69 | int use_syslog = 0; 70 | int sfd = -1; 71 | char* config_file = NULL; 72 | sigset_t ds_sigmask; 73 | struct ds_ctx* ctx = NULL; 74 | struct ds_ctx* ctx_next = NULL; 75 | 76 | struct option longopts[] = { 77 | {"config", required_argument, NULL, 'a'}, 78 | {"daemonize", no_argument, NULL, 'b'}, 79 | {"verbose", no_argument, NULL, 'c'}, 80 | {"debug", no_argument, NULL, 'd'}, 81 | {"syslog", no_argument, NULL, 'e'}, 82 | {"help", no_argument, NULL, 'f'}, 83 | {"version", no_argument, NULL, 'g'}, 84 | {0, 0, 0, 0} 85 | }; 86 | 87 | pfcq_zero(&ds_sigmask, sizeof(sigset_t)); 88 | 89 | while ((opts = getopt_long(_argc, _argv, "abcdefg", longopts, NULL)) != -1) 90 | { 91 | switch (opts) 92 | { 93 | case 'a': 94 | config_file = pfcq_strdup(optarg); 95 | break; 96 | case 'b': 97 | daemonize = 1; 98 | break; 99 | case 'c': 100 | be_verbose = 1; 101 | break; 102 | case 'd': 103 | do_debug = 1; 104 | break; 105 | case 'e': 106 | use_syslog = 1; 107 | break; 108 | case 'f': 109 | help(); 110 | stop_code(EX_OK, NULL); 111 | break; 112 | case 'g': 113 | stop_code(EX_OK, DS_APP_NAME " v" DS_APP_VERSION); 114 | break; 115 | default: 116 | stop_code(EX_USAGE, "Unknown option occurred"); 117 | break; 118 | } 119 | } 120 | 121 | pfcq_debug_init(be_verbose, do_debug, use_syslog); 122 | 123 | if (unlikely(!config_file)) 124 | stop("No config file specified"); 125 | 126 | if (daemonize) 127 | if (unlikely(daemon(0, 0) != 0)) 128 | panic("daemon"); 129 | 130 | sigemptyset(&ds_sigmask); 131 | sigaddset(&ds_sigmask, SIGTERM); 132 | sigaddset(&ds_sigmask, SIGINT); 133 | sigaddset(&ds_sigmask, SIGUSR1); 134 | pthread_sigmask(SIG_BLOCK, &ds_sigmask, NULL); 135 | 136 | sfd = signalfd(-1, &ds_sigmask, 0); 137 | if (unlikely(sfd == -1)) 138 | panic("signalfd"); 139 | 140 | // context 141 | ctx = ds_ctx_load(config_file); 142 | 143 | while (true) 144 | { 145 | struct signalfd_siginfo si; 146 | ssize_t res = -1; 147 | 148 | pfcq_zero(&si, sizeof(struct signalfd_siginfo)); 149 | 150 | res = ds_read(sfd, &si, sizeof(struct signalfd_siginfo)); 151 | if (unlikely(res == -1 || res != sizeof(struct signalfd_siginfo))) 152 | panic("ds_read"); 153 | 154 | switch (si.ssi_signo) 155 | { 156 | case SIGINT: 157 | case SIGTERM: 158 | ds_ctx_unload(ctx); 159 | goto out; 160 | case SIGUSR1: 161 | ctx_next = ds_ctx_load(config_file); 162 | 163 | ctx->ctx_next = ctx_next; 164 | ctx->redirect = true; 165 | 166 | ds_ctx_unload(ctx); 167 | 168 | ctx = ctx_next; 169 | 170 | continue; 171 | default: 172 | panic("Unknown signal"); 173 | } 174 | } 175 | 176 | out: 177 | ds_close(sfd); 178 | pthread_sigmask(SIG_UNBLOCK, &ds_sigmask, NULL); 179 | 180 | verbose("%s\n", "Ciao."); 181 | 182 | pfcq_free(config_file); 183 | 184 | pfcq_debug_done(); 185 | 186 | stop_code(EX_OK, NULL); 187 | } 188 | -------------------------------------------------------------------------------- /evloop.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include "context.h" 22 | #include "handlers.h" 23 | #include "pfcq.h" 24 | #include "rb.h" 25 | #include "utils.h" 26 | 27 | #include "evloop.h" 28 | 29 | int ds_wrk_loop_handler(struct epoll_event _event, struct ds_wrk_ctx* _data) 30 | { 31 | struct ds_fe_sk* fe_sk = NULL; 32 | struct ds_fe_sk ref_fe_sk; 33 | struct ds_fwd_sk* fwd_sk = NULL; 34 | struct ds_fwd_sk ref_fwd_sk; 35 | int ret = 0; 36 | 37 | pfcq_zero(&ref_fe_sk, sizeof(struct ds_fe_sk)); 38 | 39 | // prepare to forward 40 | if (_event.data.fd == _data->ev_prep_fd) 41 | { 42 | ret = ds_wrk_prep_handler(_event.data.fd, _data); 43 | goto out; 44 | } 45 | 46 | // perform forwarding 47 | if (_event.data.fd == _data->ev_fwd_fd) 48 | { 49 | ret = ds_wrk_fwd_handler(_event.data.fd, _data); 50 | goto out; 51 | } 52 | 53 | // send reply 54 | if (_event.data.fd == _data->ev_rep_fd) 55 | { 56 | ret = ds_wrk_rep_handler(_event.data.fd, _data); 57 | goto out; 58 | } 59 | 60 | // process reply for watchdog 61 | if (_event.data.fd == _data->ev_wdt_rep_fd) 62 | { 63 | ret = ds_wrk_wdt_rep_handler(_event.data.fd, _data); 64 | goto out; 65 | } 66 | 67 | // perform exit 68 | if (_event.data.fd == _data->ev_exit_fd) 69 | { 70 | ret = ds_wrk_exit_handler(_event.data.fd, _data); 71 | goto out; 72 | } 73 | 74 | // perform tracking tree GC 75 | if (_event.data.fd == _data->ev_gc_fd) 76 | { 77 | ret = ds_wrk_gc_handler(_event.data.fd, _data); 78 | goto out; 79 | } 80 | 81 | // prepare forwarders watchdog request 82 | if (_event.data.fd == _data->ctx->wdt_fd) 83 | { 84 | ret = ds_wrk_wdt_req_handler(_event.data.fd, _data); 85 | goto out; 86 | } 87 | 88 | // perform timekeeping tasks 89 | if (_event.data.fd == _data->ctx->tk_fd) 90 | { 91 | ret = ds_wrk_tk_handler(_event.data.fd, _data); 92 | goto out; 93 | } 94 | 95 | // accept request from client 96 | ref_fe_sk.sk = _event.data.fd; 97 | if ((fe_sk = rb_find(_data->fe_sk_set, &ref_fe_sk))) 98 | { 99 | ret = ds_wrk_acpt_handler(fe_sk, _data); 100 | goto out; 101 | } 102 | 103 | // obtain regular response from forwarder 104 | ref_fwd_sk.sk = _event.data.fd; 105 | if ((fwd_sk = rb_find(_data->fwd_sk_set, &ref_fwd_sk))) 106 | { 107 | ret = ds_wrk_obt_handler(fwd_sk, _data); 108 | goto out; 109 | } 110 | 111 | // obtain watchdog response from forwarder 112 | ref_fwd_sk.sk = _event.data.fd; 113 | if ((fwd_sk = rb_find(_data->fwd_wdt_sk_set, &ref_fwd_sk))) 114 | { 115 | ret = ds_wrk_obt_handler(fwd_sk, _data); 116 | goto out; 117 | } 118 | 119 | out: 120 | return ret; 121 | } 122 | 123 | void ds_loop(ds_loop_handler_fn_t _handler, struct ds_wrk_ctx* _data) 124 | { 125 | int events = -1; 126 | ssize_t has_read = -1; 127 | struct epoll_event got_events[EPOLL_MAXEVENTS]; 128 | char* read_buf = NULL; 129 | 130 | pfcq_zero(got_events, EPOLL_MAXEVENTS * sizeof(struct epoll_event)); 131 | 132 | read_buf = pfcq_alloc(_data->ctx->max_pkt_size); 133 | 134 | while (true) 135 | { 136 | events = ds_epoll_wait(_data->wrk_fd, got_events, EPOLL_MAXEVENTS, _data->poll_timeo); 137 | if (unlikely(events == 0)) 138 | { 139 | if (unlikely(rb_count(_data->tracking) == 0 && pfcq_counter_get(&_data->ctx->in_flight) == 0)) 140 | goto out; 141 | else 142 | continue; 143 | } else if (unlikely(events == -1)) 144 | { 145 | // EINTR is caught in wrapper; 146 | // everything else is unexpected, so if it happens, something went wrong; 147 | // that's why panic immediately 148 | panic("epoll_wait"); 149 | } else 150 | { 151 | for (size_t i = 0; i < (size_t)events; i++) 152 | { 153 | if (likely(got_events[i].events == EPOLLIN)) 154 | { 155 | // consume data as no error happened 156 | if (unlikely(_handler(got_events[i], _data) == -1)) 157 | goto out; 158 | } else if (unlikely(got_events[i].events == EPOLLERR)) 159 | { 160 | // EPOLLERR might be returned if network issue happened 161 | // explicitly ignore this; also consume value and check 162 | // for error returned; ECONNREFUSED and EAGAIN are expected only, 163 | // otherwise panic immediately 164 | has_read = ds_read(got_events[i].data.fd, read_buf, _data->ctx->max_pkt_size); 165 | if (unlikely(has_read == -1 && errno != ECONNREFUSED && errno != EAGAIN)) 166 | { 167 | inform("%d\n", errno); 168 | panic("ds_read"); 169 | } else 170 | continue; 171 | } else 172 | { 173 | // nothing else is expected, so panic immediately, 174 | // because something went wrong 175 | inform("Got events: %d\n", got_events[i].events); 176 | panic("epoll_wait"); 177 | } 178 | } 179 | } 180 | } 181 | 182 | out: 183 | 184 | pfcq_free(read_buf); 185 | 186 | return; 187 | } 188 | 189 | -------------------------------------------------------------------------------- /pfcq.h: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "sys.h" 34 | 35 | #define __noop (void)0 36 | 37 | #define inform(A, ...) __pfcq_debug(1, A, __VA_ARGS__) 38 | #define message(A) inform("%s\n", A) 39 | #define verbose(A, ...) __pfcq_debug(0, A, __VA_ARGS__) 40 | 41 | #ifdef MODE_DEBUG 42 | #define debug(A, ...) __pfcq_debug(0, A, __VA_ARGS__) 43 | #else /* MODE_DEBUG */ 44 | #define debug(A, ...) __noop 45 | #endif /* MODE_DEBUG */ 46 | 47 | #define warning(A) __pfcq_warning(A, errno, __FILE__, __LINE__, 1) 48 | #define fail(A) __pfcq_fail(A, errno) 49 | #define stop(A) stop_code(EX_SOFTWARE, A) 50 | #define stop_code(A, B) __pfcq_stop(A, B) 51 | #define panic(A) __pfcq_panic(A, errno, __FILE__, __LINE__) 52 | 53 | #define pfcq_zero(A, B) pfcq_memset_g(A, 0, B) 54 | #define pfcq_free(A) __pfcq_free((void**)&(A)) 55 | 56 | #ifdef __GNUC__ 57 | #define likely(x) __builtin_expect(!!(x), 1) 58 | #define unlikely(x) __builtin_expect(!!(x), 0) 59 | #else /* __GNUC__ */ 60 | #define likely(x) (x) 61 | #define unlikely(x) (x) 62 | #endif /* __GNUC__ */ 63 | 64 | // Convention: B *MUST* be string literal, 65 | // otherwise compiler will throw an error 66 | #define pfcq_strlcmp(A, B) __pfcq_strlcmp(A, "" B) 67 | 68 | // Under 23.5k+ QPS max 6 events are returned per worker; 69 | // doubling it just in case 70 | #define EPOLL_MAXEVENTS 12 71 | 72 | struct pfcq_net_addr 73 | { 74 | sa_family_t family; 75 | union addr 76 | { 77 | struct sockaddr_in ip4; 78 | struct sockaddr_in6 ip6; 79 | } addr; 80 | }; 81 | 82 | struct pfcq_counter 83 | { 84 | #ifdef DS_HAVE_ATOMICS 85 | atomic_size_t val; 86 | #else /* DS_HAVE_ATOMICS */ 87 | AO_t val; 88 | #endif /* DS_HAVE_ATOMICS */ 89 | }; 90 | 91 | void __pfcq_debug(int, const char*, ...) __attribute__((format(printf, 2, 3))); 92 | void __pfcq_warning(const char*, const int, const char*, int, int); 93 | void __pfcq_fail(const char*, const int); 94 | void __pfcq_stop(const int, const char*) __attribute__((noreturn)); 95 | void __pfcq_panic(const char*, const int, const char*, int) __attribute__((noreturn)); 96 | void pfcq_debug_init(int, int, int); 97 | void pfcq_debug_done(void); 98 | 99 | void pfcq_memset_g(void*, int, size_t); 100 | 101 | #ifdef __clang__ 102 | void* pfcq_alloc(size_t) __attribute__((malloc, warn_unused_result)); 103 | void* pfcq_realloc(void*, size_t) __attribute__((malloc, warn_unused_result)); 104 | #else /* __clang__ */ 105 | void* pfcq_alloc(size_t) __attribute__((malloc, alloc_size(1), warn_unused_result)); 106 | void* pfcq_realloc(void*, size_t) __attribute__((malloc, alloc_size(2), warn_unused_result)); 107 | #endif /* __clang__ */ 108 | void __pfcq_free(void**); 109 | 110 | int pfcq_isnumber(const char*) __attribute__((warn_unused_result)); 111 | int __pfcq_strlcmp(const char*, const char*) __attribute__((warn_unused_result)); 112 | char* pfcq_strdup(const char*) __attribute__((warn_unused_result)); 113 | char* pfcq_mstring(const char*, ...) __attribute__((format(printf, 1, 2), warn_unused_result)); 114 | char* pfcq_cstring(char*, const char*) __attribute__((warn_unused_result)); 115 | char* pfcq_bstring(const char*, size_t) __attribute__((warn_unused_result)); 116 | char** pfcq_split_string(const char*, const char*, size_t*) __attribute__((warn_unused_result)); 117 | void pfcq_free_split_string(char**, size_t); 118 | unsigned long int pfcq_strtoul(const char*, int) __attribute__((warn_unused_result)); 119 | 120 | unsigned short int pfcq_hint_cpus(int) __attribute__((warn_unused_result)); 121 | 122 | int64_t pfcq_timespec_diff_ns(struct timespec, struct timespec) __attribute__((warn_unused_result)); 123 | struct timeval pfcq_us_to_timeval(uint64_t) __attribute__((warn_unused_result)); 124 | void pfcq_sleep(uint64_t); 125 | struct timespec pfcq_ns_to_timespec(uint64_t) __attribute__((warn_unused_result)); 126 | 127 | void pfcq_spin_init(pthread_spinlock_t*); 128 | void pfcq_spin_lock(pthread_spinlock_t*); 129 | void pfcq_spin_unlock(pthread_spinlock_t*); 130 | void pfcq_spin_done(pthread_spinlock_t*); 131 | 132 | uint64_t pfcq_fast_hash(const uint8_t*, size_t, uint64_t) __attribute__((warn_unused_result)); 133 | 134 | bool pfcq_net_addr_cmp(struct pfcq_net_addr*, struct pfcq_net_addr*) __attribute__((warn_unused_result)); 135 | 136 | void pfcq_counter_init(struct pfcq_counter*); 137 | void pfcq_counter_reset(struct pfcq_counter*); 138 | void pfcq_counter_inc(struct pfcq_counter*); 139 | void pfcq_counter_dec(struct pfcq_counter*); 140 | size_t pfcq_counter_get(struct pfcq_counter*) __attribute__((warn_unused_result)); 141 | size_t pfcq_counter_get_inc_mod(struct pfcq_counter*, size_t, size_t) __attribute__((warn_unused_result)); 142 | bool pfcq_counter_reset_if_gt(struct pfcq_counter*, size_t) __attribute__((warn_unused_result)); 143 | void pfcq_counter_set(struct pfcq_counter*, size_t); 144 | 145 | -------------------------------------------------------------------------------- /worker.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "evloop.h" 25 | #include "pfcq.h" 26 | #include "rb.h" 27 | #include "utils.h" 28 | 29 | #include "worker.h" 30 | 31 | void* ds_wrk(void* _data) 32 | { 33 | struct ds_wrk_ctx* data = _data; 34 | 35 | int option = 1; 36 | struct rb_traverser iter; 37 | struct ds_fwd_sk* cur_fwd_sk = NULL; 38 | struct ds_fwd_sk* cur_fwd_wdt_sk = NULL; 39 | 40 | pfcq_zero(&iter, sizeof(struct rb_traverser)); 41 | 42 | data->wrk_fd = ds_epoll_create(); 43 | data->poll_timeo = -1; 44 | 45 | data->tracking = rb_create(ds_tsk_cmp, NULL, &ds_rb_allocator); 46 | 47 | data->fe_sk_set = rb_create(ds_fe_sk_cmp, NULL, &ds_rb_allocator); 48 | for (size_t i = 0; i < data->ctx->nfes; i++) 49 | { 50 | struct ds_fe_sk* new_sk = pfcq_alloc(sizeof(struct ds_fe_sk)); 51 | new_sk->fe = &data->ctx->fes[i]; 52 | new_sk->sk = ds_socket(ds_af_to_pf(new_sk->fe->addr.family), SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 53 | ds_setsockopt(new_sk->sk, IPPROTO_IP, IP_FREEBIND, 54 | (const void*)&option, sizeof(option)); 55 | ds_setsockopt(new_sk->sk, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, 56 | (const void*)&option, sizeof(option)); 57 | ds_set_sock_dscp(new_sk->sk, ds_af_to_pf(new_sk->fe->addr.family), new_sk->fe->dscp); 58 | ds_bind(new_sk->sk, &new_sk->fe->addr); 59 | ds_epoll_add_fd(data->wrk_fd, new_sk->sk, EPOLLIN); 60 | rb_insert(data->fe_sk_set, (void*)new_sk); 61 | } 62 | 63 | data->fwd_sk_set = rb_create(ds_fwd_sk_cmp, NULL, &ds_rb_allocator); 64 | for (size_t i = 0; i < data->ctx->nfwds; i++) 65 | { 66 | struct ds_fwd_sk* new_sk = pfcq_alloc(sizeof(struct ds_fwd_sk)); 67 | new_sk->fwd = &data->ctx->fwds[i]; 68 | new_sk->sk = ds_socket(ds_af_to_pf(new_sk->fwd->addr.family), SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 69 | ds_setsockopt(new_sk->sk, SOL_SOCKET, SO_REUSEADDR, 70 | (const void*)&option, sizeof(option)); 71 | ds_set_sock_dscp(new_sk->sk, ds_af_to_pf(new_sk->fwd->addr.family), new_sk->fwd->reg_dscp); 72 | ds_connect(new_sk->sk, &new_sk->fwd->addr); 73 | ds_epoll_add_fd(data->wrk_fd, new_sk->sk, EPOLLIN); 74 | rb_insert(data->fwd_sk_set, (void*)new_sk); 75 | } 76 | 77 | data->fwd_wdt_sk_set = rb_create(ds_fwd_sk_cmp, NULL, &ds_rb_allocator); 78 | for (size_t i = 0; i < data->ctx->nfwds; i++) 79 | { 80 | struct ds_fwd_sk* new_sk = pfcq_alloc(sizeof(struct ds_fwd_sk)); 81 | new_sk->fwd = &data->ctx->fwds[i]; 82 | new_sk->sk = ds_socket(ds_af_to_pf(new_sk->fwd->addr.family), SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); 83 | ds_setsockopt(new_sk->sk, SOL_SOCKET, SO_REUSEADDR, 84 | (const void*)&option, sizeof(option)); 85 | ds_set_sock_dscp(new_sk->sk, ds_af_to_pf(new_sk->fwd->addr.family), new_sk->fwd->wdt_dscp); 86 | ds_connect(new_sk->sk, &new_sk->fwd->addr); 87 | ds_epoll_add_fd(data->wrk_fd, new_sk->sk, EPOLLIN); 88 | rb_insert(data->fwd_wdt_sk_set, (void*)new_sk); 89 | } 90 | 91 | data->ev_prep_fd = ds_eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); 92 | data->ev_fwd_fd = ds_eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); 93 | data->ev_rep_fd = ds_eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); 94 | data->ev_wdt_rep_fd = ds_eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); 95 | data->ev_exit_fd = ds_eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK); 96 | data->ev_gc_fd = ds_timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 97 | ds_timerfd_settime(data->ev_gc_fd, data->ctx->gc_intvl); 98 | TAILQ_INIT(&data->prep_queue); 99 | TAILQ_INIT(&data->fwd_queue); 100 | TAILQ_INIT(&data->rep_queue); 101 | TAILQ_INIT(&data->wdt_rep_queue); 102 | pfcq_spin_init(&data->rep_queue_lock); 103 | pfcq_spin_init(&data->wdt_rep_queue_lock); 104 | ds_epoll_add_fd(data->wrk_fd, data->ev_prep_fd, EPOLLIN); 105 | ds_epoll_add_fd(data->wrk_fd, data->ev_fwd_fd, EPOLLIN); 106 | ds_epoll_add_fd(data->wrk_fd, data->ev_rep_fd, EPOLLIN); 107 | ds_epoll_add_fd(data->wrk_fd, data->ev_wdt_rep_fd, EPOLLIN); 108 | ds_epoll_add_fd(data->wrk_fd, data->ev_exit_fd, EPOLLIN); 109 | ds_epoll_add_fd(data->wrk_fd, data->ev_gc_fd, EPOLLIN); 110 | 111 | ds_epoll_add_fd(data->wrk_fd, data->ctx->wdt_fd, EPOLLIN | EPOLLEXCLUSIVE); 112 | ds_epoll_add_fd(data->wrk_fd, data->ctx->tk_fd, EPOLLIN | EPOLLEXCLUSIVE); 113 | 114 | ds_produce_u64(data->ready); 115 | 116 | ds_loop(ds_wrk_loop_handler, data); 117 | 118 | rb_destroy(data->tracking, NULL); 119 | 120 | rb_destroy(data->fe_sk_set, ds_rb_item_free); 121 | 122 | rb_t_init(&iter, data->fwd_sk_set); 123 | cur_fwd_sk = rb_t_first(&iter, data->fwd_sk_set); 124 | do { 125 | ds_close(cur_fwd_sk->sk); 126 | } while (likely((cur_fwd_sk = rb_t_next(&iter)) != NULL)); 127 | rb_destroy(data->fwd_sk_set, ds_rb_item_free); 128 | 129 | rb_t_init(&iter, data->fwd_wdt_sk_set); 130 | cur_fwd_wdt_sk = rb_t_first(&iter, data->fwd_wdt_sk_set); 131 | do { 132 | ds_close(cur_fwd_wdt_sk->sk); 133 | } while (likely((cur_fwd_wdt_sk = rb_t_next(&iter)) != NULL)); 134 | rb_destroy(data->fwd_wdt_sk_set, ds_rb_item_free); 135 | 136 | ds_epoll_del_fd(data->wrk_fd, data->ctx->tk_fd); 137 | ds_epoll_del_fd(data->wrk_fd, data->ev_prep_fd); 138 | ds_epoll_del_fd(data->wrk_fd, data->ev_fwd_fd); 139 | ds_epoll_del_fd(data->wrk_fd, data->ev_rep_fd); 140 | ds_epoll_del_fd(data->wrk_fd, data->ev_wdt_rep_fd); 141 | ds_epoll_del_fd(data->wrk_fd, data->ev_gc_fd); 142 | ds_epoll_del_fd(data->wrk_fd, data->ev_exit_fd); 143 | 144 | ds_close(data->ev_prep_fd); 145 | ds_close(data->ev_fwd_fd); 146 | ds_close(data->ev_rep_fd); 147 | ds_close(data->ev_wdt_rep_fd); 148 | ds_close(data->ev_exit_fd); 149 | ds_close(data->ev_gc_fd); 150 | 151 | pfcq_spin_done(&data->rep_queue_lock); 152 | pfcq_spin_done(&data->wdt_rep_queue_lock); 153 | 154 | ds_close(data->wrk_fd); 155 | 156 | return NULL; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "pfcq.h" 28 | #include "rb.h" 29 | 30 | #include "utils.h" 31 | 32 | struct libavl_allocator ds_rb_allocator = 33 | { 34 | ds_rb_malloc, 35 | ds_rb_free, 36 | }; 37 | 38 | void* ds_rb_malloc(struct libavl_allocator* _allocator, size_t _libavl_size) 39 | { 40 | if (unlikely(!_allocator)) 41 | panic("_allocator"); 42 | 43 | if (unlikely(_libavl_size == 0)) 44 | panic("_libavl_size"); 45 | 46 | return pfcq_alloc(_libavl_size); 47 | } 48 | 49 | void ds_rb_free(struct libavl_allocator* _allocator, void* _libavl_block) 50 | { 51 | if (unlikely(!_allocator)) 52 | panic("_allocator"); 53 | 54 | if (unlikely(!_libavl_block)) 55 | panic("_allocator"); 56 | 57 | pfcq_free(_libavl_block); 58 | } 59 | 60 | void ds_rb_item_free(void* _rb_item, void* _rb_param) 61 | { 62 | (void)_rb_param; 63 | 64 | pfcq_free(_rb_item); 65 | 66 | return; 67 | } 68 | 69 | void ds_tsk_free(struct ds_wrk_tsk* _tsk) 70 | { 71 | if (likely(_tsk->pkt)) 72 | ldns_pkt_free(_tsk->pkt); 73 | pfcq_free(_tsk->buf); 74 | pfcq_free(_tsk); 75 | 76 | return; 77 | } 78 | 79 | void ds_rb_tsk_free(void* _rb_item, void* _rb_param) 80 | { 81 | (void)_rb_param; 82 | 83 | struct ds_wrk_tsk* tsk = _rb_item; 84 | 85 | ds_tsk_free(tsk); 86 | 87 | return; 88 | } 89 | 90 | int ds_fe_sk_cmp(const void* _p1, const void* _p2, void* _param) 91 | { 92 | (void)_param; 93 | 94 | const struct ds_fe_sk* s1 = _p1; 95 | const struct ds_fe_sk* s2 = _p2; 96 | 97 | return ds_int_cmp(s1->sk, s2->sk); 98 | } 99 | 100 | int ds_fwd_sk_cmp(const void* _p1, const void* _p2, void* _param) 101 | { 102 | (void)_param; 103 | 104 | const struct ds_fwd_sk* s1 = _p1; 105 | const struct ds_fwd_sk* s2 = _p2; 106 | 107 | return ds_int_cmp(s1->sk, s2->sk); 108 | } 109 | 110 | int ds_tsk_cmp(const void* _p1, const void* _p2, void* _param) 111 | { 112 | (void)_param; 113 | 114 | const struct ds_wrk_tsk* t1 = _p1; 115 | const struct ds_wrk_tsk* t2 = _p2; 116 | 117 | int _s0 = ds_int_cmp(t1->subst_id, t2->subst_id); 118 | if (_s0 == 0) 119 | { 120 | int _s1 = strncmp(t1->fqdn, t2->fqdn, HOST_NAME_MAX); 121 | if (_s1 == 0) 122 | { 123 | int _s2 = ds_int_cmp(t1->rr_type, t2->rr_type); 124 | if (_s2 == 0) 125 | { 126 | int _s3 = ds_int_cmp(t1->rr_class, t2->rr_class); 127 | if (_s3 == 0) 128 | { 129 | return ds_ptr_cmp(t1->fwd_sk->fwd, t2->fwd_sk->fwd); 130 | } else 131 | return _s3; 132 | } else 133 | return _s2; 134 | } else 135 | return _s1; 136 | } else 137 | return _s0; 138 | } 139 | 140 | void ds_epoll_add_fd(int _evfd, int _fd, uint32_t _events) 141 | { 142 | struct epoll_event event; 143 | 144 | pfcq_zero(&event, sizeof(struct epoll_event)); 145 | 146 | event.data.fd = _fd; 147 | event.events = _events; 148 | if (unlikely(epoll_ctl(_evfd, EPOLL_CTL_ADD, _fd, &event) == -1)) 149 | panic("epoll_ctl"); 150 | } 151 | 152 | void ds_epoll_del_fd(int _evfd, int _fd) 153 | { 154 | if (unlikely(epoll_ctl(_evfd, EPOLL_CTL_DEL, _fd, NULL) == -1)) 155 | panic("epoll_ctl"); 156 | } 157 | 158 | void ds_produce_u64(int _fd) 159 | { 160 | uint64_t value = 1; 161 | __attribute__((unused)) int res = 0; 162 | 163 | res = ds_write(_fd, &value, sizeof(uint64_t)); 164 | 165 | return; 166 | } 167 | 168 | void ds_consume_u64(int _fd) 169 | { 170 | uint64_t value = 0; 171 | __attribute__((unused)) int res = 0; 172 | 173 | res = ds_read(_fd, &value, sizeof(uint64_t)); 174 | 175 | return; 176 | } 177 | 178 | int ds_try_consume_u64(int _fd) 179 | { 180 | uint64_t value = 0; 181 | int ret = 0; 182 | 183 | ret = ds_read(_fd, &value, sizeof(uint64_t)); 184 | 185 | return ret; 186 | } 187 | 188 | int ds_int_cmp(int _i1, int _i2) 189 | { 190 | if (_i1 > _i2) 191 | return 1; 192 | 193 | if (_i1 < _i2) 194 | return -1; 195 | 196 | return 0; 197 | } 198 | 199 | int ds_ptr_cmp(void* _p1, void* _p2) 200 | { 201 | if (_p1 > _p2) 202 | return 1; 203 | 204 | if (_p1 < _p2) 205 | return -1; 206 | 207 | return 0; 208 | } 209 | 210 | int ds_epoll_wait(int _epfd, struct epoll_event* _events, 211 | int _maxevents, int _timeout) 212 | { 213 | int ret = -1; 214 | 215 | do { 216 | ret = epoll_wait(_epfd, _events, _maxevents, _timeout); 217 | } while (unlikely(ret == -1 && errno == EINTR)); 218 | 219 | return ret; 220 | } 221 | 222 | ssize_t ds_read(int _fd, void* _buf, size_t _count) 223 | { 224 | ssize_t ret = -1; 225 | 226 | do { 227 | ret = read(_fd, _buf, _count); 228 | } while (unlikely(ret == -1 && errno == EINTR)); 229 | 230 | return ret; 231 | } 232 | 233 | ssize_t ds_write(int _fd, const void* _buf, size_t _count) 234 | { 235 | ssize_t ret = -1; 236 | 237 | do { 238 | ret = write(_fd, _buf, _count); 239 | } while (unlikely(ret == -1 && errno == EINTR)); 240 | 241 | return ret; 242 | } 243 | 244 | ssize_t ds_recvfrom(int _socket, void* restrict _buffer, size_t _length, 245 | struct pfcq_net_addr* _address) 246 | { 247 | ssize_t ret = -1; 248 | socklen_t addr_len = 0; 249 | 250 | do { 251 | switch (_address->family) 252 | { 253 | case AF_INET: 254 | addr_len = sizeof(struct sockaddr_in); 255 | ret = recvfrom(_socket, _buffer, _length, 0, 256 | (struct sockaddr*)&_address->addr.ip4, &addr_len); 257 | break; 258 | case AF_INET6: 259 | addr_len = sizeof(struct sockaddr_in6); 260 | ret = recvfrom(_socket, _buffer, _length, 0, (struct sockaddr*) 261 | &_address->addr.ip6, &addr_len); 262 | break; 263 | default: 264 | panic("Unknown address family"); 265 | break; 266 | } 267 | } while (unlikely(ret == -1 && errno == EINTR)); 268 | 269 | return ret; 270 | } 271 | 272 | ssize_t ds_sendto(int _socket, const void* _message, size_t _length, 273 | const struct pfcq_net_addr* _dest_addr) 274 | { 275 | ssize_t ret = -1; 276 | 277 | do { 278 | switch (_dest_addr->family) 279 | { 280 | case AF_INET: 281 | ret = sendto(_socket, _message, _length, 0, 282 | (const struct sockaddr*)&_dest_addr->addr.ip4, 283 | (socklen_t)sizeof(struct sockaddr_in)); 284 | break; 285 | case AF_INET6: 286 | ret = sendto(_socket, _message, _length, 0, 287 | (const struct sockaddr*)&_dest_addr->addr.ip6, 288 | (socklen_t)sizeof(struct sockaddr_in6)); 289 | break; 290 | default: 291 | panic("Unknown address family"); 292 | break; 293 | } 294 | } while (unlikely(ret == -1 && errno == EINTR)); 295 | 296 | return ret; 297 | } 298 | 299 | ssize_t ds_recv(int _sockfd, void* _buf, size_t _len) 300 | { 301 | ssize_t ret = -1; 302 | 303 | do { 304 | ret = recv(_sockfd, _buf, _len, 0); 305 | } while (unlikely(ret == -1 && errno == EINTR)); 306 | 307 | return ret; 308 | } 309 | 310 | ssize_t ds_send(int _sockfd, const void* _buf, size_t _len) 311 | { 312 | ssize_t ret = -1; 313 | 314 | do { 315 | ret = send(_sockfd, _buf, _len, 0); 316 | } while (unlikely(ret == -1 && errno == EINTR)); 317 | 318 | return ret; 319 | } 320 | 321 | int ds_eventfd(unsigned int _initval, int _flags) 322 | { 323 | int ret = -1; 324 | 325 | ret = eventfd(_initval, _flags); 326 | if (unlikely(ret == -1)) 327 | panic("eventfd"); 328 | 329 | return ret; 330 | } 331 | 332 | int ds_epoll_create(void) 333 | { 334 | int ret = -1; 335 | 336 | ret = epoll_create1(0); 337 | if (unlikely(ret == -1)) 338 | panic("epoll_create1"); 339 | 340 | return ret; 341 | } 342 | 343 | void ds_close(int _fd) 344 | { 345 | if (unlikely(close(_fd) == -1)) 346 | panic("close"); 347 | 348 | return; 349 | } 350 | 351 | int ds_af_to_pf(sa_family_t _af) 352 | { 353 | if (_af == AF_INET) 354 | return PF_INET; 355 | else if (_af == AF_INET6) 356 | return PF_INET6; 357 | else 358 | panic("Unknown address family"); 359 | } 360 | 361 | int ds_socket(int _domain, int _type, int _protocol) 362 | { 363 | int ret = -1; 364 | 365 | ret = socket(_domain, _type, _protocol); 366 | if (unlikely(ret == -1)) 367 | panic("socket"); 368 | 369 | return ret; 370 | } 371 | 372 | void ds_setsockopt(int _socket, int _level, int _option_name, 373 | const void* _option_value, socklen_t _option_len) 374 | { 375 | if (unlikely(setsockopt(_socket, _level, _option_name, 376 | _option_value, _option_len) == -1)) 377 | panic("setsockopt"); 378 | 379 | return; 380 | } 381 | 382 | void ds_bind(int _socket, const struct pfcq_net_addr* _address) 383 | { 384 | switch (_address->family) 385 | { 386 | case AF_INET: 387 | if (unlikely(bind(_socket, (const struct sockaddr*)&_address->addr.ip4, (socklen_t)sizeof(struct sockaddr_in)) == -1)) 388 | panic("bind"); 389 | break; 390 | case AF_INET6: 391 | if (unlikely(bind(_socket, (const struct sockaddr*)&_address->addr.ip6, (socklen_t)sizeof(struct sockaddr_in6)) == -1)) 392 | panic("bind"); 393 | break; 394 | default: 395 | panic("Unknown address family"); 396 | break; 397 | } 398 | 399 | return; 400 | } 401 | 402 | void ds_connect(int _socket, const struct pfcq_net_addr* _address) 403 | { 404 | switch (_address->family) 405 | { 406 | case AF_INET: 407 | if (unlikely(connect(_socket, (const struct sockaddr*)&_address->addr.ip4, (socklen_t)sizeof(struct sockaddr_in)) == -1)) 408 | panic("connect"); 409 | break; 410 | case AF_INET6: 411 | if (unlikely(connect(_socket, (const struct sockaddr*)&_address->addr.ip6, (socklen_t)sizeof(struct sockaddr_in6)) == -1)) 412 | panic("connect"); 413 | break; 414 | default: 415 | panic("Unknown address family"); 416 | break; 417 | } 418 | 419 | return; 420 | } 421 | 422 | void ds_inet_pton(const char* _src_addr, in_port_t _src_port, struct pfcq_net_addr* _dst) 423 | { 424 | if (inet_pton(AF_INET, _src_addr, (void*)&_dst->addr.ip4.sin_addr) == 1) 425 | { 426 | _dst->family = AF_INET; 427 | _dst->addr.ip4.sin_family = _dst->family; 428 | _dst->addr.ip4.sin_port = htons(_src_port); 429 | return; 430 | } 431 | 432 | if (inet_pton(AF_INET6, _src_addr, (void*)&_dst->addr.ip6.sin6_addr) == 1) 433 | { 434 | _dst->family = AF_INET6; 435 | _dst->addr.ip6.sin6_family = _dst->family; 436 | _dst->addr.ip6.sin6_port = htons(_src_port); 437 | return; 438 | } 439 | 440 | panic("inet_pton"); 441 | 442 | return; 443 | } 444 | 445 | void ds_inet_vlsmton(const struct pfcq_net_addr* _src, const char* _vlsm, struct pfcq_net_addr* _dst) 446 | { 447 | unsigned long int vlsm = pfcq_strtoul(_vlsm, 10); 448 | 449 | switch (_src->family) 450 | { 451 | case AF_INET: 452 | _dst->addr.ip4.sin_addr.s_addr = htonl((~0UL) << (32 - vlsm)); 453 | break; 454 | case AF_INET6: 455 | for (unsigned long int i = 0; i < vlsm; i++) 456 | { 457 | _dst->addr.ip6.sin6_addr.s6_addr[i / 8] |= (uint8_t)(1 << (i % 8)); 458 | } 459 | break; 460 | default: 461 | panic("Unknown address family"); 462 | break; 463 | } 464 | 465 | return; 466 | } 467 | 468 | uint64_t ds_hash_address(struct pfcq_net_addr* _address) 469 | { 470 | switch (_address->family) 471 | { 472 | case AF_INET: 473 | return pfcq_fast_hash((uint8_t*)&_address->addr.ip4.sin_addr, sizeof(struct in_addr), 0); 474 | break; 475 | case AF_INET6: 476 | return pfcq_fast_hash((uint8_t*)&_address->addr.ip6.sin6_addr, sizeof(struct in6_addr), 0); 477 | break; 478 | default: 479 | panic("Unknown address family"); 480 | break; 481 | } 482 | } 483 | 484 | void ds_set_sock_dscp(int _socket, int _domain, int _dscp) 485 | { 486 | switch (_domain) 487 | { 488 | case PF_INET: 489 | ds_setsockopt(_socket, IPPROTO_IP, IP_TOS, &_dscp, sizeof(int)); 490 | break; 491 | case PF_INET6: 492 | ds_setsockopt(_socket, IPPROTO_IPV6, IPV6_TCLASS, &_dscp, sizeof(int)); 493 | break; 494 | default: 495 | panic("Unknown socket domain"); 496 | break; 497 | } 498 | 499 | return; 500 | } 501 | 502 | int ds_timerfd_create(int _clockid, int _flags) 503 | { 504 | int ret = -1; 505 | 506 | ret = timerfd_create(_clockid, _flags); 507 | if (unlikely(ret == -1)) 508 | panic("timerfd_create"); 509 | 510 | return ret; 511 | } 512 | 513 | void ds_timerfd_settime(int _fd, uint64_t _period) 514 | { 515 | struct itimerspec ds_its; 516 | 517 | pfcq_zero(&ds_its, sizeof(struct itimerspec)); 518 | 519 | ds_its.it_value = pfcq_ns_to_timespec(_period); 520 | ds_its.it_interval.tv_sec = ds_its.it_value.tv_sec; 521 | ds_its.it_interval.tv_nsec = ds_its.it_value.tv_nsec; 522 | 523 | if (unlikely(timerfd_settime(_fd, 0, &ds_its, NULL) == -1)) 524 | panic("timerfd_settime"); 525 | 526 | return; 527 | } 528 | 529 | void ds_regcomp(regex_t* _preg, const char* _regex) 530 | { 531 | if (unlikely(regcomp(_preg, _regex, REG_EXTENDED | REG_NOSUB))) 532 | panic("regcomp"); 533 | } 534 | 535 | -------------------------------------------------------------------------------- /context.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "ini.h" 25 | #include "pfcq.h" 26 | #include "rb.h" 27 | #include "utils.h" 28 | #include "worker.h" 29 | 30 | #include "context.h" 31 | 32 | struct ds_ctx* ds_ctx_load(const char* _config_file) 33 | { 34 | int nsections = -1; 35 | char** sections = NULL; 36 | struct ds_ctx* ret = NULL; 37 | ds_cfg_t* cfg = NULL; 38 | 39 | ret = pfcq_alloc(sizeof(struct ds_ctx)); 40 | 41 | cfg = ds_cfg_open("dnsbalancer", _config_file); 42 | 43 | sections = ds_cfg_get_sections(cfg, &nsections); 44 | 45 | // enumerate sections 46 | for (size_t i = 0; i < (size_t)nsections; i++) 47 | { 48 | const char* stype = ds_cfg_try_get_cstr(cfg, sections[i], DS_CFG_KEY_TYPE); 49 | if (!stype) 50 | continue; 51 | 52 | if (pfcq_strlcmp(stype, DS_CFG_TYPE_FE) == 0) 53 | ret->nfes++; 54 | else if (pfcq_strlcmp(stype, DS_CFG_TYPE_FWD) == 0) 55 | ret->nfwds++; 56 | else if (pfcq_strlcmp(stype, DS_CFG_TYPE_ACT) == 0) 57 | ret->nacts++; 58 | else 59 | { 60 | inform("Section type: %s\n", stype); 61 | stop("Unknown section type"); 62 | } 63 | } 64 | 65 | pfcq_counter_init(&ret->epoch); 66 | ret->tk_fd = ds_timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 67 | ret->epoch_size = 68 | ds_cfg_get_u64(cfg, DS_CFG_SECTION_GENERAL, 69 | DS_CFG_KEY_TK_INTVL, DS_CFG_DEFAULT_TK_INTVL) * 1000000ULL; 70 | ds_timerfd_settime(ret->tk_fd, ret->epoch_size); 71 | 72 | // forwarders 73 | ret->fwds = pfcq_alloc(ret->nfwds * sizeof(struct ds_fwd)); 74 | for (size_t i = 0, c_x = 0; i < (size_t)nsections; i++) 75 | { 76 | size_t nparts = 0; 77 | char** parts = NULL; 78 | const char* cur = NULL; 79 | const char* stype = ds_cfg_try_get_cstr(cfg, sections[i], DS_CFG_KEY_TYPE); 80 | if (!stype || pfcq_strlcmp(stype, DS_CFG_TYPE_FWD) != 0) 81 | continue; 82 | 83 | // init 84 | pfcq_counter_init(&ret->fwds[c_x].c_q_id); 85 | pfcq_counter_set(&ret->fwds[c_x].c_q_id, 1); 86 | pfcq_counter_init(&ret->fwds[c_x].wdt_pending); 87 | 88 | // name 89 | ret->fwds[c_x].name = pfcq_strdup(sections[i]); 90 | 91 | // address 92 | cur = ds_cfg_get_cstr(cfg, sections[i], DS_CFG_KEY_ADDR); 93 | parts = pfcq_split_string(cur, DS_CFG_PARTS_DELIM, &nparts); 94 | if (unlikely(nparts != 2)) 95 | { 96 | inform("Address: %s\n", cur); 97 | stop("Incorrect address specified"); 98 | } 99 | ds_inet_pton(parts[0], (in_port_t)pfcq_strtoul(parts[1], 10), &ret->fwds[c_x].addr); 100 | pfcq_free_split_string(parts, nparts); 101 | 102 | // regular DSCP 103 | cur = ds_cfg_get_cstr(cfg, sections[i], DS_CFG_KEY_REG_DSCP); 104 | ret->fwds[c_x].reg_dscp = pfcq_strtoul(cur, 16); 105 | 106 | // watchdog DSCP 107 | cur = ds_cfg_get_cstr(cfg, sections[i], DS_CFG_KEY_WDT_DSCP); 108 | ret->fwds[c_x].wdt_dscp = pfcq_strtoul(cur, 16); 109 | 110 | // watchdog query 111 | cur = ds_cfg_get_cstr(cfg, sections[i], DS_CFG_KEY_WDT_QUERY); 112 | ret->fwds[c_x].wdt_query = pfcq_strdup(cur); 113 | 114 | // watchdog tries 115 | ret->fwds[c_x].wdt_tries = ds_cfg_get_uint(cfg, sections[i], DS_CFG_KEY_WDT_TRIES, DS_CFG_DEFAULT_WDT_TRIES); 116 | 117 | // forwarder is alive by default 118 | ret->fwds[c_x].alive = true; 119 | 120 | verbose("[fwd: %s] loaded\n", ret->fwds[c_x].name); 121 | 122 | c_x++; 123 | } 124 | 125 | // actions 126 | ret->acts = pfcq_alloc(ret->nacts * sizeof(struct ds_act)); 127 | for (size_t i = 0, c_x = 0; i < (size_t)nsections; i++) 128 | { 129 | const char* stype = ds_cfg_try_get_cstr(cfg, sections[i], DS_CFG_KEY_TYPE); 130 | if (!stype || pfcq_strlcmp(stype, DS_CFG_TYPE_ACT) != 0) 131 | continue; 132 | 133 | // name 134 | ret->acts[c_x].name = pfcq_strdup(sections[i]); 135 | 136 | char** keys = ds_cfg_get_keys(cfg, sections[i], (int*)&ret->acts[c_x].nact_items); 137 | if (!--ret->acts[c_x].nact_items) // minus "type" 138 | { 139 | inform("Section: %s\n", sections[i]); 140 | stop("Section is empty"); 141 | } 142 | 143 | ret->acts[c_x].act_items = pfcq_alloc(ret->acts[c_x].nact_items * sizeof(struct ds_act_item)); 144 | 145 | for (size_t j = 0, c_y = 0; j < ret->acts[c_x].nact_items + 1; j++) 146 | { 147 | size_t nparts = 0; 148 | char** parts = NULL; 149 | const char* cur = NULL; 150 | size_t naddr_parts = 0; 151 | char** addr_parts = NULL; 152 | size_t nact_parts = 0; 153 | char** act_parts = NULL; 154 | 155 | if (pfcq_strlcmp(keys[j], DS_CFG_KEY_TYPE) == 0) 156 | continue; 157 | 158 | cur = ds_cfg_get_cstr(cfg, sections[i], keys[j]); 159 | parts = pfcq_split_string(cur, DS_CFG_LIST_DELIM, &nparts); 160 | if (unlikely(nparts != 5)) 161 | { 162 | inform("Action: %s\n", cur); 163 | stop("Incorrect action specified"); 164 | } 165 | 166 | ret->acts[c_x].act_items[c_y].name = pfcq_strdup(keys[j]); 167 | 168 | // address/mask 169 | addr_parts = pfcq_split_string(parts[0], DS_CFG_PARTS_DELIM, &naddr_parts); 170 | if (unlikely(naddr_parts != 2)) 171 | { 172 | inform("Address: %s\n", parts[0]); 173 | stop("Incorrect address specified"); 174 | } 175 | ds_inet_pton(addr_parts[0], 0, &ret->acts[c_x].act_items[c_y].addr); 176 | ds_inet_vlsmton(&ret->acts[c_x].act_items[c_y].addr, 177 | addr_parts[1], &ret->acts[c_x].act_items[c_y].mask); 178 | pfcq_free_split_string(addr_parts, naddr_parts); 179 | 180 | // LDNS_RR_CLASS_FIRST (0) for "*" 181 | ret->acts[c_x].act_items[c_y].rr_class = ldns_get_rr_class_by_name(parts[1]); 182 | // LDNS_RR_TYPE_FIRST (0) for "*" 183 | ret->acts[c_x].act_items[c_y].rr_type = ldns_get_rr_type_by_name(parts[2]); 184 | ret->acts[c_x].act_items[c_y].expr = pfcq_strdup(parts[3]); 185 | ds_regcomp(&ret->acts[c_x].act_items[c_y].regex, ret->acts[c_x].act_items[c_y].expr); 186 | 187 | act_parts = pfcq_split_string(parts[4], DS_CFG_PARTS_DELIM, &nact_parts); 188 | if (pfcq_strlcmp(act_parts[0], DS_CFG_ACT_BALANCE) == 0) 189 | { 190 | size_t nfwd_parts = 0; 191 | char** fwd_parts = NULL; 192 | 193 | ret->acts[c_x].act_items[c_y].act_type = DS_ACT_BALANCE; 194 | 195 | if (pfcq_strlcmp(act_parts[1], DS_CFG_ACT_BALANCE_RR) == 0) 196 | { 197 | ret->acts[c_x].act_items[c_y].act_balance_type = DS_ACT_BALANCE_RR; 198 | } else if (pfcq_strlcmp(act_parts[1], DS_CFG_ACT_BALANCE_STICKY) == 0) 199 | { 200 | ret->acts[c_x].act_items[c_y].act_balance_type = DS_ACT_BALANCE_STICKY; 201 | } else 202 | { 203 | inform("Balancing type: %s\n", act_parts[1]); 204 | stop("Unknown balancing type specified"); 205 | } 206 | fwd_parts = pfcq_split_string(act_parts[2], DS_CFG_SUBLIST_DELIM, &nfwd_parts); 207 | ret->acts[c_x].act_items[c_y].act_balance_nfwds = nfwd_parts; 208 | ret->acts[c_x].act_items[c_y].act_balance_fwds = 209 | pfcq_alloc(ret->acts[c_x].act_items[c_y].act_balance_nfwds * 210 | sizeof(struct ds_fwd*)); 211 | for (size_t k = 0, c_z = 0; k < ret->acts[c_x].act_items[c_y].act_balance_nfwds; k++) 212 | for (size_t t = 0; t < ret->acts[c_x].act_items[c_y].act_balance_nfwds; t++) 213 | if (strncmp(ret->fwds[k].name, fwd_parts[t], strlen(fwd_parts[t])) == 0) 214 | { 215 | ret->acts[c_x].act_items[c_y].act_balance_fwds[c_z] = &ret->fwds[k]; 216 | c_z++; 217 | } 218 | pfcq_free_split_string(fwd_parts, nfwd_parts); 219 | pfcq_counter_init(&ret->acts[c_x].act_items[c_y].c_fwd); 220 | } else 221 | { 222 | inform("Action: %s\n", act_parts[0]); 223 | stop("Unknown action specified"); 224 | } 225 | pfcq_free_split_string(act_parts, nact_parts); 226 | 227 | pfcq_free_split_string(parts, nparts); 228 | 229 | c_y++; 230 | } 231 | ds_cfg_free_keys(keys); 232 | 233 | verbose("[act: %s] loaded\n", ret->acts[c_x].name); 234 | 235 | c_x++; 236 | } 237 | 238 | ret->wdt_fd = ds_timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 239 | ds_timerfd_settime(ret->wdt_fd, 240 | ds_cfg_get_u64(cfg, DS_CFG_SECTION_GENERAL, 241 | DS_CFG_KEY_WDT_INTVL, DS_CFG_DEFAULT_WDT_INTVL) * 1000000ULL); 242 | 243 | ret->fes = pfcq_alloc(ret->nfes * sizeof(struct ds_fe)); 244 | for (size_t i = 0, c_x = 0; i < (size_t)nsections; i++) 245 | { 246 | size_t nparts = 0; 247 | char** parts = NULL; 248 | const char* cur = NULL; 249 | const char* stype = ds_cfg_try_get_cstr(cfg, sections[i], DS_CFG_KEY_TYPE); 250 | if (!stype || pfcq_strlcmp(stype, DS_CFG_TYPE_FE) != 0) 251 | continue; 252 | 253 | // name 254 | ret->fes[c_x].name = pfcq_strdup(sections[i]); 255 | 256 | // address 257 | cur = ds_cfg_get_cstr(cfg, sections[i], DS_CFG_KEY_ADDR); 258 | parts = pfcq_split_string(cur, DS_CFG_PARTS_DELIM, &nparts); 259 | if (unlikely(nparts != 2)) 260 | { 261 | inform("Address: %s\n", cur); 262 | stop("Incorrect address specified"); 263 | } 264 | ds_inet_pton(parts[0], (in_port_t)pfcq_strtoul(parts[1], 10), &ret->fes[c_x].addr); 265 | pfcq_free_split_string(parts, nparts); 266 | 267 | // DSCP 268 | cur = ds_cfg_get_cstr(cfg, sections[i], DS_CFG_KEY_REG_DSCP); 269 | ret->fes[c_x].dscp = pfcq_strtoul(cur, 16); 270 | 271 | // actions 272 | cur = ds_cfg_get_cstr(cfg, sections[i], DS_CFG_KEY_ACTS); 273 | parts = pfcq_split_string(cur, DS_CFG_LIST_DELIM, &nparts); 274 | ret->fes[c_x].nacts = nparts; 275 | ret->fes[c_x].acts = pfcq_alloc(ret->fes[c_x].nacts * sizeof(struct ds_act)); 276 | for (size_t j = 0, c_y = 0; j < ret->nacts; j++) 277 | for (size_t k = 0; k < ret->fes[c_x].nacts; k++) 278 | if (strncmp(ret->acts[j].name, parts[k], strlen(parts[k])) == 0) 279 | { 280 | ret->fes[c_x].acts[c_y] = ret->acts[j]; 281 | c_y++; 282 | } 283 | pfcq_free_split_string(parts, nparts); 284 | 285 | verbose("[fe: %s] loaded\n", ret->fes[c_x].name); 286 | 287 | c_x++; 288 | } 289 | 290 | ds_cfg_free_sections(sections); 291 | 292 | ret->max_pkt_size = 293 | ds_cfg_get_uint(cfg, DS_CFG_SECTION_GENERAL, 294 | DS_CFG_KEY_MAX_PKT_SIZE, DS_CFG_DEFAULT_MAX_PKT_SIZE); 295 | if (unlikely(ret->max_pkt_size > LDNS_MAX_PACKETLEN)) 296 | { 297 | inform(DS_CFG_KEY_MAX_PKT_SIZE "=%zu exceeds max allowed packet size (%d)\n", ret->max_pkt_size, LDNS_MAX_PACKETLEN); 298 | stop("Stopping."); 299 | } 300 | 301 | pfcq_counter_init(&ret->in_flight); 302 | 303 | ret->poll_timeo = 304 | ds_cfg_get_uint(cfg, DS_CFG_SECTION_GENERAL, 305 | DS_CFG_KEY_POLL_TIMEO, DS_CFG_DEFAULT_POLL_TIMEO); 306 | 307 | ret->req_ttl = ds_cfg_get_u64(cfg, DS_CFG_SECTION_GENERAL, 308 | DS_CFG_KEY_REQ_TTL, DS_CFG_DEFAULT_REQ_TTL) * 1000000ULL; 309 | ret->gc_intvl = ds_cfg_get_u64(cfg, DS_CFG_SECTION_GENERAL, 310 | DS_CFG_KEY_GC_INTVL, 311 | DS_CFG_DEFAULT_GC_INTVL) * 1000000ULL; 312 | 313 | pfcq_counter_init(&ret->c_redirect_wrk); 314 | 315 | ret->nwrks = pfcq_hint_cpus(ds_cfg_get_int(cfg, DS_CFG_SECTION_GENERAL, DS_CFG_KEY_WRKS, DS_CFG_DEFAULT_WRKS)); 316 | ret->wrks = pfcq_alloc(ret->nwrks * sizeof(struct ds_wrk_ctx*)); 317 | for (size_t i = 0; i < ret->nwrks; i++) 318 | { 319 | ret->wrks[i] = pfcq_alloc(sizeof(struct ds_wrk_ctx)); 320 | 321 | ret->wrks[i]->ctx = ret; 322 | ret->wrks[i]->index = i; 323 | ret->wrks[i]->ready = ds_eventfd(0, 0); 324 | 325 | pthread_create(&ret->wrks[i]->id, NULL, ds_wrk, (void*)ret->wrks[i]); 326 | ds_consume_u64(ret->wrks[i]->ready); 327 | ds_close(ret->wrks[i]->ready); 328 | verbose("[ctx: %p, wrk: %zu/%#lx] started\n", (void*)ret, ret->wrks[i]->index, ret->wrks[i]->id); 329 | } 330 | 331 | ds_cfg_close(cfg); 332 | 333 | return ret; 334 | } 335 | 336 | void ds_ctx_unload(struct ds_ctx* _ctx) 337 | { 338 | for (size_t i = 0; i < _ctx->nwrks; i++) 339 | ds_produce_u64(_ctx->wrks[i]->ev_exit_fd); 340 | 341 | pfcq_counter_reset(&_ctx->c_redirect_wrk); 342 | 343 | for (size_t i = 0; i < _ctx->nwrks; i++) 344 | { 345 | pthread_join(_ctx->wrks[i]->id, NULL); 346 | verbose("[ctx: %p, wrk: %zu/%#lx] exited\n", (void*)_ctx, _ctx->wrks[i]->index, _ctx->wrks[i]->id); 347 | pfcq_free(_ctx->wrks[i]); 348 | } 349 | pfcq_free(_ctx->wrks); 350 | 351 | ds_close(_ctx->wdt_fd); 352 | 353 | pfcq_counter_reset(&_ctx->in_flight); 354 | 355 | for (size_t i = 0; i < _ctx->nfes; i++) 356 | { 357 | pfcq_free(_ctx->fes[i].name); 358 | pfcq_free(_ctx->fes[i].acts); 359 | } 360 | pfcq_free(_ctx->fes); 361 | 362 | for (size_t i = 0; i < _ctx->nacts; i++) 363 | { 364 | for (size_t j = 0; j < _ctx->acts[i].nact_items; j++) 365 | { 366 | pfcq_free(_ctx->acts[i].act_items[j].name); 367 | pfcq_free(_ctx->acts[i].act_items[j].expr); 368 | regfree(&_ctx->acts[i].act_items[j].regex); 369 | pfcq_free(_ctx->acts[i].act_items[j].act_balance_fwds); 370 | } 371 | pfcq_free(_ctx->acts[i].act_items); 372 | pfcq_free(_ctx->acts[i].name); 373 | } 374 | pfcq_free(_ctx->acts); 375 | 376 | for (size_t i = 0; i < _ctx->nfwds; i++) 377 | { 378 | pfcq_free(_ctx->fwds[i].wdt_query); 379 | pfcq_free(_ctx->fwds[i].name); 380 | pfcq_counter_reset(&_ctx->fwds[i].c_q_id); 381 | pfcq_counter_reset(&_ctx->fwds[i].wdt_pending); 382 | } 383 | pfcq_free(_ctx->fwds); 384 | 385 | ds_close(_ctx->tk_fd); 386 | pfcq_counter_reset(&_ctx->epoch); 387 | 388 | pfcq_free(_ctx); 389 | } 390 | 391 | -------------------------------------------------------------------------------- /handlers.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include "context.h" 22 | #include "dns.h" 23 | #include "pfcq.h" 24 | #include "rb.h" 25 | #include "utils.h" 26 | 27 | #include "handlers.h" 28 | 29 | int ds_wrk_acpt_handler(struct ds_fe_sk* _fe_sk, struct ds_wrk_ctx* _data) 30 | { 31 | struct ds_wrk_tsk* tsk = NULL; 32 | 33 | pfcq_counter_inc(&_data->ctx->in_flight); 34 | 35 | tsk = pfcq_alloc(sizeof(struct ds_wrk_tsk)); 36 | tsk->buf = pfcq_alloc(_data->ctx->max_pkt_size); 37 | tsk->addr.family = _fe_sk->fe->addr.family; 38 | tsk->buf_size = ds_recvfrom(_fe_sk->sk, tsk->buf, _data->ctx->max_pkt_size, 39 | &tsk->addr); 40 | if (unlikely(tsk->buf_size == -1)) 41 | { 42 | ds_tsk_free(tsk); 43 | goto err; 44 | } 45 | 46 | tsk->type = DS_TSK_REG; 47 | // save frontend address separately 48 | // in case response will be redirected 49 | // through new context on reload 50 | tsk->orig_fe_sk = _fe_sk; 51 | tsk->orig_fe_addr = tsk->orig_fe_sk->fe->addr; 52 | 53 | TAILQ_INSERT_TAIL(&_data->prep_queue, tsk, tailq); 54 | ds_produce_u64(_data->ev_prep_fd); 55 | 56 | goto out; 57 | 58 | err: 59 | pfcq_counter_dec(&_data->ctx->in_flight); 60 | 61 | out: 62 | return 0; 63 | } 64 | 65 | int ds_wrk_prep_handler(int _fd, struct ds_wrk_ctx* _data) 66 | { 67 | struct ds_wrk_tsk* tsk = NULL; 68 | 69 | ds_consume_u64(_fd); 70 | 71 | tsk = TAILQ_FIRST(&_data->prep_queue); 72 | TAILQ_REMOVE(&_data->prep_queue, tsk, tailq); 73 | 74 | if (unlikely(!ds_tsk_buf_to_pkt(tsk))) 75 | { 76 | ds_tsk_free(tsk); 77 | goto err; 78 | } 79 | 80 | for (size_t i = 0; i < _data->ctx->nacts; i++) 81 | { 82 | for (size_t j = 0; j < _data->ctx->acts[i].nact_items; j++) 83 | { 84 | struct ds_act_item* c_act_item = &_data->ctx->acts[i].act_items[j]; 85 | // TODO: other matchers 86 | switch (c_act_item->act_type) 87 | { 88 | case DS_ACT_BALANCE: 89 | ds_tsk_get_fwd(tsk, _data->fwd_sk_set, c_act_item); 90 | if (unlikely(!tsk->fwd)) 91 | { 92 | ds_tsk_free(tsk); 93 | goto err; 94 | } 95 | if (unlikely(ds_tsk_buf_parse(_data, tsk, DS_PKT_REQ) == -1)) 96 | { 97 | ds_tsk_free(tsk); 98 | goto err; 99 | } 100 | TAILQ_INSERT_TAIL(&_data->fwd_queue, tsk, tailq); 101 | ds_produce_u64(_data->ev_fwd_fd); 102 | goto out; 103 | break; 104 | default: 105 | // TODO: unknown action 106 | break; 107 | } 108 | } 109 | } 110 | 111 | err: 112 | pfcq_counter_dec(&_data->ctx->in_flight); 113 | 114 | out: 115 | return 0; 116 | } 117 | 118 | int ds_wrk_fwd_handler(int _fd, struct ds_wrk_ctx* _data) 119 | { 120 | struct ds_wrk_tsk* tsk = NULL; 121 | 122 | ds_consume_u64(_fd); 123 | 124 | tsk = TAILQ_FIRST(&_data->fwd_queue); 125 | TAILQ_REMOVE(&_data->fwd_queue, tsk, tailq); 126 | 127 | if (unlikely(ds_send(tsk->fwd_sk->sk, tsk->buf, tsk->buf_size) == -1)) 128 | { 129 | // TODO: stats 130 | goto out; 131 | } 132 | 133 | rb_insert(_data->tracking, (void*)tsk); 134 | 135 | out: 136 | pfcq_counter_dec(&_data->ctx->in_flight); 137 | 138 | return 0; 139 | } 140 | 141 | int ds_wrk_obt_handler(struct ds_fwd_sk* _fwd_sk, struct ds_wrk_ctx* _data) 142 | { 143 | struct ds_wrk_tsk* tsk = NULL; 144 | struct ds_wrk_tsk* found = NULL; 145 | struct ds_wrk_ctx* wrk_next = NULL; 146 | size_t wrk_idx_next = 0; 147 | 148 | pfcq_counter_inc(&_data->ctx->in_flight); 149 | 150 | tsk = pfcq_alloc(sizeof(struct ds_wrk_tsk)); 151 | tsk->buf = pfcq_alloc(_data->ctx->max_pkt_size); 152 | tsk->buf_size = ds_recv(_fwd_sk->sk, tsk->buf, _data->ctx->max_pkt_size); 153 | if (unlikely(tsk->buf_size == -1)) 154 | { 155 | ds_tsk_free(tsk); 156 | goto err; 157 | } 158 | 159 | if (unlikely(!ds_tsk_buf_to_pkt(tsk))) 160 | { 161 | ds_tsk_free(tsk); 162 | goto err; 163 | } 164 | 165 | if (unlikely(ds_tsk_buf_parse(_data, tsk, DS_PKT_REP)) == -1) 166 | { 167 | ds_tsk_free(tsk); 168 | goto err; 169 | } 170 | 171 | tsk->fwd_sk = _fwd_sk; 172 | 173 | found = rb_find(_data->tracking, tsk); 174 | if (unlikely(!found)) 175 | { 176 | ds_tsk_free(tsk); 177 | goto err; 178 | } 179 | 180 | rb_delete(_data->tracking, found); 181 | 182 | tsk->type = found->type; 183 | memcpy(tsk->buf, &found->orig_id, sizeof(uint16_t)); 184 | tsk->addr = found->addr; 185 | tsk->orig_fe_sk = found->orig_fe_sk; 186 | tsk->orig_fe_addr = found->orig_fe_addr; 187 | tsk->fwd = found->fwd; 188 | tsk->fwd_sk_addr = found->fwd_sk_addr; 189 | 190 | pfcq_counter_reset(&found->epoch); 191 | ds_tsk_free(found); 192 | 193 | tsk->redirected = _data->ctx->redirect; 194 | if (tsk->redirected) 195 | { 196 | wrk_idx_next = pfcq_counter_get_inc_mod(&_data->ctx->ctx_next->c_redirect_wrk, 197 | _data->ctx->ctx_next->nwrks, 0); 198 | wrk_next = _data->ctx->ctx_next->wrks[wrk_idx_next]; 199 | 200 | pfcq_counter_dec(&_data->ctx->in_flight); 201 | pfcq_counter_inc(&_data->ctx->ctx_next->in_flight); 202 | } else 203 | wrk_next = _data; 204 | 205 | if (likely(tsk->type == DS_TSK_REG)) 206 | { 207 | // response to client query 208 | pfcq_spin_lock(&wrk_next->rep_queue_lock); 209 | TAILQ_INSERT_TAIL(&wrk_next->rep_queue, tsk, tailq); 210 | pfcq_spin_unlock(&wrk_next->rep_queue_lock); 211 | ds_produce_u64(wrk_next->ev_rep_fd); 212 | } else if (likely(tsk->type == DS_TSK_WDT)) 213 | { 214 | // response to watchdog 215 | pfcq_spin_lock(&wrk_next->wdt_rep_queue_lock); 216 | TAILQ_INSERT_TAIL(&wrk_next->wdt_rep_queue, tsk, tailq); 217 | pfcq_spin_unlock(&wrk_next->wdt_rep_queue_lock); 218 | ds_produce_u64(wrk_next->ev_wdt_rep_fd); 219 | } else 220 | panic("Unknown task type"); 221 | 222 | goto out; 223 | 224 | err: 225 | pfcq_counter_dec(&_data->ctx->in_flight); 226 | 227 | out: 228 | return 0; 229 | } 230 | 231 | int ds_wrk_rep_handler(int _fd, struct ds_wrk_ctx* _data) 232 | { 233 | int sk = -1; 234 | struct ds_wrk_tsk* tsk = NULL; 235 | 236 | ds_consume_u64(_fd); 237 | 238 | pfcq_spin_lock(&_data->rep_queue_lock); 239 | tsk = TAILQ_FIRST(&_data->rep_queue); 240 | TAILQ_REMOVE(&_data->rep_queue, tsk, tailq); 241 | pfcq_spin_unlock(&_data->rep_queue_lock); 242 | 243 | if (unlikely(tsk->redirected)) 244 | { 245 | struct rb_traverser iter; 246 | struct ds_fe_sk* cur = NULL; 247 | bool found = false; 248 | 249 | rb_t_init(&iter, _data->fe_sk_set); 250 | cur = rb_t_first(&iter, _data->fe_sk_set); 251 | do { 252 | if (pfcq_net_addr_cmp(&cur->fe->addr, &tsk->orig_fe_addr)) 253 | { 254 | sk = cur->sk; 255 | found = true; 256 | break; 257 | } 258 | } while (likely((cur = rb_t_next(&iter)) != NULL)); 259 | 260 | if (!found) 261 | goto out; 262 | } else 263 | { 264 | sk = tsk->orig_fe_sk->sk; 265 | } 266 | 267 | if (unlikely(ds_sendto(sk, tsk->buf, tsk->buf_size, &tsk->addr) == -1)) 268 | { 269 | // TODO: stats 270 | __noop; 271 | } 272 | 273 | out: 274 | ds_tsk_free(tsk); 275 | 276 | pfcq_counter_dec(&_data->ctx->in_flight); 277 | 278 | return 0; 279 | } 280 | 281 | int ds_wrk_exit_handler(int _fd, struct ds_wrk_ctx* _data) 282 | { 283 | struct rb_traverser iter; 284 | struct ds_fe_sk* cur_fe_sk = NULL; 285 | 286 | ds_consume_u64(_fd); 287 | 288 | verbose("[ctx: %p, wrk: %zu/%#lx] exiting...\n", (void*)_data->ctx, _data->index, _data->id); 289 | 290 | rb_t_init(&iter, _data->fe_sk_set); 291 | cur_fe_sk = rb_t_first(&iter, _data->fe_sk_set); 292 | do { 293 | ds_close(cur_fe_sk->sk); 294 | } while (likely((cur_fe_sk = rb_t_next(&iter)) != NULL)); 295 | 296 | ds_epoll_del_fd(_data->wrk_fd, _data->ctx->wdt_fd); 297 | 298 | _data->poll_timeo = _data->ctx->poll_timeo; 299 | 300 | return 0; 301 | } 302 | 303 | static uint64_t __ds_epoch_diff_ns(struct pfcq_counter* _epoch1, 304 | struct pfcq_counter* _epoch2, 305 | uint64_t _epoch_size) 306 | { 307 | uint64_t e1 = 0; 308 | uint64_t e2 = 0; 309 | 310 | e1 = (uint64_t)pfcq_counter_get(_epoch1); 311 | e2 = (uint64_t)pfcq_counter_get(_epoch2); 312 | return (e2 - e1) * _epoch_size; 313 | } 314 | 315 | int ds_wrk_gc_handler(int _fd, struct ds_wrk_ctx* _data) 316 | { 317 | struct pfcq_counter now; 318 | struct rb_traverser iter; 319 | struct rb_traverser iter_f; 320 | struct ds_wrk_tsk* cur = NULL; 321 | struct ds_wrk_tsk* cur_f = NULL; 322 | struct rb_table* found = NULL; 323 | 324 | pfcq_zero(&now, sizeof(struct timespec)); 325 | pfcq_zero(&iter, sizeof(struct rb_traverser)); 326 | pfcq_zero(&iter_f, sizeof(struct rb_traverser)); 327 | 328 | ds_consume_u64(_fd); 329 | 330 | pfcq_counter_init(&now); 331 | pfcq_counter_set(&now, pfcq_counter_get(&_data->ctx->epoch)); 332 | 333 | rb_t_init(&iter, _data->tracking); 334 | cur = rb_t_first(&iter, _data->tracking); 335 | if (likely(cur)) 336 | { 337 | found = rb_create(ds_tsk_cmp, NULL, &ds_rb_allocator); 338 | 339 | do { 340 | if (unlikely( 341 | __ds_epoch_diff_ns(&cur->epoch, 342 | &_data->ctx->epoch, 343 | _data->ctx->epoch_size) > _data->ctx->req_ttl)) 344 | { 345 | rb_insert(found, cur); 346 | } 347 | } while (likely((cur = rb_t_next(&iter)) != NULL)); 348 | 349 | rb_t_init(&iter_f, found); 350 | cur_f = rb_t_first(&iter_f, found); 351 | if (likely(cur_f)) 352 | { 353 | do { 354 | rb_delete(_data->tracking, cur_f); 355 | } while (likely((cur_f = rb_t_next(&iter_f)) != NULL)); 356 | } 357 | rb_destroy(found, ds_rb_tsk_free); 358 | } 359 | 360 | return 0; 361 | } 362 | 363 | int ds_wrk_wdt_req_handler(int _fd, struct ds_wrk_ctx* _data) 364 | { 365 | ldns_rr* rr = NULL; 366 | int push_res = -1; 367 | struct ds_wrk_tsk* tsk = NULL; 368 | struct ds_fwd_sk* cur_fwd_wdt_sk = NULL; 369 | struct rb_traverser iter; 370 | 371 | if (unlikely(ds_try_consume_u64(_fd) == -1 && errno == EAGAIN)) 372 | return 0; 373 | 374 | rb_t_init(&iter, _data->fwd_wdt_sk_set); 375 | cur_fwd_wdt_sk = rb_t_first(&iter, _data->fwd_wdt_sk_set); 376 | do 377 | { 378 | if (unlikely(pfcq_counter_reset_if_gt(&cur_fwd_wdt_sk->fwd->wdt_pending, 379 | cur_fwd_wdt_sk->fwd->wdt_tries))) 380 | { 381 | if (unlikely(cur_fwd_wdt_sk->fwd->alive)) 382 | { 383 | verbose("Forwarder %s became unreachable\n", cur_fwd_wdt_sk->fwd->name); 384 | cur_fwd_wdt_sk->fwd->alive = false; 385 | } 386 | } 387 | 388 | pfcq_counter_inc(&_data->ctx->in_flight); 389 | 390 | tsk = pfcq_alloc(sizeof(struct ds_wrk_tsk)); 391 | 392 | tsk->pkt = ldns_pkt_new(); 393 | if (unlikely(!tsk->pkt)) 394 | goto fail; 395 | 396 | ldns_pkt_set_random_id(tsk->pkt); 397 | ldns_pkt_set_qr(tsk->pkt, 0); 398 | ldns_pkt_set_opcode(tsk->pkt, LDNS_PACKET_QUERY); 399 | ldns_pkt_set_tc(tsk->pkt, 0); 400 | ldns_pkt_set_rd(tsk->pkt, 1); 401 | if (unlikely(ldns_rr_new_question_frm_str(&rr, cur_fwd_wdt_sk->fwd->wdt_query, NULL, NULL) != LDNS_STATUS_OK)) 402 | goto fail; 403 | push_res = ldns_pkt_push_rr(tsk->pkt, LDNS_SECTION_QUESTION, rr); 404 | if (unlikely(push_res != LDNS_STATUS_OK && push_res != LDNS_STATUS_EMPTY_LABEL)) 405 | goto fail; 406 | 407 | if (unlikely(ldns_pkt2wire((uint8_t**)&tsk->buf, tsk->pkt, (size_t*)&tsk->buf_size) != LDNS_STATUS_OK)) 408 | goto fail; 409 | 410 | tsk->type = DS_TSK_WDT; 411 | tsk->fwd_sk = cur_fwd_wdt_sk; 412 | tsk->fwd_sk_addr = tsk->fwd_sk->fwd->addr; 413 | 414 | if (unlikely(ds_tsk_buf_parse(_data, tsk, DS_PKT_REQ) == -1)) 415 | goto fail; 416 | 417 | pfcq_counter_inc(&tsk->fwd_sk->fwd->wdt_pending); 418 | 419 | TAILQ_INSERT_TAIL(&_data->fwd_queue, tsk, tailq); 420 | ds_produce_u64(_data->ev_fwd_fd); 421 | 422 | goto ok; 423 | 424 | fail: 425 | ds_tsk_free(tsk); 426 | pfcq_counter_dec(&_data->ctx->in_flight); 427 | 428 | ok: 429 | continue; 430 | } while (likely((cur_fwd_wdt_sk = rb_t_next(&iter)) != NULL)); 431 | 432 | return 0; 433 | } 434 | 435 | int ds_wrk_wdt_rep_handler(int _fd, struct ds_wrk_ctx* _data) 436 | { 437 | struct ds_wrk_tsk* tsk = NULL; 438 | struct ds_fwd_sk* fwd_wdt_sk = NULL; 439 | 440 | ds_consume_u64(_fd); 441 | 442 | pfcq_spin_lock(&_data->wdt_rep_queue_lock); 443 | tsk = TAILQ_FIRST(&_data->wdt_rep_queue); 444 | TAILQ_REMOVE(&_data->wdt_rep_queue, tsk, tailq); 445 | pfcq_spin_unlock(&_data->wdt_rep_queue_lock); 446 | 447 | if (unlikely(tsk->redirected)) 448 | { 449 | struct rb_traverser iter; 450 | struct ds_fwd_sk* cur = NULL; 451 | bool found = false; 452 | 453 | rb_t_init(&iter, _data->fwd_wdt_sk_set); 454 | cur = rb_t_first(&iter, _data->fwd_wdt_sk_set); 455 | do { 456 | if (pfcq_net_addr_cmp(&cur->fwd->addr, &tsk->fwd_sk_addr)) 457 | { 458 | fwd_wdt_sk = cur; 459 | found = true; 460 | break; 461 | } 462 | } while (likely((cur = rb_t_next(&iter)) != NULL)); 463 | 464 | if (!found) 465 | goto out; 466 | } else 467 | { 468 | fwd_wdt_sk = tsk->fwd_sk; 469 | } 470 | 471 | pfcq_counter_reset(&fwd_wdt_sk->fwd->wdt_pending); 472 | if (unlikely(!fwd_wdt_sk->fwd->alive)) 473 | { 474 | fwd_wdt_sk->fwd->alive = true; 475 | verbose("Forwarder %s became reachable\n", fwd_wdt_sk->fwd->name); 476 | } 477 | 478 | out: 479 | ds_tsk_free(tsk); 480 | 481 | pfcq_counter_dec(&_data->ctx->in_flight); 482 | 483 | return 0; 484 | } 485 | 486 | int ds_wrk_tk_handler(int _fd, struct ds_wrk_ctx* _data) 487 | { 488 | if (unlikely(ds_try_consume_u64(_fd) == -1 && errno == EAGAIN)) 489 | return 0; 490 | 491 | pfcq_counter_inc(&_data->ctx->epoch); 492 | 493 | return 0; 494 | } 495 | 496 | -------------------------------------------------------------------------------- /pfcq.c: -------------------------------------------------------------------------------- 1 | /* vim: set tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab */ 2 | 3 | /* 4 | * dnsbalancer - daemon to balance UDP DNS requests over DNS servers 5 | * Initially created under patronage of Lanet Network 6 | * Programmed by Oleksandr Natalenko , 2015-2017 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, version 3 of the License. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #define UNW_LOCAL_ONLY 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "pfcq.h" 38 | 39 | #define STACKITEM_NAME_SIZE 256 40 | #define STACKITEM_PREFIX_SYSLOG "%ju) %s+%lx: ip = %lx, sp = %lx\n" 41 | #define STACKITEM_PREFIX_STDERR "\t"STACKITEM_PREFIX_SYSLOG 42 | #define WARNING_SUFFIX_SYSLOG "Warning #%d" 43 | #define WARNING_SUFFIX_STDERR WARNING_SUFFIX_SYSLOG", " 44 | 45 | static int pfcq_be_verbose; 46 | static int pfcq_do_debug; 47 | static int pfcq_warnings_count; 48 | static int pfcq_use_syslog; 49 | static pthread_spinlock_t pfcq_warning_ordering_lock; 50 | 51 | static inline uint64_t __pfcq_timespec_to_ns(struct timespec _timestamp) __attribute__((always_inline)); 52 | 53 | void __pfcq_debug(int _direct, const char* _format, ...) 54 | { 55 | va_list arguments; 56 | 57 | va_start(arguments, _format); 58 | if (_direct || pfcq_be_verbose || pfcq_do_debug) 59 | { 60 | if (pfcq_use_syslog) 61 | vsyslog(LOG_DEBUG, _format, arguments); 62 | else 63 | vfprintf(stderr, _format, arguments); 64 | } 65 | va_end(arguments); 66 | 67 | return; 68 | } 69 | 70 | static void show_stacktrace(void) 71 | { 72 | unw_cursor_t cursor; 73 | unw_context_t uc; 74 | unw_word_t ip = 0; 75 | unw_word_t sp = 0; 76 | unw_word_t offp = 0; 77 | size_t index = 0; 78 | char name[STACKITEM_NAME_SIZE]; 79 | 80 | pfcq_zero(&cursor, sizeof(unw_cursor_t)); 81 | pfcq_zero(&uc, sizeof(unw_context_t)); 82 | pfcq_zero(name, STACKITEM_NAME_SIZE); 83 | 84 | unw_getcontext(&uc); 85 | unw_init_local(&cursor, &uc); 86 | 87 | __pfcq_debug(1, "Stacktrace:\n"); 88 | while (unw_step(&cursor) > 0) 89 | { 90 | unw_get_proc_name(&cursor, name, STACKITEM_NAME_SIZE, &offp); 91 | unw_get_reg(&cursor, UNW_REG_IP, &ip); 92 | unw_get_reg(&cursor, UNW_REG_SP, &sp); 93 | __pfcq_debug(1, pfcq_use_syslog ? STACKITEM_PREFIX_SYSLOG : STACKITEM_PREFIX_STDERR, 94 | ++index, name, (long)offp, (long)ip, (long)sp); 95 | } 96 | 97 | return; 98 | } 99 | 100 | void __pfcq_warning(const char* _message, const int _errno, const char* _file, int _line, int _direct) 101 | { 102 | pfcq_spin_lock(&pfcq_warning_ordering_lock); 103 | if (likely(_direct)) 104 | { 105 | pfcq_warnings_count++; 106 | __pfcq_debug(1, pfcq_use_syslog ? WARNING_SUFFIX_SYSLOG : WARNING_SUFFIX_STDERR, pfcq_warnings_count); 107 | } 108 | __pfcq_debug(1, "File=%s, line=%d\n", _file, _line); 109 | __pfcq_debug(1, "%s: %s\n", _message, strerror(_errno)); 110 | show_stacktrace(); 111 | pfcq_spin_unlock(&pfcq_warning_ordering_lock); 112 | 113 | return; 114 | } 115 | 116 | void __pfcq_fail(const char* _message, const int _errno) 117 | { 118 | __pfcq_debug(1, "%s: %s\n", _message, strerror(_errno)); 119 | 120 | return; 121 | } 122 | 123 | void __pfcq_stop(const int _exit_code, const char* _message) 124 | { 125 | if (likely(_message)) 126 | __pfcq_debug(1, "%s\n", _message); 127 | exit(_exit_code); 128 | } 129 | 130 | void __pfcq_panic(const char* _message, const int _errno, const char* _file, int _line) 131 | { 132 | __pfcq_warning(_message, _errno, _file, _line, 0); 133 | __pfcq_stop(EX_SOFTWARE, NULL); 134 | } 135 | 136 | void pfcq_debug_init(int _verbose, int _debug, int _syslog) 137 | { 138 | pfcq_be_verbose = _verbose; 139 | pfcq_do_debug = _debug; 140 | pfcq_use_syslog = _syslog; 141 | pfcq_warnings_count = 0; 142 | pfcq_spin_init(&pfcq_warning_ordering_lock); 143 | if (pfcq_use_syslog) 144 | openlog(NULL, LOG_PID, LOG_DAEMON); 145 | 146 | return; 147 | } 148 | 149 | void pfcq_debug_done(void) 150 | { 151 | pfcq_spin_done(&pfcq_warning_ordering_lock); 152 | if (pfcq_use_syslog) 153 | closelog(); 154 | 155 | return; 156 | } 157 | 158 | void pfcq_memset_g(void* _data, int _byte, size_t _size) 159 | { 160 | volatile unsigned char* data = _data; 161 | 162 | while (_size--) 163 | { 164 | *data++ = (unsigned char)_byte; 165 | } 166 | 167 | return; 168 | } 169 | 170 | void* pfcq_alloc(size_t _size) 171 | { 172 | void* ret = NULL; 173 | 174 | if (unlikely(_size == 0)) 175 | panic("_size"); 176 | 177 | ret = calloc(1, _size); 178 | if (unlikely(!ret)) 179 | panic("calloc"); 180 | 181 | return ret; 182 | } 183 | 184 | void* pfcq_realloc(void* _old_pointer, size_t _new_size) 185 | { 186 | void* ret = NULL; 187 | 188 | if (unlikely(!_old_pointer)) 189 | panic("_old_pointer"); 190 | 191 | if (unlikely(_new_size == 0)) 192 | panic("new_size"); 193 | 194 | ret = realloc(_old_pointer, _new_size); 195 | if (unlikely(!ret)) 196 | panic("realloc"); 197 | 198 | return ret; 199 | } 200 | 201 | void __pfcq_free(void** _pointer) 202 | { 203 | void* p = NULL; 204 | 205 | if (unlikely(!_pointer)) 206 | panic("_pointer"); 207 | 208 | p = *_pointer; 209 | if (unlikely(!p)) 210 | panic("p"); 211 | 212 | free(p); 213 | *_pointer = NULL; 214 | 215 | return; 216 | } 217 | 218 | int __pfcq_strlcmp(const char* _s1, const char* _s2) 219 | { 220 | return strncmp(_s1, _s2, strlen(_s2)); 221 | } 222 | 223 | int pfcq_isnumber(const char* _string) 224 | { 225 | while (likely(*_string)) 226 | { 227 | char current_char = *_string++; 228 | if (unlikely(isdigit(current_char) == 0)) 229 | return 0; 230 | } 231 | 232 | return 1; 233 | } 234 | 235 | char* pfcq_mstring(const char* _format, ...) 236 | { 237 | va_list arguments; 238 | char* ret = NULL; 239 | 240 | va_start(arguments, _format); 241 | int length = vsnprintf(NULL, 0, _format, arguments); 242 | va_end(arguments); 243 | 244 | if (unlikely(length < 0)) 245 | return ret; 246 | 247 | ret = pfcq_alloc(length + 1); 248 | 249 | va_start(arguments, _format); 250 | vsprintf(ret, _format, arguments); 251 | va_end(arguments); 252 | 253 | return ret; 254 | } 255 | 256 | char* pfcq_strdup(const char* _string) 257 | { 258 | return pfcq_mstring("%s", _string); 259 | } 260 | 261 | char* pfcq_cstring(char* _left, const char* _right) 262 | { 263 | size_t left_length = strlen(_left); 264 | size_t right_length = strlen(_right); 265 | 266 | char* ret = pfcq_realloc(_left, left_length + right_length + 1); 267 | memcpy(ret + left_length, _right, right_length); 268 | ret[left_length + right_length] = '\0'; 269 | 270 | return ret; 271 | } 272 | 273 | char* pfcq_bstring(const char* _buffer, size_t _buffer_size) 274 | { 275 | char* ret = NULL; 276 | 277 | ret = pfcq_alloc(_buffer_size + 1); 278 | memcpy(ret, _buffer, _buffer_size); 279 | ret[_buffer_size] = '\0'; 280 | 281 | return ret; 282 | } 283 | 284 | char** pfcq_split_string(const char* _string, const char* _delimiter, size_t* _size) 285 | { 286 | char* iter = pfcq_strdup(_string); 287 | char* iter_p = iter; 288 | char* part = NULL; 289 | char** ret = NULL; 290 | size_t nparts = 0; 291 | 292 | while (likely(part = strsep(&iter, _delimiter))) 293 | { 294 | if (unlikely(!ret)) 295 | ret = pfcq_alloc(sizeof(char*)); 296 | else 297 | ret = pfcq_realloc(ret, (nparts + 1) * sizeof(char*)); 298 | 299 | ret[nparts] = pfcq_strdup(part); 300 | 301 | nparts++; 302 | } 303 | 304 | pfcq_free(iter_p); 305 | 306 | *_size = nparts; 307 | 308 | return ret; 309 | } 310 | 311 | void pfcq_free_split_string(char** _parts, size_t _nparts) 312 | { 313 | for (size_t i = 0; i < _nparts; i++) 314 | pfcq_free(_parts[i]); 315 | pfcq_free(_parts); 316 | 317 | return; 318 | } 319 | 320 | unsigned long int pfcq_strtoul(const char* _nptr, int _base) 321 | { 322 | char* endptr = NULL; 323 | unsigned long int ret = 0; 324 | 325 | ret = strtoul(_nptr, &endptr, _base); 326 | if (unlikely(*endptr != '\0' || (ret == ULONG_MAX && errno == ERANGE))) 327 | panic("strtoul"); 328 | 329 | return ret; 330 | } 331 | 332 | unsigned short int pfcq_hint_cpus(int _hint) 333 | { 334 | unsigned short int ret = 0; 335 | int res = 0; 336 | 337 | if (_hint < 1) 338 | { 339 | res = sysconf(_SC_NPROCESSORS_ONLN); 340 | if (unlikely(res == -1)) 341 | ret = 1; 342 | else 343 | ret = (unsigned short int)res; 344 | } else 345 | ret = (unsigned short int)_hint; 346 | 347 | return ret; 348 | } 349 | 350 | int64_t pfcq_timespec_diff_ns(struct timespec _timestamp1, struct timespec _timestamp2) 351 | { 352 | uint64_t ns1 = __pfcq_timespec_to_ns(_timestamp1); 353 | uint64_t ns2 = __pfcq_timespec_to_ns(_timestamp2); 354 | return ns2 - ns1; 355 | } 356 | 357 | struct timeval pfcq_us_to_timeval(uint64_t _us) 358 | { 359 | struct timeval ret; 360 | 361 | ret.tv_sec = _us / 1000000ULL; 362 | ret.tv_usec = _us - ret.tv_sec * 1000000ULL; 363 | 364 | return ret; 365 | } 366 | 367 | void pfcq_sleep(uint64_t _ns) 368 | { 369 | struct timespec time_to_sleep = pfcq_ns_to_timespec(_ns); 370 | 371 | while (likely(nanosleep(&time_to_sleep, &time_to_sleep) == -1 && errno == EINTR)) 372 | continue; 373 | } 374 | 375 | struct timespec pfcq_ns_to_timespec(uint64_t _ns) 376 | { 377 | struct timespec ret; 378 | 379 | ret.tv_sec = _ns / 1000000000ULL; 380 | ret.tv_nsec = _ns - ret.tv_sec * 1000000000ULL; 381 | 382 | return ret; 383 | } 384 | 385 | void pfcq_spin_init(pthread_spinlock_t* _lock) 386 | { 387 | if (unlikely(pthread_spin_init(_lock, PTHREAD_PROCESS_PRIVATE) != 0)) 388 | panic("pthread_spin_init"); 389 | } 390 | 391 | void pfcq_spin_lock(pthread_spinlock_t* _lock) 392 | { 393 | if (unlikely(pthread_spin_lock(_lock) != 0)) 394 | panic("pthread_spin_lock"); 395 | } 396 | 397 | void pfcq_spin_unlock(pthread_spinlock_t* _lock) 398 | { 399 | if (unlikely(pthread_spin_unlock(_lock) != 0)) 400 | panic("pthread_spin_unlock"); 401 | } 402 | 403 | void pfcq_spin_done(pthread_spinlock_t* _lock) 404 | { 405 | if (unlikely(pthread_spin_destroy(_lock) != 0)) 406 | panic("pthread_spin_destroy"); 407 | } 408 | 409 | uint64_t pfcq_fast_hash(const uint8_t* _data, size_t _data_size, uint64_t _seed) 410 | { 411 | uint64_t ret = 0xcbf29ce484222325; 412 | 413 | for (size_t i = 0; i < _data_size; i++) 414 | { 415 | ret ^= _data[i]; 416 | ret *= 0x100000001b3; 417 | } 418 | 419 | return ret ^ _seed; 420 | } 421 | 422 | static inline uint64_t __pfcq_timespec_to_ns(struct timespec _timestamp) 423 | { 424 | return _timestamp.tv_sec * 1000000000ULL + _timestamp.tv_nsec; 425 | } 426 | 427 | bool pfcq_net_addr_cmp(struct pfcq_net_addr* _na1, struct pfcq_net_addr* _na2) 428 | { 429 | if (_na1->family == _na2->family) 430 | { 431 | switch (_na1->family) 432 | { 433 | case AF_INET: 434 | return ((_na1->addr.ip4.sin_addr.s_addr == _na2->addr.ip4.sin_addr.s_addr) && 435 | (_na1->addr.ip4.sin_port == _na2->addr.ip4.sin_port)); 436 | break; 437 | case AF_INET6: 438 | return ((_na1->addr.ip6.sin6_addr.s6_addr == _na2->addr.ip6.sin6_addr.s6_addr) && 439 | (_na1->addr.ip6.sin6_port == _na2->addr.ip6.sin6_port)); 440 | break; 441 | default: 442 | panic("Unknown address family"); 443 | break; 444 | } 445 | } else 446 | return false; 447 | } 448 | 449 | void pfcq_counter_init(struct pfcq_counter* _counter) 450 | { 451 | #ifdef DS_HAVE_ATOMICS 452 | _counter->val = ATOMIC_VAR_INIT(0); 453 | #else /* DS_HAVE_ATOMICS */ 454 | pfcq_counter_reset(_counter); 455 | #endif /* DS_HAVE_ATOMICS */ 456 | } 457 | 458 | void pfcq_counter_reset(struct pfcq_counter* _counter) 459 | { 460 | pfcq_counter_set(_counter, 0); 461 | } 462 | 463 | void pfcq_counter_inc(struct pfcq_counter* _counter) 464 | { 465 | #ifdef DS_HAVE_ATOMICS 466 | _counter->val++; 467 | #else /* DS_HAVE_ATOMICS */ 468 | AO_fetch_and_add1(&_counter->val); 469 | #endif /* DS_HAVE_ATOMICS */ 470 | } 471 | 472 | void pfcq_counter_dec(struct pfcq_counter* _counter) 473 | { 474 | #ifdef DS_HAVE_ATOMICS 475 | _counter->val--; 476 | #else /* DS_HAVE_ATOMICS */ 477 | AO_fetch_and_sub1(&_counter->val); 478 | #endif /* DS_HAVE_ATOMICS */ 479 | } 480 | 481 | size_t pfcq_counter_get(struct pfcq_counter* _counter) 482 | { 483 | #ifdef DS_HAVE_ATOMICS 484 | return _counter->val; 485 | #else /* DS_HAVE_ATOMICS */ 486 | return AO_load(&_counter->val); 487 | #endif /* DS_HAVE_ATOMICS */ 488 | } 489 | 490 | size_t pfcq_counter_get_inc_mod(struct pfcq_counter* _counter, size_t _mod, size_t _min) 491 | { 492 | #ifdef DS_HAVE_ATOMICS 493 | size_t orig_val = 0; 494 | atomic_size_t next_val = 0; 495 | #else /* DS_HAVE_ATOMICS */ 496 | AO_t orig_val = 0; 497 | AO_t next_val = 0; 498 | #endif 499 | 500 | while (true) 501 | { 502 | orig_val = pfcq_counter_get(_counter); 503 | 504 | next_val = (orig_val + 1) % _mod; 505 | if (next_val < _min) 506 | next_val = _min; 507 | 508 | #ifdef DS_HAVE_ATOMICS 509 | if (likely(atomic_compare_exchange_weak(&_counter->val, &orig_val, next_val))) 510 | #else /* DS_HAVE_ATOMICS */ 511 | if (likely(AO_compare_and_swap(&_counter->val, orig_val, next_val))) 512 | #endif 513 | break; 514 | } 515 | 516 | return orig_val; 517 | } 518 | 519 | bool pfcq_counter_reset_if_gt(struct pfcq_counter* _counter, size_t _max) 520 | { 521 | #ifdef DS_HAVE_ATOMICS 522 | size_t orig_val = 0; 523 | atomic_size_t zero_val = 0; 524 | #else /* DS_HAVE_ATOMICS */ 525 | AO_t orig_val = 0; 526 | AO_t zero_val = 0; 527 | #endif 528 | 529 | while (true) 530 | { 531 | orig_val = pfcq_counter_get(_counter); 532 | if (orig_val > _max) 533 | { 534 | 535 | #ifdef DS_HAVE_ATOMICS 536 | if (likely(atomic_compare_exchange_weak(&_counter->val, &orig_val, zero_val))) 537 | #else /* DS_HAVE_ATOMICS */ 538 | if (likely(AO_compare_and_swap(&_counter->val, orig_val, zero_val))) 539 | #endif 540 | return true; 541 | else 542 | continue; 543 | } else 544 | return false; 545 | } 546 | } 547 | 548 | void pfcq_counter_set(struct pfcq_counter* _counter, size_t _val) 549 | { 550 | #ifdef DS_HAVE_ATOMICS 551 | _counter->val = _val; 552 | #else /* DS_HAVE_ATOMICS */ 553 | AO_store(&_counter->val, (AO_t)_val); 554 | #endif /* DS_HAVE_ATOMICS */ 555 | } 556 | 557 | -------------------------------------------------------------------------------- /rb.c: -------------------------------------------------------------------------------- 1 | /* Produced by texiweb from libavl.w. */ 2 | 3 | /* libavl - library for manipulation of binary trees. 4 | Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License as 8 | published by the Free Software Foundation; either version 2 of the 9 | License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | See the GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 19 | 02111-1307, USA. 20 | 21 | The author may be contacted at on the Internet, or 22 | write to Ben Pfaff, Stanford University, Computer Science Dept., 353 23 | Serra Mall, Stanford CA 94305, USA. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "rb.h" 31 | 32 | /* Creates and returns a new table 33 | with comparison function |compare| using parameter |param| 34 | and memory allocator |allocator|. 35 | Returns |NULL| if memory allocation failed. */ 36 | struct rb_table *rb_create(rb_comparison_func * compare, void *param, 37 | struct libavl_allocator *allocator) 38 | { 39 | struct rb_table *tree; 40 | 41 | assert(compare != NULL); 42 | 43 | if (allocator == NULL) 44 | allocator = &rb_allocator_default; 45 | 46 | tree = allocator->libavl_malloc(allocator, sizeof *tree); 47 | if (tree == NULL) 48 | return NULL; 49 | 50 | tree->rb_root = NULL; 51 | tree->rb_compare = compare; 52 | tree->rb_param = param; 53 | tree->rb_alloc = allocator; 54 | tree->rb_count = 0; 55 | tree->rb_generation = 0; 56 | 57 | return tree; 58 | } 59 | 60 | /* Search |tree| for an item matching |item|, and return it if found. 61 | Otherwise return |NULL|. */ 62 | void *rb_find(const struct rb_table *tree, const void *item) 63 | { 64 | const struct rb_node *p; 65 | 66 | assert(tree != NULL && item != NULL); 67 | for (p = tree->rb_root; p != NULL;) { 68 | int cmp = tree->rb_compare(item, p->rb_data, tree->rb_param); 69 | 70 | if (cmp < 0) 71 | p = p->rb_link[0]; 72 | else if (cmp > 0) 73 | p = p->rb_link[1]; 74 | else /* |cmp == 0| */ 75 | return p->rb_data; 76 | } 77 | 78 | return NULL; 79 | } 80 | 81 | /* Inserts |item| into |tree| and returns a pointer to |item|'s address. 82 | If a duplicate item is found in the tree, 83 | returns a pointer to the duplicate without inserting |item|. 84 | Returns |NULL| in case of memory allocation failure. */ 85 | void **rb_probe(struct rb_table *tree, void *item) 86 | { 87 | struct rb_node *pa[RB_MAX_HEIGHT]; /* Nodes on stack. */ 88 | unsigned char da[RB_MAX_HEIGHT]; /* Directions moved from stack nodes. */ 89 | int k; /* Stack height. */ 90 | 91 | struct rb_node *p; /* Traverses tree looking for insertion point. */ 92 | struct rb_node *n; /* Newly inserted node. */ 93 | 94 | assert(tree != NULL && item != NULL); 95 | 96 | pa[0] = (struct rb_node *)&tree->rb_root; 97 | da[0] = 0; 98 | k = 1; 99 | for (p = tree->rb_root; p != NULL; p = p->rb_link[da[k - 1]]) { 100 | int cmp = tree->rb_compare(item, p->rb_data, tree->rb_param); 101 | if (cmp == 0) 102 | return &p->rb_data; 103 | 104 | pa[k] = p; 105 | da[k++] = (unsigned char)(cmp > 0); 106 | } 107 | 108 | n = pa[k - 1]->rb_link[da[k - 1]] = 109 | tree->rb_alloc->libavl_malloc(tree->rb_alloc, sizeof *n); 110 | if (n == NULL) 111 | return NULL; 112 | 113 | n->rb_data = item; 114 | n->rb_link[0] = n->rb_link[1] = NULL; 115 | n->rb_color = RB_RED; 116 | tree->rb_count++; 117 | tree->rb_generation++; 118 | 119 | while (k >= 3 && pa[k - 1]->rb_color == RB_RED) { 120 | if (da[k - 2] == 0) { 121 | struct rb_node *y = pa[k - 2]->rb_link[1]; 122 | if (y != NULL && y->rb_color == RB_RED) { 123 | pa[k - 1]->rb_color = y->rb_color = RB_BLACK; 124 | pa[k - 2]->rb_color = RB_RED; 125 | k -= 2; 126 | } else { 127 | struct rb_node *x; 128 | 129 | if (da[k - 1] == 0) 130 | y = pa[k - 1]; 131 | else { 132 | x = pa[k - 1]; 133 | y = x->rb_link[1]; 134 | x->rb_link[1] = y->rb_link[0]; 135 | y->rb_link[0] = x; 136 | pa[k - 2]->rb_link[0] = y; 137 | } 138 | 139 | x = pa[k - 2]; 140 | x->rb_color = RB_RED; 141 | y->rb_color = RB_BLACK; 142 | 143 | x->rb_link[0] = y->rb_link[1]; 144 | y->rb_link[1] = x; 145 | pa[k - 3]->rb_link[da[k - 3]] = y; 146 | break; 147 | } 148 | } else { 149 | struct rb_node *y = pa[k - 2]->rb_link[0]; 150 | if (y != NULL && y->rb_color == RB_RED) { 151 | pa[k - 1]->rb_color = y->rb_color = RB_BLACK; 152 | pa[k - 2]->rb_color = RB_RED; 153 | k -= 2; 154 | } else { 155 | struct rb_node *x; 156 | 157 | if (da[k - 1] == 1) 158 | y = pa[k - 1]; 159 | else { 160 | x = pa[k - 1]; 161 | y = x->rb_link[0]; 162 | x->rb_link[0] = y->rb_link[1]; 163 | y->rb_link[1] = x; 164 | pa[k - 2]->rb_link[1] = y; 165 | } 166 | 167 | x = pa[k - 2]; 168 | x->rb_color = RB_RED; 169 | y->rb_color = RB_BLACK; 170 | 171 | x->rb_link[1] = y->rb_link[0]; 172 | y->rb_link[0] = x; 173 | pa[k - 3]->rb_link[da[k - 3]] = y; 174 | break; 175 | } 176 | } 177 | } 178 | tree->rb_root->rb_color = RB_BLACK; 179 | 180 | return &n->rb_data; 181 | } 182 | 183 | /* Inserts |item| into |table|. 184 | Returns |NULL| if |item| was successfully inserted 185 | or if a memory allocation error occurred. 186 | Otherwise, returns the duplicate item. */ 187 | void *rb_insert(struct rb_table *table, void *item) 188 | { 189 | void **p = rb_probe(table, item); 190 | return p == NULL || *p == item ? NULL : *p; 191 | } 192 | 193 | /* Inserts |item| into |table|, replacing any duplicate item. 194 | Returns |NULL| if |item| was inserted without replacing a duplicate, 195 | or if a memory allocation error occurred. 196 | Otherwise, returns the item that was replaced. */ 197 | void *rb_replace(struct rb_table *table, void *item) 198 | { 199 | void **p = rb_probe(table, item); 200 | if (p == NULL || *p == item) 201 | return NULL; 202 | else { 203 | void *r = *p; 204 | *p = item; 205 | return r; 206 | } 207 | } 208 | 209 | /* Deletes from |tree| and returns an item matching |item|. 210 | Returns a null pointer if no matching item found. */ 211 | const void *rb_delete(struct rb_table *tree, const void *item) 212 | { 213 | struct rb_node *pa[RB_MAX_HEIGHT]; /* Nodes on stack. */ 214 | unsigned char da[RB_MAX_HEIGHT]; /* Directions moved from stack nodes. */ 215 | int k; /* Stack height. */ 216 | 217 | struct rb_node *p; /* The node to delete, or a node part way to it. */ 218 | int cmp; /* Result of comparison between |item| and |p|. */ 219 | 220 | assert(tree != NULL && item != NULL); 221 | 222 | k = 0; 223 | p = (struct rb_node *)&tree->rb_root; 224 | for (cmp = -1; cmp != 0; 225 | cmp = tree->rb_compare(item, p->rb_data, tree->rb_param)) { 226 | int dir = cmp > 0; 227 | 228 | pa[k] = p; 229 | da[k++] = (unsigned char)dir; 230 | 231 | p = p->rb_link[dir]; 232 | if (p == NULL) 233 | return NULL; 234 | } 235 | item = p->rb_data; 236 | 237 | if (p->rb_link[1] == NULL) 238 | pa[k - 1]->rb_link[da[k - 1]] = p->rb_link[0]; 239 | else { 240 | enum rb_color t; 241 | struct rb_node *r = p->rb_link[1]; 242 | 243 | if (r->rb_link[0] == NULL) { 244 | r->rb_link[0] = p->rb_link[0]; 245 | t = (enum rb_color)r->rb_color; 246 | r->rb_color = p->rb_color; 247 | p->rb_color = (unsigned char)t; 248 | pa[k - 1]->rb_link[da[k - 1]] = r; 249 | da[k] = 1; 250 | pa[k++] = r; 251 | } else { 252 | struct rb_node *s; 253 | int j = k++; 254 | 255 | for (;;) { 256 | da[k] = 0; 257 | pa[k++] = r; 258 | s = r->rb_link[0]; 259 | if (s->rb_link[0] == NULL) 260 | break; 261 | 262 | r = s; 263 | } 264 | 265 | da[j] = 1; 266 | pa[j] = s; 267 | pa[j - 1]->rb_link[da[j - 1]] = s; 268 | 269 | s->rb_link[0] = p->rb_link[0]; 270 | r->rb_link[0] = s->rb_link[1]; 271 | s->rb_link[1] = p->rb_link[1]; 272 | 273 | t = (enum rb_color)s->rb_color; 274 | s->rb_color = p->rb_color; 275 | p->rb_color = (unsigned char)t; 276 | } 277 | } 278 | 279 | if (p->rb_color == RB_BLACK) { 280 | for (;;) { 281 | struct rb_node *x = pa[k - 1]->rb_link[da[k - 1]]; 282 | if (x != NULL && x->rb_color == RB_RED) { 283 | x->rb_color = RB_BLACK; 284 | break; 285 | } 286 | if (k < 2) 287 | break; 288 | 289 | if (da[k - 1] == 0) { 290 | struct rb_node *w = pa[k - 1]->rb_link[1]; 291 | 292 | if (w->rb_color == RB_RED) { 293 | w->rb_color = RB_BLACK; 294 | pa[k - 1]->rb_color = RB_RED; 295 | 296 | pa[k - 1]->rb_link[1] = w->rb_link[0]; 297 | w->rb_link[0] = pa[k - 1]; 298 | pa[k - 2]->rb_link[da[k - 2]] = w; 299 | 300 | pa[k] = pa[k - 1]; 301 | da[k] = 0; 302 | pa[k - 1] = w; 303 | k++; 304 | 305 | w = pa[k - 1]->rb_link[1]; 306 | } 307 | 308 | if ((w->rb_link[0] == NULL 309 | || w->rb_link[0]->rb_color == RB_BLACK) 310 | && (w->rb_link[1] == NULL 311 | || w->rb_link[1]->rb_color == RB_BLACK)) 312 | w->rb_color = RB_RED; 313 | else { 314 | if (w->rb_link[1] == NULL 315 | || w->rb_link[1]->rb_color == 316 | RB_BLACK) { 317 | struct rb_node *y = 318 | w->rb_link[0]; 319 | y->rb_color = RB_BLACK; 320 | w->rb_color = RB_RED; 321 | w->rb_link[0] = y->rb_link[1]; 322 | y->rb_link[1] = w; 323 | w = pa[k - 1]->rb_link[1] = y; 324 | } 325 | 326 | w->rb_color = pa[k - 1]->rb_color; 327 | pa[k - 1]->rb_color = RB_BLACK; 328 | w->rb_link[1]->rb_color = RB_BLACK; 329 | 330 | pa[k - 1]->rb_link[1] = w->rb_link[0]; 331 | w->rb_link[0] = pa[k - 1]; 332 | pa[k - 2]->rb_link[da[k - 2]] = w; 333 | break; 334 | } 335 | } else { 336 | struct rb_node *w = pa[k - 1]->rb_link[0]; 337 | 338 | if (w->rb_color == RB_RED) { 339 | w->rb_color = RB_BLACK; 340 | pa[k - 1]->rb_color = RB_RED; 341 | 342 | pa[k - 1]->rb_link[0] = w->rb_link[1]; 343 | w->rb_link[1] = pa[k - 1]; 344 | pa[k - 2]->rb_link[da[k - 2]] = w; 345 | 346 | pa[k] = pa[k - 1]; 347 | da[k] = 1; 348 | pa[k - 1] = w; 349 | k++; 350 | 351 | w = pa[k - 1]->rb_link[0]; 352 | } 353 | 354 | if ((w->rb_link[0] == NULL 355 | || w->rb_link[0]->rb_color == RB_BLACK) 356 | && (w->rb_link[1] == NULL 357 | || w->rb_link[1]->rb_color == RB_BLACK)) 358 | w->rb_color = RB_RED; 359 | else { 360 | if (w->rb_link[0] == NULL 361 | || w->rb_link[0]->rb_color == 362 | RB_BLACK) { 363 | struct rb_node *y = 364 | w->rb_link[1]; 365 | y->rb_color = RB_BLACK; 366 | w->rb_color = RB_RED; 367 | w->rb_link[1] = y->rb_link[0]; 368 | y->rb_link[0] = w; 369 | w = pa[k - 1]->rb_link[0] = y; 370 | } 371 | 372 | w->rb_color = pa[k - 1]->rb_color; 373 | pa[k - 1]->rb_color = RB_BLACK; 374 | w->rb_link[0]->rb_color = RB_BLACK; 375 | 376 | pa[k - 1]->rb_link[0] = w->rb_link[1]; 377 | w->rb_link[1] = pa[k - 1]; 378 | pa[k - 2]->rb_link[da[k - 2]] = w; 379 | break; 380 | } 381 | } 382 | 383 | k--; 384 | } 385 | 386 | } 387 | 388 | tree->rb_alloc->libavl_free(tree->rb_alloc, p); 389 | tree->rb_count--; 390 | tree->rb_generation++; 391 | return item; 392 | } 393 | 394 | /* Refreshes the stack of parent pointers in |trav| 395 | and updates its generation number. */ 396 | static void trav_refresh(struct rb_traverser *trav) 397 | { 398 | assert(trav != NULL); 399 | 400 | trav->rb_generation = trav->rb_table->rb_generation; 401 | 402 | if (trav->rb_node != NULL) { 403 | rb_comparison_func *cmp = trav->rb_table->rb_compare; 404 | void *param = trav->rb_table->rb_param; 405 | struct rb_node *node = trav->rb_node; 406 | struct rb_node *i; 407 | 408 | trav->rb_height = 0; 409 | for (i = trav->rb_table->rb_root; i != node;) { 410 | assert(trav->rb_height < RB_MAX_HEIGHT); 411 | assert(i != NULL); 412 | 413 | trav->rb_stack[trav->rb_height++] = i; 414 | i = i->rb_link[cmp(node->rb_data, i->rb_data, param) > 415 | 0]; 416 | } 417 | } 418 | } 419 | 420 | /* Initializes |trav| for use with |tree| 421 | and selects the null node. */ 422 | void rb_t_init(struct rb_traverser *trav, struct rb_table *tree) 423 | { 424 | trav->rb_table = tree; 425 | trav->rb_node = NULL; 426 | trav->rb_height = 0; 427 | trav->rb_generation = tree->rb_generation; 428 | } 429 | 430 | /* Initializes |trav| for |tree| 431 | and selects and returns a pointer to its least-valued item. 432 | Returns |NULL| if |tree| contains no nodes. */ 433 | void *rb_t_first(struct rb_traverser *trav, struct rb_table *tree) 434 | { 435 | struct rb_node *x; 436 | 437 | assert(tree != NULL && trav != NULL); 438 | 439 | trav->rb_table = tree; 440 | trav->rb_height = 0; 441 | trav->rb_generation = tree->rb_generation; 442 | 443 | x = tree->rb_root; 444 | if (x != NULL) 445 | while (x->rb_link[0] != NULL) { 446 | assert(trav->rb_height < RB_MAX_HEIGHT); 447 | trav->rb_stack[trav->rb_height++] = x; 448 | x = x->rb_link[0]; 449 | } 450 | trav->rb_node = x; 451 | 452 | return x != NULL ? x->rb_data : NULL; 453 | } 454 | 455 | /* Initializes |trav| for |tree| 456 | and selects and returns a pointer to its greatest-valued item. 457 | Returns |NULL| if |tree| contains no nodes. */ 458 | void *rb_t_last(struct rb_traverser *trav, struct rb_table *tree) 459 | { 460 | struct rb_node *x; 461 | 462 | assert(tree != NULL && trav != NULL); 463 | 464 | trav->rb_table = tree; 465 | trav->rb_height = 0; 466 | trav->rb_generation = tree->rb_generation; 467 | 468 | x = tree->rb_root; 469 | if (x != NULL) 470 | while (x->rb_link[1] != NULL) { 471 | assert(trav->rb_height < RB_MAX_HEIGHT); 472 | trav->rb_stack[trav->rb_height++] = x; 473 | x = x->rb_link[1]; 474 | } 475 | trav->rb_node = x; 476 | 477 | return x != NULL ? x->rb_data : NULL; 478 | } 479 | 480 | /* Searches for |item| in |tree|. 481 | If found, initializes |trav| to the item found and returns the item 482 | as well. 483 | If there is no matching item, initializes |trav| to the null item 484 | and returns |NULL|. */ 485 | void *rb_t_find(struct rb_traverser *trav, struct rb_table *tree, void *item) 486 | { 487 | struct rb_node *p, *q; 488 | 489 | assert(trav != NULL && tree != NULL && item != NULL); 490 | trav->rb_table = tree; 491 | trav->rb_height = 0; 492 | trav->rb_generation = tree->rb_generation; 493 | for (p = tree->rb_root; p != NULL; p = q) { 494 | int cmp = tree->rb_compare(item, p->rb_data, tree->rb_param); 495 | 496 | if (cmp < 0) 497 | q = p->rb_link[0]; 498 | else if (cmp > 0) 499 | q = p->rb_link[1]; 500 | else { /* |cmp == 0| */ 501 | 502 | trav->rb_node = p; 503 | return p->rb_data; 504 | } 505 | 506 | assert(trav->rb_height < RB_MAX_HEIGHT); 507 | trav->rb_stack[trav->rb_height++] = p; 508 | } 509 | 510 | trav->rb_height = 0; 511 | trav->rb_node = NULL; 512 | return NULL; 513 | } 514 | 515 | /* Attempts to insert |item| into |tree|. 516 | If |item| is inserted successfully, it is returned and |trav| is 517 | initialized to its location. 518 | If a duplicate is found, it is returned and |trav| is initialized to 519 | its location. No replacement of the item occurs. 520 | If a memory allocation failure occurs, |NULL| is returned and |trav| 521 | is initialized to the null item. */ 522 | void *rb_t_insert(struct rb_traverser *trav, struct rb_table *tree, void *item) 523 | { 524 | void **p; 525 | 526 | assert(trav != NULL && tree != NULL && item != NULL); 527 | 528 | p = rb_probe(tree, item); 529 | if (p != NULL) { 530 | trav->rb_table = tree; 531 | trav->rb_node = ((struct rb_node *) 532 | ((char *)p - 533 | offsetof(struct rb_node, rb_data))); 534 | trav->rb_generation = tree->rb_generation - 1; 535 | return *p; 536 | } else { 537 | rb_t_init(trav, tree); 538 | return NULL; 539 | } 540 | } 541 | 542 | /* Initializes |trav| to have the same current node as |src|. */ 543 | void *rb_t_copy(struct rb_traverser *trav, const struct rb_traverser *src) 544 | { 545 | assert(trav != NULL && src != NULL); 546 | 547 | if (trav != src) { 548 | trav->rb_table = src->rb_table; 549 | trav->rb_node = src->rb_node; 550 | trav->rb_generation = src->rb_generation; 551 | if (trav->rb_generation == trav->rb_table->rb_generation) { 552 | trav->rb_height = src->rb_height; 553 | memcpy(trav->rb_stack, (const void *)src->rb_stack, 554 | sizeof *trav->rb_stack * trav->rb_height); 555 | } 556 | } 557 | 558 | return trav->rb_node != NULL ? trav->rb_node->rb_data : NULL; 559 | } 560 | 561 | /* Returns the next data item in inorder 562 | within the tree being traversed with |trav|, 563 | or if there are no more data items returns |NULL|. */ 564 | void *rb_t_next(struct rb_traverser *trav) 565 | { 566 | struct rb_node *x; 567 | 568 | assert(trav != NULL); 569 | 570 | if (trav->rb_generation != trav->rb_table->rb_generation) 571 | trav_refresh(trav); 572 | 573 | x = trav->rb_node; 574 | if (x == NULL) { 575 | return rb_t_first(trav, trav->rb_table); 576 | } else if (x->rb_link[1] != NULL) { 577 | assert(trav->rb_height < RB_MAX_HEIGHT); 578 | trav->rb_stack[trav->rb_height++] = x; 579 | x = x->rb_link[1]; 580 | 581 | while (x->rb_link[0] != NULL) { 582 | assert(trav->rb_height < RB_MAX_HEIGHT); 583 | trav->rb_stack[trav->rb_height++] = x; 584 | x = x->rb_link[0]; 585 | } 586 | } else { 587 | struct rb_node *y; 588 | 589 | do { 590 | if (trav->rb_height == 0) { 591 | trav->rb_node = NULL; 592 | return NULL; 593 | } 594 | 595 | y = x; 596 | x = trav->rb_stack[--trav->rb_height]; 597 | } 598 | while (y == x->rb_link[1]); 599 | } 600 | trav->rb_node = x; 601 | 602 | return x->rb_data; 603 | } 604 | 605 | /* Returns the previous data item in inorder 606 | within the tree being traversed with |trav|, 607 | or if there are no more data items returns |NULL|. */ 608 | void *rb_t_prev(struct rb_traverser *trav) 609 | { 610 | struct rb_node *x; 611 | 612 | assert(trav != NULL); 613 | 614 | if (trav->rb_generation != trav->rb_table->rb_generation) 615 | trav_refresh(trav); 616 | 617 | x = trav->rb_node; 618 | if (x == NULL) { 619 | return rb_t_last(trav, trav->rb_table); 620 | } else if (x->rb_link[0] != NULL) { 621 | assert(trav->rb_height < RB_MAX_HEIGHT); 622 | trav->rb_stack[trav->rb_height++] = x; 623 | x = x->rb_link[0]; 624 | 625 | while (x->rb_link[1] != NULL) { 626 | assert(trav->rb_height < RB_MAX_HEIGHT); 627 | trav->rb_stack[trav->rb_height++] = x; 628 | x = x->rb_link[1]; 629 | } 630 | } else { 631 | struct rb_node *y; 632 | 633 | do { 634 | if (trav->rb_height == 0) { 635 | trav->rb_node = NULL; 636 | return NULL; 637 | } 638 | 639 | y = x; 640 | x = trav->rb_stack[--trav->rb_height]; 641 | } 642 | while (y == x->rb_link[0]); 643 | } 644 | trav->rb_node = x; 645 | 646 | return x->rb_data; 647 | } 648 | 649 | /* Returns |trav|'s current item. */ 650 | void *rb_t_cur(struct rb_traverser *trav) 651 | { 652 | assert(trav != NULL); 653 | 654 | return trav->rb_node != NULL ? trav->rb_node->rb_data : NULL; 655 | } 656 | 657 | /* Replaces the current item in |trav| by |new| and returns the item replaced. 658 | |trav| must not have the null item selected. 659 | The new item must not upset the ordering of the tree. */ 660 | void *rb_t_replace(struct rb_traverser *trav, void *new) 661 | { 662 | void *old; 663 | 664 | assert(trav != NULL && trav->rb_node != NULL && new != NULL); 665 | old = trav->rb_node->rb_data; 666 | trav->rb_node->rb_data = new; 667 | return old; 668 | } 669 | 670 | /* Destroys |new| with |rb_destroy (new, destroy)|, 671 | first setting right links of nodes in |stack| within |new| 672 | to null pointers to avoid touching uninitialized data. */ 673 | static void 674 | copy_error_recovery(struct rb_node **stack, int height, 675 | struct rb_table *new, rb_item_func * destroy) 676 | { 677 | assert(stack != NULL && height >= 0 && new != NULL); 678 | 679 | for (; height > 2; height -= 2) 680 | stack[height - 1]->rb_link[1] = NULL; 681 | rb_destroy(new, destroy); 682 | } 683 | 684 | /* Copies |org| to a newly created tree, which is returned. 685 | If |copy != NULL|, each data item in |org| is first passed to |copy|, 686 | and the return values are inserted into the tree, 687 | with |NULL| return values taken as indications of failure. 688 | On failure, destroys the partially created new tree, 689 | applying |destroy|, if non-null, to each item in the new tree so far, 690 | and returns |NULL|. 691 | If |allocator != NULL|, it is used for allocation in the new tree. 692 | Otherwise, the same allocator used for |org| is used. */ 693 | struct rb_table *rb_copy(const struct rb_table *org, rb_copy_func * copy, 694 | rb_item_func * destroy, 695 | struct libavl_allocator *allocator) 696 | { 697 | struct rb_node *stack[2 * (RB_MAX_HEIGHT + 1)]; 698 | int height = 0; 699 | 700 | struct rb_table *new; 701 | const struct rb_node *x; 702 | struct rb_node *y; 703 | 704 | assert(org != NULL); 705 | new = rb_create(org->rb_compare, org->rb_param, 706 | allocator != NULL ? allocator : org->rb_alloc); 707 | if (new == NULL) 708 | return NULL; 709 | new->rb_count = org->rb_count; 710 | if (new->rb_count == 0) 711 | return new; 712 | 713 | x = (const struct rb_node *)&org->rb_root; 714 | y = (struct rb_node *)&new->rb_root; 715 | for (;;) { 716 | while (x->rb_link[0] != NULL) { 717 | assert(height < 2 * (RB_MAX_HEIGHT + 1)); 718 | 719 | y->rb_link[0] = 720 | new->rb_alloc->libavl_malloc(new->rb_alloc, 721 | sizeof *y->rb_link[0]); 722 | if (y->rb_link[0] == NULL) { 723 | if (y != (struct rb_node *)&new->rb_root) { 724 | y->rb_data = NULL; 725 | y->rb_link[1] = NULL; 726 | } 727 | 728 | copy_error_recovery(stack, height, new, 729 | destroy); 730 | return NULL; 731 | } 732 | 733 | memcpy(&stack[height++], &x, 734 | sizeof(const struct rb_node *)); 735 | stack[height++] = y; 736 | x = x->rb_link[0]; 737 | y = y->rb_link[0]; 738 | } 739 | y->rb_link[0] = NULL; 740 | 741 | for (;;) { 742 | y->rb_color = x->rb_color; 743 | if (copy == NULL) 744 | y->rb_data = x->rb_data; 745 | else { 746 | y->rb_data = copy(x->rb_data, org->rb_param); 747 | if (y->rb_data == NULL) { 748 | y->rb_link[1] = NULL; 749 | copy_error_recovery(stack, height, new, 750 | destroy); 751 | return NULL; 752 | } 753 | } 754 | 755 | if (x->rb_link[1] != NULL) { 756 | y->rb_link[1] = 757 | new->rb_alloc->libavl_malloc(new->rb_alloc, 758 | sizeof *y-> 759 | rb_link[1]); 760 | if (y->rb_link[1] == NULL) { 761 | copy_error_recovery(stack, height, new, 762 | destroy); 763 | return NULL; 764 | } 765 | 766 | x = x->rb_link[1]; 767 | y = y->rb_link[1]; 768 | break; 769 | } else 770 | y->rb_link[1] = NULL; 771 | 772 | if (height <= 2) 773 | return new; 774 | 775 | y = stack[--height]; 776 | x = stack[--height]; 777 | } 778 | } 779 | } 780 | 781 | /* Frees storage allocated for |tree|. 782 | If |destroy != NULL|, applies it to each data item in inorder. */ 783 | void rb_destroy(struct rb_table *tree, rb_item_func * destroy) 784 | { 785 | struct rb_node *p, *q; 786 | 787 | assert(tree != NULL); 788 | 789 | for (p = tree->rb_root; p != NULL; p = q) 790 | if (p->rb_link[0] == NULL) { 791 | q = p->rb_link[1]; 792 | if (destroy != NULL && p->rb_data != NULL) 793 | destroy(p->rb_data, tree->rb_param); 794 | tree->rb_alloc->libavl_free(tree->rb_alloc, p); 795 | } else { 796 | q = p->rb_link[0]; 797 | p->rb_link[0] = q->rb_link[1]; 798 | q->rb_link[1] = p; 799 | } 800 | 801 | tree->rb_alloc->libavl_free(tree->rb_alloc, tree); 802 | } 803 | 804 | /* Allocates |size| bytes of space using |malloc()|. 805 | Returns a null pointer if allocation fails. */ 806 | void *rb_malloc(struct libavl_allocator *allocator, size_t size) 807 | { 808 | assert(allocator != NULL && size > 0); 809 | return malloc(size); 810 | } 811 | 812 | /* Frees |block|. */ 813 | void rb_free(struct libavl_allocator *allocator, void *block) 814 | { 815 | assert(allocator != NULL && block != NULL); 816 | free(block); 817 | } 818 | 819 | /* Default memory allocator that uses |malloc()| and |free()|. */ 820 | struct libavl_allocator rb_allocator_default = { 821 | rb_malloc, 822 | rb_free 823 | }; 824 | 825 | #undef NDEBUG 826 | #include 827 | 828 | /* Asserts that |rb_insert()| succeeds at inserting |item| into |table|. */ 829 | void 830 | (rb_assert_insert) (struct rb_table * table, void *item) { 831 | void **p = rb_probe(table, item); 832 | assert(p != NULL && *p == item); 833 | } 834 | 835 | /* Asserts that |rb_delete()| really removes |item| from |table|, 836 | and returns the removed item. */ 837 | const void *(rb_assert_delete) (struct rb_table * table, const void *item) { 838 | const void *p = rb_delete(table, item); 839 | assert(p != NULL); 840 | return p; 841 | } 842 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------