├── .gitignore ├── src ├── atomic.h ├── version.h ├── portcheck.h ├── log.h ├── log.c ├── job.h ├── job.c ├── util.h ├── db.h ├── listener.c ├── client.h ├── util.c ├── packet.h ├── server.h ├── portcheck.c ├── main.c ├── client.c ├── config.c ├── ed2k_proto.h ├── packet.c ├── server.c ├── db_sqlite.c └── queue.h ├── cppcheck.sh ├── eb ├── CMakeLists.txt └── src │ └── main.c ├── README.md ├── ed2kd.conf.dist ├── LICENSE ├── cmake └── modules │ ├── FindLibconfig.cmake │ └── FindLibevent.cmake └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /build 3 | -------------------------------------------------------------------------------- /src/atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_ATOMIC_H 2 | #define ED2KD_ATOMIC_H 3 | 4 | #include 5 | #include 6 | 7 | typedef _Atomic uint16_t atomic_uint16_t; 8 | typedef _Atomic uint32_t atomic_uint32_t; 9 | typedef _Atomic uint64_t atomic_uint64_t; 10 | 11 | #endif // ED2KD_ATOMIC_H 12 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_VERSION_H 2 | #define ED2KD_VERSION_H 3 | 4 | #define _CSTR(x) #x 5 | #define CSTR(x) _CSTR(x) 6 | 7 | #define ED2KD_VER_MJR 17 8 | #define ED2KD_VER_MNR 15 9 | 10 | #define ED2KD_VER_STR CSTR(ED2KD_VER_MJR) "." CSTR(ED2KD_VER_MNR) 11 | 12 | #endif // ED2KD_VERSION_H 13 | -------------------------------------------------------------------------------- /cppcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cppcheck -q \ 4 | --enable=all \ 5 | --inconclusive \ 6 | --library=/usr/share/cppcheck/cfg/std.cfg \ 7 | --library=/usr/share/cppcheck/cfg/posix.cfg \ 8 | --library=/usr/share/cppcheck/cfg/gnu.cfg \ 9 | --std=c11 \ 10 | --platform=unix64 \ 11 | -Ibuild \ 12 | ./src 13 | -------------------------------------------------------------------------------- /src/portcheck.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_PORTCHECK_H 2 | #define ED2KD_PORTCHECK_H 3 | 4 | /* 5 | @file portcheck.h 6 | */ 7 | 8 | struct client; 9 | 10 | void portcheck_read(struct client *client); 11 | 12 | void portcheck_event(struct client *client, short events); 13 | 14 | void portcheck_timeout(struct client *clnt); 15 | 16 | #endif // ED2KD_PORTCHECK_H 17 | -------------------------------------------------------------------------------- /eb/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7) 2 | PROJECT(eb C) 3 | 4 | SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} 5 | ${CMAKE_SOURCE_DIR}/../cmake/modules) 6 | 7 | FIND_PACKAGE(Libevent REQUIRED core) 8 | FIND_PACKAGE(ZLIB REQUIRED) 9 | 10 | SET(INCLUDES ${LIBEVENT_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) 11 | 12 | SET(LIBS ${LIBEVENT_LIBRARIES} ${ZLIB_LIBRARIES}) 13 | 14 | SET(SOURCES src/main.c) 15 | 16 | INCLUDE_DIRECTORIES(${INCLUDES}) 17 | ADD_EXECUTABLE(eb ${SOURCES}) 18 | 19 | TARGET_LINK_LIBRARIES(eb ${LIBS}) 20 | 21 | INSTALL(TARGETS eb 22 | RUNTIME DESTINATION bin 23 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #ed2kd 2 | 3 | eDonkey2000 server. 4 | 5 | [![Coverity Scan Build Status](https://scan.coverity.com/projects/3436/badge.svg)](https://scan.coverity.com/projects/3436) 6 | 7 | ### Build instructions 8 | 9 | You need the following libraries installed: 10 | - [libevent](http://libevent.org/), >= 2.0, event-based network I/O library 11 | - [zlib](http://zlib.net/), compression library 12 | - [libconfig](http://www.hyperrealm.com/libconfig/), >=1.4, configuration management library 13 | 14 | Build using cmake: 15 | 16 | ```shell 17 | mkdir build 18 | cd build 19 | cmake .. 20 | make 21 | ``` 22 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_LOG_H 2 | #define ED2KD_LOG_H 3 | 4 | enum log_severity { 5 | #ifdef USE_DEBUG 6 | LOG_DBG, 7 | #endif 8 | LOG_NFO, 9 | LOG_WRN, 10 | LOG_ERR 11 | }; 12 | 13 | void _ed2kd_log(enum log_severity svrt, const char *fmt, ...); 14 | 15 | #define ED2KD_LOGNFO(msg, args...) _ed2kd_log(LOG_NFO, msg, ##args) 16 | #define ED2KD_LOGWRN(msg, args...) _ed2kd_log(LOG_WRN, msg, ##args) 17 | #ifdef USE_DEBUG 18 | #define ED2KD_LOGDBG(msg, args...) _ed2kd_log(LOG_DBG, msg, ##args) 19 | #else 20 | #define ED2KD_LOGDBG(msg, args...) 21 | #endif 22 | #define ED2KD_LOGERR(msg, args...) _ed2kd_log(LOG_ERR, "(%s:%d): " msg, __FILE__, __LINE__, ##args) 23 | 24 | #endif // ED2KD_LOG_H 25 | -------------------------------------------------------------------------------- /ed2kd.conf.dist: -------------------------------------------------------------------------------- 1 | // server hash 2 | server_hash = "cc9039c4e1b90e12756c50885dbe0e6f"; 3 | 4 | // listen address 5 | listen_addr = "0.0.0.0"; 6 | 7 | // listen port 8 | listen_port = 4661; 9 | 10 | // listen backlog 11 | listen_backlog = 5; 12 | 13 | // welcom message, optional 14 | welcome_message = "test messaage"; 15 | 16 | // allow lowid clients 17 | allow_lowid = 1; 18 | 19 | // port check timeout (milliseconds) 20 | portcheck_timeout = 20000; 21 | 22 | // server status notify interval (milliseconds) 23 | status_notify_interval = 5000; 24 | 25 | // maximum connected clients 26 | max_clients = 100000; 27 | 28 | // maximum shared files on server 29 | max_files = 100000000; 30 | 31 | // maximum shared files per client 32 | max_files_per_client = 20000; 33 | 34 | // maximum number of client's file offers per second 35 | max_offers_limit = 2; 36 | 37 | // maximum number of client's search requests per second 38 | max_searches_limit = 10; 39 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void _ed2kd_log(enum log_severity svrt, const char *fmt, ...) 7 | { 8 | char buf[1024]; 9 | const char *severity_str; 10 | va_list ap; 11 | va_start(ap, fmt); 12 | 13 | // todo: add loggin mutex 14 | 15 | switch (svrt) { 16 | #ifdef USE_DEBUG 17 | case LOG_DBG: 18 | severity_str = "dbg"; 19 | break; 20 | #endif 21 | case LOG_NFO: 22 | severity_str = "nfo"; 23 | break; 24 | case LOG_WRN: 25 | severity_str = "wrn"; 26 | break; 27 | case LOG_ERR: 28 | severity_str = "err"; 29 | break; 30 | default: 31 | severity_str = "???"; 32 | break; 33 | } 34 | 35 | evutil_snprintf(buf, sizeof(buf), "[%s] %s\n", severity_str, fmt); 36 | vfprintf(stderr, buf, ap); 37 | 38 | va_end(ap); 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 gureedo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 5 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 6 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial 9 | portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 12 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 13 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 14 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 15 | DEALINGS IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /src/job.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_JOB_H 2 | #define ED2KD_JOB_H 3 | 4 | #include "queue.h" 5 | #include 6 | 7 | struct sockaddr; 8 | struct bufferevent; 9 | struct evconnlistener; 10 | struct client; 11 | 12 | enum job_type { 13 | JOB_SERVER_EVENT, 14 | JOB_SERVER_READ, 15 | JOB_SERVER_STATUS_NOTIFY, 16 | JOB_PORTCHECK_EVENT, 17 | JOB_PORTCHECK_READ, 18 | JOB_PORTCHECK_TIMEOUT 19 | }; 20 | 21 | struct job { 22 | enum job_type type; 23 | struct client *clnt; 24 | TAILQ_ENTRY(job) qentry; 25 | }; 26 | 27 | struct job_event { 28 | struct job hdr; 29 | short events; 30 | }; 31 | 32 | TAILQ_HEAD(job_queue, job); 33 | 34 | void server_read_cb(struct bufferevent *bev, void *ctx); 35 | 36 | void server_event_cb(struct bufferevent *bev, short events, void *ctx); 37 | 38 | void server_status_notify_cb(evutil_socket_t fd, short events, void *ctx); 39 | 40 | void portcheck_read_cb(struct bufferevent *bev, void *ctx); 41 | 42 | void portcheck_event_cb(struct bufferevent *bev, short events, void *ctx); 43 | 44 | void portcheck_timeout_cb(evutil_socket_t fd, short events, void *ctx); 45 | 46 | #endif // ED2KD_JOB_H 47 | -------------------------------------------------------------------------------- /src/job.c: -------------------------------------------------------------------------------- 1 | #include "job.h" 2 | #include 3 | 4 | #include "server.h" 5 | #include "client.h" 6 | 7 | void server_read_cb(struct bufferevent *bev, void *ctx) 8 | { 9 | struct job *job = (struct job *) calloc(1, sizeof *job); 10 | (void) bev; 11 | 12 | job->type = JOB_SERVER_READ; 13 | job->clnt = (struct client *) ctx; 14 | 15 | server_add_job(job); 16 | } 17 | 18 | void server_event_cb(struct bufferevent *bev, short events, void *ctx) 19 | { 20 | struct job_event *job = (struct job_event *) calloc(1, sizeof *job); 21 | (void) bev; 22 | 23 | job->hdr.type = JOB_SERVER_EVENT; 24 | job->hdr.clnt = (struct client *) ctx; 25 | job->events = events; 26 | 27 | server_add_job((struct job *) job); 28 | } 29 | 30 | void server_status_notify_cb(evutil_socket_t fd, short events, void *ctx) 31 | { 32 | struct job *job = (struct job *) calloc(1, sizeof(*job)); 33 | (void) fd; 34 | (void) events; 35 | 36 | job->type = JOB_SERVER_STATUS_NOTIFY; 37 | job->clnt = (struct client *) ctx; 38 | 39 | server_add_job(job); 40 | } 41 | 42 | void portcheck_read_cb(struct bufferevent *bev, void *ctx) 43 | { 44 | struct job *job = (struct job *) calloc(1, sizeof(*job)); 45 | (void) bev; 46 | 47 | job->type = JOB_PORTCHECK_READ; 48 | job->clnt = (struct client *) ctx; 49 | 50 | server_add_job((struct job *) job); 51 | } 52 | 53 | void portcheck_timeout_cb(evutil_socket_t fd, short events, void *ctx) 54 | { 55 | struct job *job = (struct job *) calloc(1, sizeof(*job)); 56 | (void) fd; 57 | (void) events; 58 | 59 | job->type = JOB_PORTCHECK_TIMEOUT; 60 | job->clnt = (struct client *) ctx; 61 | 62 | server_add_job(job); 63 | } 64 | 65 | void portcheck_event_cb(struct bufferevent *bev, short events, void *ctx) 66 | { 67 | struct job_event *job = (struct job_event *) calloc(1, sizeof(*job)); 68 | (void) bev; 69 | 70 | job->hdr.type = JOB_PORTCHECK_EVENT; 71 | job->hdr.clnt = (struct client *) ctx; 72 | job->events = events; 73 | 74 | server_add_job((struct job *) job); 75 | } 76 | -------------------------------------------------------------------------------- /cmake/modules/FindLibconfig.cmake: -------------------------------------------------------------------------------- 1 | # - Find libconfig 2 | # Find the native libconfig includes and library. 3 | # Once done this will define 4 | # 5 | # LIBCONFIG_INCLUDE_DIRS - where to find libconfig.h, etc. 6 | # LIBCONFIG_LIBRARIES - List of libraries when using libconfig. 7 | # LIBCONFIG_FOUND - True if libconfig found. 8 | # 9 | # LIBCONFIG_VERSION_STRING - The version of libconfig found (x.y.z) 10 | # LIBCONFIG_VERSION_MAJOR - The major version 11 | # LIBCONFIG_VERSION_MINOR - The minor version 12 | # LIBCONFIG_VERSION_PATCH - The patch version 13 | 14 | FIND_PATH(LIBCONFIG_INCLUDE_DIR NAMES libconfig.h) 15 | FIND_LIBRARY(LIBCONFIG_LIBRARY NAMES config) 16 | 17 | MARK_AS_ADVANCED(LIBCONFIG_LIBRARY LIBCONFIG_INCLUDE_DIR) 18 | 19 | IF(LIBCONFIG_INCLUDE_DIR AND EXISTS "${LIBCONFIG_INCLUDE_DIR}/libconfig.h") 20 | # Read and parse version header file for version number 21 | file(READ "${LIBCONFIG_INCLUDE_DIR}/libconfig.h" _libconfig_HEADER_CONTENTS) 22 | IF(_libconfig_HEADER_CONTENTS MATCHES ".*LIBCONFIG_VER_MAJOR.*") 23 | string(REGEX REPLACE ".*#define LIBCONFIG_VER_MAJOR +([0-9]+).*" "\\1" LIBCONFIG_VERSION_MAJOR "${_libconfig_HEADER_CONTENTS}") 24 | string(REGEX REPLACE ".*#define LIBCONFIG_VER_MINOR +([0-9]+).*" "\\1" LIBCONFIG_VERSION_MINOR "${_libconfig_HEADER_CONTENTS}") 25 | string(REGEX REPLACE ".*#define LIBCONFIG_VER_REVISION +([0-9]+).*" "\\1" LIBCONFIG_VERSION_PATCH "${_libconfig_HEADER_CONTENTS}") 26 | ELSE() 27 | SET(LIBCONFIG_VERSION_MAJOR 0) 28 | SET(LIBCONFIG_VERSION_MINOR 0) 29 | SET(LIBCONFIG_VERSION_PATCH 0) 30 | ENDIF() 31 | 32 | SET(LIBCONFIG_VERSION_STRING "${LIBCONFIG_VERSION_MAJOR}.${LIBCONFIG_VERSION_MINOR}.${LIBCONFIG_VERSION_PATCH}") 33 | ENDIF() 34 | 35 | # handle the QUIETLY and REQUIRED arguments and set LIBCONFIG_FOUND to TRUE if 36 | # all listed variables are TRUE 37 | INCLUDE(FindPackageHandleStandardArgs) 38 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Libconfig 39 | REQUIRED_VARS LIBCONFIG_LIBRARY LIBCONFIG_INCLUDE_DIR 40 | VERSION_VAR LIBCONFIG_VERSION_STRING 41 | ) 42 | 43 | IF(LIBCONFIG_FOUND) 44 | SET(LIBCONFIG_INCLUDE_DIRS ${LIBCONFIG_INCLUDE_DIR}) 45 | SET(LIBCONFIG_LIBRARIES ${LIBCONFIG_LIBRARY}) 46 | ENDIF() 47 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_UTIL_H 2 | #define ED2KD_UTIL_H 3 | 4 | /** 5 | @file util.h utilities 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef USE_DEBUG 13 | #define DEBUG_ONLY(x) x 14 | #else 15 | #define DEBUG_ONLY(x) 16 | #endif 17 | 18 | #define THREAD_LOCAL __thread 19 | 20 | #ifndef ARRAY_SIZE 21 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 22 | #endif 23 | 24 | /** 25 | @brief convert "DEADBEEF" -> {0xDE,0xAD,0xBE,0xEF} 26 | @param src source string 27 | @param dst destination buffer 28 | @param dst_len dst buffer length 29 | @return 0 on success, -1 on failure 30 | */ 31 | int hex2bin(const char *src, unsigned char *dst, size_t dst_len); 32 | 33 | /** 34 | @brief convert {0xDE,0xAD,0xBE,0xEF} -> "DEADBEEF\0" 35 | @param src source binary buffer 36 | @param dst destination string 37 | @param dst_len destination string length, including null character 38 | @return 0 on success, -1 on failure 39 | */ 40 | int bin2hex(const unsigned char *src, char *dst, size_t dst_len); 41 | 42 | /** 43 | @brief generate random ed2k user hash 44 | @param hash destination buffer, at least HASH_SIZE bytes 45 | */ 46 | void get_random_user_hash(unsigned char *hash); 47 | 48 | /** 49 | @brief get integer ed2k file type from string file type 50 | @param type string type 51 | @param len length of type without null byte 52 | */ 53 | uint8_t get_ed2k_file_type(const char *type, size_t len); 54 | 55 | /** 56 | @brief search file extension 57 | @param name file name 58 | @param len file name length 59 | @return pointer where file extension begins or NULL 60 | */ 61 | const char *file_extension(const char *name, size_t len); 62 | 63 | struct token_bucket { 64 | double tokens; 65 | time_t last_update; 66 | }; 67 | 68 | /** 69 | @brief token bucket initialization 70 | @param bucket pinter to target bucket 71 | @param max_tokens initial number of tokens in bucket (tokens per second) 72 | */ 73 | static inline void token_bucket_init(struct token_bucket *bucket, double tokens) 74 | { 75 | bucket->last_update = time(NULL); 76 | bucket->tokens = tokens; 77 | } 78 | 79 | /** 80 | @brief try to get one token from bucket 81 | @param bucket pinter to target bucket 82 | @param max_tokens maximum number of tokens per second 83 | @return non-zero on success 84 | */ 85 | int token_bucket_update(struct token_bucket *bucket, double max_tokens); 86 | 87 | 88 | #endif // ED2KD_UTIL_H 89 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.7) 2 | project(ed2kd C) 3 | 4 | if (CMAKE_BINARY_DIR STREQUAL ${CMAKE_SOURCE_DIR}) 5 | message(FATAL_ERROR "Building in source dir prohibited. Remove CMakeCache.txt and try 'mkdir build && cd build && cmake ..'.") 6 | endif () 7 | 8 | if (NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE EQUAL "") 9 | message(STATUS "No build type selected, default to RelWithDebInfo") 10 | set(CMAKE_BUILD_TYPE "RelWithDebInfo") 11 | endif () 12 | 13 | if (NOT CMAKE_INSTALL_PREFIX) 14 | message(STATUS "No install prefix provided, default to /usr/local") 15 | set(CMAKE_INSTALL_PREFIX /usr/local) 16 | endif () 17 | 18 | SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules) 19 | 20 | find_package(Threads REQUIRED) 21 | find_package(Libevent REQUIRED core pthreads) 22 | find_package(Libconfig 1.4.8 REQUIRED) 23 | find_package(ZLIB REQUIRED) 24 | 25 | set(INCLUDES 26 | "${CMAKE_SOURCE_DIR}/3rdparty" 27 | "${LIBEVENT_INCLUDE_DIRS}" 28 | "${LIBCONFIG_INCLUDE_DIRS}" 29 | "${ZLIB_INCLUDE_DIRS}" 30 | ) 31 | 32 | set(LIBS 33 | "${LIBCONFIG_LIBRARIES}" 34 | "${LIBEVENT_LIBRARIES}" 35 | "${ZLIB_LIBRARIES}" 36 | ) 37 | 38 | set(SOURCES 39 | src/client.c 40 | src/config.c 41 | src/job.c 42 | src/log.c 43 | src/main.c 44 | src/packet.c 45 | src/portcheck.c 46 | src/server.c 47 | src/listener.c 48 | src/util.c 49 | src/db_sqlite.c 50 | 3rdparty/sqlite3/sqlite3.c 51 | ) 52 | 53 | set_source_files_properties(3rdparty/sqlite3/sqlite3.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter) 54 | 55 | add_definitions( 56 | -DSQLITE_THREADSAFE=1 57 | -DSQLITE_ENABLE_FTS3_PARENTHESIS 58 | -DSQLITE_ENABLE_FTS4 59 | -DSQLITE_ENABLE_FTS4_UNICODE61 60 | -DSQLITE_OMIT_LOAD_EXTENSION 61 | ) 62 | 63 | include_directories(${INCLUDES}) 64 | add_executable(ed2kd ${SOURCES}) 65 | 66 | find_library(M_LIB m) 67 | list(APPEND LIBS ${M_LIB}) 68 | set_target_properties(ed2kd PROPERTIES LINK_FLAGS "-fopenmp") 69 | 70 | set(CMAKE_C_FLAGS_RELEASE "-Ofast -flto -march=native -funroll-loops -DNDEBUG") 71 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -Ofast -flto -march=native -funroll-loops -DNDEBUG") 72 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 -Wall -Wextra -fno-fast-math ") 73 | 74 | target_link_libraries(ed2kd ${LIBS}) 75 | 76 | install(TARGETS ed2kd 77 | RUNTIME DESTINATION bin 78 | ) 79 | -------------------------------------------------------------------------------- /src/db.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_DB_H 2 | #define ED2KD_DB_H 3 | 4 | /* 5 | @file db.h Database stuff 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | struct evbuffer; 12 | struct client; 13 | struct file_source; 14 | 15 | #define MAX_FILENAME_LEN 255 16 | #define MAX_MCODEC_LEN 64 17 | #define MAX_FILEEXT_LEN 16 18 | 19 | struct pub_file { 20 | unsigned char hash[16]; 21 | uint16_t name_len; 22 | char name[MAX_FILENAME_LEN + 1]; 23 | uint64_t size; 24 | uint32_t rating; 25 | uint32_t type; 26 | uint32_t media_length; 27 | uint32_t media_bitrate; 28 | uint16_t media_codec_len; 29 | char media_codec[MAX_MCODEC_LEN + 1]; 30 | unsigned char complete; 31 | }; 32 | 33 | enum search_node_type { 34 | ST_EMPTY, 35 | // logical nodes 36 | ST_AND, 37 | ST_OR, 38 | ST_NOT, 39 | // string nodes 40 | ST_STRING, 41 | ST_EXTENSION, 42 | ST_CODEC, 43 | ST_TYPE, 44 | // int nodes 45 | ST_MINSIZE, 46 | ST_MAXSIZE, 47 | ST_SRCAVAIL, 48 | ST_SRCCOMLETE, 49 | ST_MINBITRATE, 50 | ST_MINLENGTH 51 | }; 52 | 53 | struct search_node { 54 | enum search_node_type type; 55 | struct search_node *parent; 56 | unsigned left_visited:1; 57 | unsigned right_visited:1; 58 | unsigned string_term:1; 59 | union { 60 | struct { 61 | uint16_t str_len; 62 | const char *str_val; 63 | }; 64 | uint64_t int_val; 65 | struct { 66 | struct search_node *left; 67 | struct search_node *right; 68 | }; 69 | }; 70 | }; 71 | 72 | /** 73 | @return non-zero on success 74 | */ 75 | int db_create(void); 76 | 77 | /** 78 | @return non-zero on success 79 | */ 80 | int db_destroy(void); 81 | 82 | /** 83 | @return non-zero on success 84 | */ 85 | int db_open(void); 86 | 87 | /** 88 | @return non-zero on success 89 | */ 90 | int db_close(void); 91 | 92 | /** 93 | @return non-zero on success 94 | */ 95 | int db_share_files(const struct pub_file *files, size_t count, const struct client *owner); 96 | 97 | /** 98 | @return non-zero on success 99 | */ 100 | int db_remove_source(const struct client *owner); 101 | 102 | /** 103 | @return non-zero on success 104 | */ 105 | int db_search_files(struct search_node *root, struct evbuffer *buf, size_t *count); 106 | 107 | /** 108 | @return non-zero on success 109 | */ 110 | int db_get_sources(const unsigned char *hash, struct file_source *out_sources, uint8_t *size); 111 | 112 | #endif // ED2KD_DB_H 113 | -------------------------------------------------------------------------------- /cmake/modules/FindLibevent.cmake: -------------------------------------------------------------------------------- 1 | # - Find libevent 2 | # Find the native libevent includes and library. 3 | # Once done this will define 4 | # 5 | # LIBEVENT_INCLUDE_DIRS - where to find event2/event.h, etc. 6 | # LIBEVENT_LIBRARIES - List of libraries when using libevent. 7 | # LIBEVENT_FOUND - True if libevent found. 8 | # 9 | # LIBEVENT_VERSION_STRING - The version of libevent found (x.y.z) 10 | # LIBEVENT_VERSION_MAJOR - The major version 11 | # LIBEVENT_VERSION_MINOR - The minor version 12 | # LIBEVENT_VERSION_PATCH - The patch version 13 | # 14 | # Avaliable components: 15 | # core 16 | # pthreads 17 | # extra 18 | # openssl 19 | # 20 | 21 | SET(LIBEVENT_core_LIBRARY "NOTREQ") 22 | SET(LIBEVENT_pthreads_LIBRARY "NOTREQ") 23 | SET(LIBEVENT_extra_LIBRARY "NOTREQ") 24 | SET(LIBEVENT_openssl_LIBRARY "NOTREQ") 25 | 26 | FIND_PATH(LIBEVENT_INCLUDE_DIR NAMES event2/event.h) 27 | 28 | IF(LIBEVENT_INCLUDE_DIR AND EXISTS "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h") 29 | # Read and parse libevent version header file for version number 30 | file(READ "${LIBEVENT_INCLUDE_DIR}/event2/event-config.h" _libevent_HEADER_CONTENTS) 31 | 32 | string(REGEX REPLACE ".*#define EVENT__VERSION +\"([0-9]+).*" "\\1" LIBEVENT_VERSION_MAJOR "${_libevent_HEADER_CONTENTS}") 33 | string(REGEX REPLACE ".*#define EVENT__VERSION +\"[0-9]+\\.([0-9]+).*" "\\1" LIBEVENT_VERSION_MINOR "${_libevent_HEADER_CONTENTS}") 34 | string(REGEX REPLACE ".*#define EVENT__VERSION +\"[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" LIBEVENT_VERSION_PATCH "${_libevent_HEADER_CONTENTS}") 35 | 36 | SET(LIBEVENT_VERSION_STRING "${LIBEVENT_VERSION_MAJOR}.${LIBEVENT_VERSION_MINOR}.${LIBEVENT_VERSION_PATCH}") 37 | ENDIF() 38 | 39 | FOREACH(component ${Libevent_FIND_COMPONENTS}) 40 | UNSET(LIBEVENT_${component}_LIBRARY) 41 | FIND_LIBRARY(LIBEVENT_${component}_LIBRARY NAMES event_${component}) 42 | ENDFOREACH() 43 | 44 | # handle the QUIETLY and REQUIRED arguments and set LIBEVENT_FOUND to TRUE if 45 | # all listed variables are TRUE 46 | INCLUDE(FindPackageHandleStandardArgs) 47 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Libevent 48 | REQUIRED_VARS LIBEVENT_core_LIBRARY LIBEVENT_pthreads_LIBRARY LIBEVENT_extra_LIBRARY LIBEVENT_openssl_LIBRARY LIBEVENT_INCLUDE_DIR 49 | VERSION_VAR LIBEVENT_VERSION_STRING 50 | ) 51 | 52 | IF(LIBEVENT_FOUND) 53 | SET(LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) 54 | FOREACH(component ${Libevent_FIND_COMPONENTS}) 55 | LIST(APPEND LIBEVENT_LIBRARIES ${LIBEVENT_${component}_LIBRARY}) 56 | ENDFOREACH() 57 | ENDIF() 58 | 59 | MARK_AS_ADVANCED(LIBEVENT_core_LIBRARY LIBEVENT_pthreads_LIBRARY LIBEVENT_extra_LIBRARY LIBEVENT_openssl_LIBRARY LIBEVENT_INCLUDE_DIR) -------------------------------------------------------------------------------- /src/listener.c: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "log.h" 10 | #include "client.h" 11 | #include "packet.h" 12 | 13 | static void accept_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *ctx) 14 | { 15 | struct sockaddr_in *sa_in; 16 | struct client *clnt; 17 | struct bufferevent *bev; 18 | 19 | (void) listener; 20 | (void) socklen; 21 | (void) ctx; 22 | 23 | assert(AF_INET == sa->sa_family); 24 | sa_in = (struct sockaddr_in *) sa; 25 | 26 | // todo: limit total client count 27 | // todo: limit connections from same ip 28 | // todo: block banned ips 29 | 30 | clnt = client_new(); 31 | 32 | bev = bufferevent_socket_new(g_srv.evbase_tcp, fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE); 33 | clnt->bev = bev; 34 | clnt->ip = sa_in->sin_addr.s_addr; 35 | 36 | #ifdef USE_DEBUG 37 | evutil_inet_ntop(AF_INET, &(clnt->ip), clnt->dbg.ip_str, sizeof(clnt->dbg.ip_str)); 38 | ED2KD_LOGDBG("got connection from ip:%s", clnt->dbg.ip_str); 39 | #endif 40 | 41 | bufferevent_setcb(clnt->bev, server_read_cb, NULL, server_event_cb, clnt); 42 | bufferevent_enable(clnt->bev, EV_READ | EV_WRITE); 43 | 44 | // todo: set timeout for op_login 45 | } 46 | 47 | static void accept_error_cb(struct evconnlistener *listener, void *ctx) 48 | { 49 | int err = EVUTIL_SOCKET_ERROR(); 50 | (void) listener; 51 | (void) ctx; 52 | 53 | ED2KD_LOGERR("tcp listener error %d (%s)", \ 54 | err, evutil_socket_error_to_string(err)); 55 | 56 | server_stop(); 57 | } 58 | 59 | int server_listen(void) 60 | { 61 | int ret; 62 | struct sockaddr_in bind_sa; 63 | int bind_sa_len; 64 | 65 | bind_sa_len = sizeof(bind_sa); 66 | memset(&bind_sa, 0, sizeof(bind_sa)); 67 | ret = evutil_parse_sockaddr_port(g_srv.cfg->listen_addr, (struct sockaddr *) &bind_sa, &bind_sa_len); 68 | if (ret < 0) { 69 | ED2KD_LOGERR("failed to parse listen addr '%s'", g_srv.cfg->listen_addr); 70 | return 0; 71 | } 72 | bind_sa.sin_port = htons(g_srv.cfg->listen_port); 73 | bind_sa.sin_family = AF_INET; 74 | 75 | g_srv.tcp_listener = evconnlistener_new_bind(g_srv.evbase_main, 76 | accept_cb, NULL, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 77 | g_srv.cfg->listen_backlog, (struct sockaddr *) &bind_sa, sizeof(bind_sa)); 78 | if (NULL == g_srv.tcp_listener) { 79 | int err = EVUTIL_SOCKET_ERROR(); 80 | ED2KD_LOGERR("failed to start listen on %s:%u, last error: %s", g_srv.cfg->listen_addr, g_srv.cfg->listen_port, evutil_socket_error_to_string(err)); 81 | return 0; 82 | } 83 | 84 | evconnlistener_set_error_cb(g_srv.tcp_listener, accept_error_cb); 85 | 86 | ED2KD_LOGNFO("start listening on %s:%u", g_srv.cfg->listen_addr, g_srv.cfg->listen_port); 87 | 88 | ret = event_base_dispatch(g_srv.evbase_main); 89 | if (ret < 0) 90 | ED2KD_LOGERR("main loop finished with error"); 91 | 92 | return 1; 93 | } 94 | 95 | -------------------------------------------------------------------------------- /src/client.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_CLIENT_H 2 | #define ED2KD_CLIENT_H 3 | 4 | #include 5 | #include 6 | #include "uthash/uthash.h" 7 | #include "atomic.h" 8 | #include "util.h" 9 | 10 | struct search_node; 11 | struct shared_file_entry; 12 | struct pub_file; 13 | 14 | #define MAX_NICK_LEN 255 15 | #define MAX_FOUND_SOURCES 200 // todo: move to config 16 | #define MAX_FOUND_FILES 200 // todo: move to config 17 | 18 | enum portcheck_result { 19 | PORTCHECK_FAILED, 20 | PORTCHECK_SUCCESS 21 | }; 22 | 23 | struct client { 24 | /* ed2k hash */ 25 | unsigned char hash[16]; 26 | /* ip address (network order) */ 27 | uint32_t ip; 28 | /* port (from OP_LOGIN) */ 29 | uint16_t port; 30 | /* ed2k id */ 31 | uint32_t id; 32 | /* nick */ 33 | char nick[MAX_NICK_LEN + 1]; 34 | /* nick length */ 35 | uint16_t nick_len; 36 | /* tcp flags */ 37 | uint32_t tcp_flags; 38 | /* shared files counter */ 39 | uint32_t file_count; 40 | /* remote port check status flag */ 41 | unsigned portcheck_finished:1; 42 | /* lowid flag */ 43 | unsigned lowid:1; 44 | /* set of already shared files hashes */ 45 | struct shared_file_entry *shared_files; 46 | 47 | /* connection bufferevent */ 48 | struct bufferevent *bev; 49 | /* portcheck bufferevent */ 50 | struct bufferevent *bev_pc; 51 | /* portcheck timeout timer */ 52 | struct event *evtimer_portcheck; 53 | /* status notify timer */ 54 | struct event *evtimer_status_notify; 55 | 56 | /* lock flag */ 57 | atomic_uint32_t locked; 58 | /* references counter */ 59 | atomic_uint32_t ref_cnt; 60 | /* marked for remove flag */ 61 | atomic_uint32_t deleted; 62 | 63 | /* offer limit */ 64 | struct token_bucket limit_offer; 65 | 66 | /* search limit */ 67 | struct token_bucket limit_search; 68 | 69 | #ifdef USE_DEBUG 70 | /* for debugging only */ 71 | struct { 72 | /* ip address string */ 73 | char ip_str[16]; 74 | } dbg; 75 | #endif 76 | }; 77 | 78 | /** 79 | @brief allocates and initializes empty client structure 80 | @return pointer to new client structure 81 | */ 82 | struct client *client_new(void); 83 | 84 | void client_delete(struct client *clnt); 85 | 86 | static __inline void client_addref(struct client *clnt) 87 | { 88 | atomic_fetch_add(&clnt->ref_cnt, 1); 89 | } 90 | 91 | static __inline void client_decref(struct client *clnt) 92 | { 93 | if (!atomic_fetch_sub(&clnt->ref_cnt, 1) - 1 && atomic_load(&clnt->deleted)) 94 | client_delete(clnt); 95 | } 96 | 97 | void client_portcheck_start(struct client *client); 98 | 99 | void client_portcheck_finish(struct client *clnt, enum portcheck_result result); 100 | 101 | void client_search_files(struct client *clnt, struct search_node *search_tree); 102 | 103 | void client_get_sources(struct client *clnt, const unsigned char *hash); 104 | 105 | void client_share_files(struct client *clnt, struct pub_file *files, size_t count); 106 | 107 | #endif // ED2KD_CLIENT_H 108 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | #include "ed2k_proto.h" 6 | 7 | int hex2bin(const char *src, unsigned char *dst, size_t dst_len) 8 | { 9 | size_t i; 10 | for (i = 0; i < dst_len; ++i) { 11 | dst[i] = 0; 12 | if (src[i] >= '0' && src[i] <= '9') 13 | dst[i] = 16 * (src[i] - '0'); 14 | else if (src[i] >= 'a' && src[i] <= 'f') 15 | dst[i] = 16 * (src[i] - 'a' + 10); 16 | else if (src[i] >= 'A' && src[i] <= 'F') 17 | dst[i] = 16 * (src[i] - 'A' + 10); 18 | else 19 | return -1; 20 | 21 | if (src[i + 1] >= '0' && src[i + 1] <= '9') 22 | dst[i] += src[i + 1] - '0'; 23 | else if (src[i + 1] >= 'a' && src[i + 1] <= 'f') 24 | dst[i] += src[i + 1] - 'a' + 10; 25 | else if (src[i + i] >= 'A' && src[i + 1] <= 'F') 26 | dst[i] += src[i + 1] - 'A' + 10; 27 | else 28 | return -1; 29 | } 30 | 31 | return 0; 32 | } 33 | 34 | int bin2hex(const unsigned char *src, char *dst, size_t dst_len) 35 | { 36 | size_t i, j; 37 | static unsigned char hex[] = "0123456789abcdef"; 38 | 39 | if (dst_len % 2 == 0) 40 | return -1; 41 | 42 | for (i = 0, j = 0; i < dst_len - 1; i += 2, j++) { 43 | dst[i] = hex[(src[j] >> 4) & 0xf]; 44 | dst[i + 1] = hex[src[j] & 0xf]; 45 | } 46 | dst[dst_len - 1] = '\0'; 47 | 48 | return 0; 49 | } 50 | 51 | void get_random_user_hash(unsigned char *hash) 52 | { 53 | evutil_secure_rng_get_bytes((char *) hash, ED2K_HASH_SIZE); 54 | hash[6] = 14; 55 | hash[15] = 111; 56 | } 57 | 58 | uint8_t get_ed2k_file_type(const char *type, size_t len) 59 | { 60 | if (strncmp(FTS_AUDIO, type, len) == 0) { 61 | return FT_AUDIO; 62 | } else if (strncmp(FTS_VIDEO, type, len) == 0) { 63 | return FT_VIDEO; 64 | } else if (strncmp(FTS_IMAGE, type, len) == 0) { 65 | return FT_IMAGE; 66 | } else if (strncmp(FTS_DOCUMENT, type, len) == 0) { 67 | return FT_DOCUMENT; 68 | } else if (strncmp(FTS_PROGRAM, type, len) == 0) { 69 | return FT_PROGRAM; 70 | } else if (strncmp(FTS_ARCHIVE, type, len) == 0) { 71 | return FT_ARCHIVE; 72 | } else if (strncmp(FTS_CDIMAGE, type, len) == 0) { 73 | return FT_CDIMAGE; 74 | } else if (strncmp(FTS_EMULECOLLECTION, type, len) == 0) { 75 | return FT_EMULECOLLECTION; 76 | } else { 77 | return FT_ANY; 78 | } 79 | } 80 | 81 | const char *file_extension(const char *name, size_t len) 82 | { 83 | const char *ext; 84 | 85 | if (!len) 86 | len = strlen(name); 87 | 88 | ext = name + len - 1; 89 | while ((name <= ext) && ('.' != *ext)) { 90 | ext--; 91 | } 92 | 93 | if (name == ext) 94 | ext = NULL; 95 | else 96 | ext++; 97 | 98 | return ext; 99 | } 100 | 101 | int token_bucket_update(struct token_bucket *bucket, double max_tokens) 102 | { 103 | time_t now = time(NULL); 104 | time_t passed = now - bucket->last_update; 105 | 106 | bucket->last_update = now; 107 | bucket->tokens += passed * max_tokens; 108 | if (bucket->tokens > max_tokens) 109 | bucket->tokens = max_tokens; 110 | if (bucket->tokens < 1.0) { 111 | return 0; 112 | } else { 113 | bucket->tokens -= 1.0; 114 | return 1; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/packet.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_PACKET_H 2 | #define ED2KD_PACKET_H 3 | 4 | #include 5 | #include 6 | 7 | struct bufferevent; 8 | struct evbuffer; 9 | struct file_source; 10 | 11 | struct search_file { 12 | const unsigned char *hash; 13 | uint32_t client_id; 14 | uint16_t client_port; 15 | uint16_t name_len; 16 | const char *name; 17 | uint64_t size; 18 | uint32_t type; 19 | uint32_t rating; 20 | uint32_t rated_count; 21 | uint16_t ext_len; 22 | const char *ext; 23 | uint32_t media_length; 24 | uint32_t media_bitrate; 25 | uint16_t media_codec_len; 26 | const char *media_codec; 27 | uint32_t srcavail; 28 | uint32_t srccomplete; 29 | }; 30 | 31 | void send_id_change(struct bufferevent *bev, uint32_t id); 32 | 33 | void send_server_message(struct bufferevent *bev, const char *msg, uint16_t len); 34 | 35 | void send_server_status(struct bufferevent *bev); 36 | 37 | void send_server_ident(struct bufferevent *bev); 38 | 39 | void send_server_list(struct bufferevent *bev); 40 | 41 | void send_reject(struct bufferevent *bev); 42 | 43 | void send_callback_fail(struct bufferevent *bev); 44 | 45 | void send_found_sources(struct bufferevent *bev, const unsigned char *hash, struct file_source *sources, size_t count); 46 | 47 | void send_search_result(struct bufferevent *bev, struct evbuffer *result, size_t count); 48 | 49 | void write_search_file(struct evbuffer *buf, const struct search_file *file); 50 | 51 | struct packet_buffer { 52 | const unsigned char *ptr; 53 | /**< current location pointer */ 54 | const unsigned char *end; /**< buffer end pinter */ 55 | }; 56 | 57 | #define PB_INIT(pb, buf, len) \ 58 | (pb)->ptr = (buf); \ 59 | (pb)->end = (buf) + (len); 60 | 61 | #define PB_END(pb) \ 62 | ((pb)->ptr < (pb)->end) 63 | 64 | #define PB_CHECK(stmt) \ 65 | if ( !(stmt) ) goto malformed 66 | 67 | #define PB_LEFT(pb) \ 68 | ((pb)->end - (pb)->ptr) 69 | 70 | #define PB_SEEK(pb, off) \ 71 | (pb)->ptr += off; \ 72 | PB_CHECK( (pb)->ptr <= (pb)->end ) 73 | 74 | #define PB_MEMCPY(pb, dst, len) \ 75 | memcpy((dst), (pb)->ptr, (len)); \ 76 | PB_SEEK((pb), (len)) 77 | 78 | #define PB_READ_UINT8(pb, val) \ 79 | (val) = *(uint8_t*)(pb)->ptr; \ 80 | PB_SEEK((pb), sizeof(uint8_t)) 81 | 82 | #define PB_READ_UINT16(pb, val) \ 83 | (val) = *(uint16_t*)(pb)->ptr; \ 84 | PB_SEEK((pb), sizeof(uint16_t)) 85 | 86 | #define PB_READ_UINT32(pb, val) \ 87 | (val) = *(uint32_t*)(pb)->ptr; \ 88 | PB_SEEK((pb), sizeof(uint32_t)) 89 | 90 | #define PB_READ_UINT64(pb, val) \ 91 | (val) = *(uint64_t*)(pb)->ptr; \ 92 | PB_SEEK((pb), sizeof(uint64_t)) 93 | 94 | #define PB_PTR_UINT8(pb) *(uint8_t*)(pb)->ptr 95 | #define PB_PTR_UINT16(pb) *(uint16_t*)(pb)->ptr 96 | #define PB_PTR_UINT32(pb) *(uint32_t*)(pb)->ptr 97 | #define PB_PTR_UINT64(pb) *(uint64_t*)(pb)->ptr 98 | 99 | #define PB_READ_STRING(pb, dst, max_len) \ 100 | { \ 101 | uint16_t _pb_len; \ 102 | PB_READ_UINT16((pb), _pb_len); \ 103 | (max_len) = _pb_len > (max_len) ? (max_len) : _pb_len; \ 104 | memcpy((dst), pb->ptr, (max_len)); \ 105 | PB_SEEK(pb, _pb_len); \ 106 | } 107 | 108 | #define PB_SKIP_TAGHDR_INT(pb) \ 109 | PB_SEEK((pb), sizeof(uint8_t)*2) 110 | 111 | #define PB_SKIP_TAGHDR(pb, hdr) \ 112 | PB_SEEK((pb), sizeof(uint8_t)+sizeof(uint16_t)+(hdr)->name_len) 113 | 114 | #endif // ED2KD_PACKET_H 115 | -------------------------------------------------------------------------------- /src/server.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_SERVER_H 2 | #define ED2KD_SERVER_H 3 | 4 | /** 5 | @file server.h General server configuration variables and routines 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include "job.h" 12 | #include "atomic.h" 13 | 14 | struct event_base; 15 | struct evconnlistener; 16 | struct bufferevent; 17 | struct client; 18 | 19 | #define MAX_WELCOMEMSG_LEN 1024 20 | #define MAX_SERVER_NAME_LEN 64 21 | #define MAX_SERVER_DESCR_LEN 64 22 | #define MAX_SEARCH_FILES 200 23 | #define MAX_UNCOMPRESSED_PACKET_SIZE 300*1024 24 | 25 | struct server_config { 26 | /* listen ip address */ 27 | char *listen_addr; 28 | 29 | /* listen ip address (parsed) */ 30 | uint32_t listen_addr_inaddr; 31 | 32 | /* listen port */ 33 | uint16_t listen_port; 34 | 35 | /* listen backlog */ 36 | int listen_backlog; 37 | 38 | /* server hash */ 39 | unsigned char hash[16]; 40 | 41 | /* server TCP capabilities flags */ 42 | uint32_t srv_tcp_flags; 43 | 44 | /* welcome message length */ 45 | size_t welcome_msg_len; 46 | /* welcome message */ 47 | char welcome_msg[MAX_WELCOMEMSG_LEN + 1]; 48 | 49 | /* server name length */ 50 | size_t server_name_len; 51 | /* server name */ 52 | char server_name[MAX_SERVER_NAME_LEN + 1]; 53 | 54 | /* server description length */ 55 | size_t server_descr_len; 56 | /* server description */ 57 | char server_descr[MAX_SERVER_DESCR_LEN + 1]; 58 | 59 | /* port check timeout */ 60 | struct timeval portcheck_timeout_tv; 61 | 62 | /* server status sending interval */ 63 | struct timeval status_notify_tv; 64 | 65 | /* maximum connected clients */ 66 | size_t max_clients; 67 | 68 | /* maximum shared files */ 69 | size_t max_files; 70 | 71 | /* maximum shared files per client */ 72 | size_t max_files_per_client; 73 | 74 | /* maximum offers limit */ 75 | size_t max_offers_limit; 76 | 77 | /* maximum searches limit */ 78 | size_t max_searches_limit; 79 | 80 | /* allow lowid clients flag */ 81 | unsigned allow_lowid:1; 82 | }; 83 | 84 | struct server_instance { 85 | /* general event base */ 86 | struct event_base *evbase_tcp; 87 | /* login event base */ 88 | struct event_base *evbase_main; 89 | /* tcp connection listener */ 90 | struct evconnlistener *tcp_listener; 91 | /* server configuration loaded from file */ 92 | const struct server_config *cfg; 93 | /* working threads count */ 94 | size_t thread_count; 95 | /* connected users count */ 96 | atomic_uint32_t user_count; 97 | /* shared files count */ 98 | atomic_uint32_t file_count; 99 | /* lowid counter */ 100 | atomic_uint32_t lowid_counter; 101 | 102 | /* termination flag */ 103 | atomic_uint32_t terminate; 104 | /* job queue mutex */ 105 | pthread_mutex_t job_mutex; 106 | /* job access condition */ 107 | pthread_cond_t job_cond; 108 | /* job queue */ 109 | struct job_queue jqueue; 110 | 111 | /* common timeval for port check timeout */ 112 | const struct timeval *portcheck_timeout_tv; 113 | /* common server status notify interval */ 114 | const struct timeval *status_notify_tv; 115 | }; 116 | 117 | extern struct server_instance g_srv; 118 | 119 | /** 120 | @brief loads server configuration from given file 121 | @param path full or relative path to configuration file 122 | @return non-zero on success 123 | */ 124 | int server_load_config(const char *path); 125 | 126 | /** 127 | @brief frees server configuration 128 | */ 129 | void server_free_config(void); 130 | 131 | /** 132 | @brief start main loop and accept incoming connections 133 | @return non-zero on success 134 | */ 135 | int server_listen(void); 136 | 137 | /** 138 | @brief breaks all running event loops 139 | */ 140 | void server_stop(void); 141 | 142 | /** 143 | @param arg 144 | @return 145 | */ 146 | void *server_base_worker(void *arg); 147 | 148 | /** 149 | @param ctx 150 | @return 151 | */ 152 | void *server_job_worker(void *ctx); 153 | 154 | /** 155 | @param job 156 | */ 157 | void server_add_job(struct job *job); 158 | 159 | #endif // ED2KD_SERVER_H 160 | -------------------------------------------------------------------------------- /src/portcheck.c: -------------------------------------------------------------------------------- 1 | #include "portcheck.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "server.h" 11 | #include "client.h" 12 | #include "log.h" 13 | #include "packet.h" 14 | #include "ed2k_proto.h" 15 | 16 | static void send_hello(struct client *clnt) 17 | { 18 | static const char name[] = {'e', 'd', '2', 'k', 'd'}; 19 | struct packet_hello data; 20 | evutil_socket_t fd; 21 | struct sockaddr_in sa; 22 | socklen_t sa_len; 23 | 24 | // get local ip addr 25 | fd = bufferevent_getfd(clnt->bev_pc); 26 | sa_len = sizeof(sa); 27 | getsockname(fd, (struct sockaddr *) &sa, &sa_len); 28 | 29 | data.hdr.proto = PROTO_EDONKEY; 30 | data.hdr.length = sizeof(data) - sizeof(data.hdr); 31 | data.opcode = OP_HELLO; 32 | data.hash_size = 16; 33 | memcpy(data.hash, g_srv.cfg->hash, sizeof data.hash); 34 | data.client_id = ntohl(sa.sin_addr.s_addr); 35 | data.client_port = 4662; 36 | data.tag_count = 2; 37 | data.tag_name.type = TT_STR5 | 0x80; 38 | data.tag_name.name = TN_NAME; 39 | memcpy(data.tag_name.value, name, sizeof(data.tag_name.value)); 40 | data.tag_version.type = TT_UINT8 | 0x80; 41 | data.tag_version.name = TN_VERSION; 42 | data.tag_version.value = EDONKEYVERSION; 43 | data.ip = 0; 44 | data.port = 0; 45 | 46 | bufferevent_write(clnt->bev_pc, &data, sizeof data); 47 | } 48 | 49 | static int process_hello_answer(struct packet_buffer *pb, struct client *clnt) 50 | { 51 | PB_CHECK(PB_LEFT(pb) > ED2K_HASH_SIZE); 52 | PB_CHECK(memcmp(clnt->hash, pb->ptr, ED2K_HASH_SIZE) == 0); 53 | 54 | return 1; 55 | 56 | malformed: 57 | return 0; 58 | } 59 | 60 | static int process_packet(struct packet_buffer *pb, uint8_t opcode, struct client *clnt) 61 | { 62 | switch (opcode) { 63 | case OP_HELLOANSWER: 64 | PB_CHECK(process_hello_answer(pb, clnt)); 65 | client_portcheck_finish(clnt, PORTCHECK_SUCCESS); 66 | break; 67 | } 68 | 69 | return 1; 70 | 71 | malformed: 72 | ED2KD_LOGDBG("malformed portcheck packet (opcode:%u)", opcode); 73 | return 0; 74 | } 75 | 76 | void portcheck_read(struct client *clnt) 77 | { 78 | struct evbuffer *input; 79 | size_t src_len; 80 | 81 | if (clnt->portcheck_finished) 82 | return; 83 | 84 | input = bufferevent_get_input(clnt->bev_pc); 85 | src_len = evbuffer_get_length(input); 86 | 87 | while (!clnt->deleted && src_len > sizeof(struct packet_header)) { 88 | unsigned char *data; 89 | struct packet_buffer pb; 90 | size_t packet_len; 91 | int ret; 92 | const struct packet_header *header = 93 | (struct packet_header *) evbuffer_pullup(input, sizeof(struct packet_header)); 94 | 95 | if ((PROTO_PACKED != header->proto) && (PROTO_EDONKEY != header->proto)) { 96 | ED2KD_LOGDBG("unknown packet protocol from %s:%u", clnt->dbg.ip_str, clnt->port); 97 | client_portcheck_finish(clnt, PORTCHECK_FAILED); 98 | return; 99 | } 100 | 101 | /* wait for full length packet */ 102 | packet_len = header->length + sizeof(*header); 103 | if (packet_len > src_len) 104 | return; 105 | 106 | data = evbuffer_pullup(input, packet_len); 107 | header = (struct packet_header *) data; 108 | data += sizeof(struct packet_header); 109 | 110 | if (PROTO_PACKED == header->proto) { 111 | unsigned long unpacked_len = MAX_UNCOMPRESSED_PACKET_SIZE; 112 | unsigned char *unpacked = (unsigned char *) malloc(unpacked_len); 113 | 114 | ret = uncompress(unpacked, &unpacked_len, data + 1, header->length - 1); 115 | if (Z_OK == ret) { 116 | PB_INIT(&pb, unpacked, unpacked_len); 117 | ret = process_packet(&pb, *data, clnt); 118 | } else { 119 | ED2KD_LOGDBG("failed to unpack packet from %s:%u", clnt->dbg.ip_str, clnt->port); 120 | ret = 0; 121 | } 122 | free(unpacked); 123 | } else { 124 | PB_INIT(&pb, data + 1, header->length - 1); 125 | ret = process_packet(&pb, *data, clnt); 126 | } 127 | 128 | if (!ret) { 129 | client_portcheck_finish(clnt, PORTCHECK_FAILED); 130 | return; 131 | } 132 | 133 | if (clnt->portcheck_finished) 134 | return; 135 | 136 | evbuffer_drain(input, packet_len); 137 | src_len = evbuffer_get_length(input); 138 | } 139 | } 140 | 141 | void portcheck_timeout(struct client *clnt) 142 | { 143 | if (clnt->portcheck_finished) 144 | return; 145 | 146 | ED2KD_LOGDBG("port check timeout for %s", clnt->dbg.ip_str); 147 | client_portcheck_finish(clnt, PORTCHECK_FAILED); 148 | } 149 | 150 | void portcheck_event(struct client *clnt, short events) 151 | { 152 | if (clnt->portcheck_finished) 153 | return; 154 | 155 | if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { 156 | client_portcheck_finish(clnt, PORTCHECK_FAILED); 157 | } else if (events & BEV_EVENT_CONNECTED) { 158 | bufferevent_enable(clnt->bev_pc, EV_READ | EV_WRITE); 159 | send_hello(clnt); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "version.h" 12 | #include "util.h" 13 | #include "log.h" 14 | #include "ed2k_proto.h" 15 | #include "server.h" 16 | #include "db.h" 17 | 18 | struct server_instance g_srv; 19 | 20 | // command line options 21 | static const char *optString = "vhg"; 22 | static const struct option longOpts[] = { 23 | {"version", no_argument, NULL, 'v'}, 24 | {"help", no_argument, NULL, 'h'}, 25 | {"genhash", no_argument, NULL, 'g'}, 26 | {NULL, no_argument, NULL, 0} 27 | }; 28 | 29 | static void display_version(void) 30 | { 31 | puts( 32 | "ed2kd v" ED2KD_VER_STR "\n" 33 | "Build on: "__DATE__ " " __TIME__ 34 | ); 35 | } 36 | 37 | static void display_usage(void) 38 | { 39 | puts( 40 | "Options:\n" 41 | "--help, -h\tshow this help\n" 42 | "--version, -v\tprint version\n" 43 | "--genhash, -g\tgenerate random ed2k hash" 44 | ); 45 | } 46 | 47 | static void sigint_cb(evutil_socket_t fd, short what, void *ctx) 48 | { 49 | (void) fd; 50 | (void) what; 51 | (void) ctx; 52 | ED2KD_LOGNFO("caught SIGINT, terminating..."); 53 | server_stop(); 54 | } 55 | 56 | static void display_libevent_info(void) 57 | { 58 | int i; 59 | const char **methods = event_get_supported_methods(); 60 | if (NULL == methods) { 61 | ED2KD_LOGERR("failed to get supported libevent methods"); 62 | return; 63 | } 64 | ED2KD_LOGNFO("using libevent %s. available methods are:", event_get_version()); 65 | for (i = 0; methods[i] != NULL; ++i) { 66 | ED2KD_LOGNFO(" %s", methods[i]); 67 | } 68 | } 69 | 70 | int main(int argc, char *argv[]) 71 | { 72 | size_t i; 73 | int ret, opt, longIndex = 0; 74 | struct event *evsig_int; 75 | pthread_t tcp_thread, *job_threads; 76 | 77 | if (evutil_secure_rng_init() < 0) { 78 | ED2KD_LOGERR("failed to seed random number generator"); 79 | return EXIT_FAILURE; 80 | } 81 | 82 | /* parse command line arguments */ 83 | opt = getopt_long(argc, argv, optString, longOpts, &longIndex); 84 | while (opt != -1) { 85 | switch (opt) { 86 | case 'v': 87 | display_version(); 88 | return EXIT_SUCCESS; 89 | 90 | case 'g': { 91 | unsigned char hash[ED2K_HASH_SIZE]; 92 | char hex_hash[sizeof(hash) * 2 + 1]; 93 | get_random_user_hash(hash); 94 | bin2hex(hash, hex_hash, sizeof(hex_hash)); 95 | puts(hex_hash); 96 | return EXIT_SUCCESS; 97 | } 98 | 99 | case 'h': 100 | display_usage(); 101 | return EXIT_SUCCESS; 102 | 103 | default: 104 | return EXIT_FAILURE; 105 | } 106 | opt = getopt_long(argc, argv, optString, longOpts, &longIndex); 107 | } 108 | 109 | if (!server_load_config(NULL)) { 110 | ED2KD_LOGERR("failed to load configuration file"); 111 | return EXIT_FAILURE; 112 | } 113 | 114 | display_libevent_info(); 115 | 116 | ret = evthread_use_pthreads(); 117 | if (ret < 0) { 118 | ED2KD_LOGERR("failed to init libevent threading model"); 119 | return EXIT_FAILURE; 120 | } 121 | 122 | g_srv.evbase_main = event_base_new(); 123 | if (NULL == g_srv.evbase_main) { 124 | ED2KD_LOGERR("failed to create main event loop"); 125 | return EXIT_FAILURE; 126 | } 127 | g_srv.evbase_tcp = event_base_new(); 128 | if (NULL == g_srv.evbase_tcp) { 129 | ED2KD_LOGERR("failed to create tcp event loop"); 130 | return EXIT_FAILURE; 131 | } 132 | 133 | evsig_int = evsignal_new(g_srv.evbase_main, SIGINT, sigint_cb, NULL); 134 | evsignal_add(evsig_int, NULL); 135 | 136 | // common timers timevals 137 | g_srv.portcheck_timeout_tv = event_base_init_common_timeout(g_srv.evbase_tcp, &g_srv.cfg->portcheck_timeout_tv); 138 | g_srv.status_notify_tv = event_base_init_common_timeout(g_srv.evbase_tcp, &g_srv.cfg->status_notify_tv); 139 | 140 | if (!db_create()) { 141 | ED2KD_LOGERR("failed to create database"); 142 | return EXIT_FAILURE; 143 | } 144 | 145 | g_srv.thread_count = omp_get_num_procs() + 1; 146 | 147 | pthread_cond_init(&g_srv.job_cond, NULL); 148 | pthread_mutex_init(&g_srv.job_mutex, NULL); 149 | TAILQ_INIT(&g_srv.jqueue); 150 | 151 | job_threads = (pthread_t *) malloc(g_srv.thread_count * sizeof(*job_threads)); 152 | 153 | // start tcp worker threads 154 | for (i = 0; i < g_srv.thread_count; ++i) { 155 | pthread_create(&job_threads[i], NULL, server_job_worker, NULL); 156 | } 157 | 158 | // start tcp dispatch thread 159 | pthread_create(&tcp_thread, NULL, server_base_worker, g_srv.evbase_tcp); 160 | 161 | // start tcp listen loop 162 | if (!server_listen()) { 163 | ED2KD_LOGERR("failed to start server listener"); 164 | server_stop(); 165 | } 166 | 167 | pthread_join(tcp_thread, NULL); 168 | 169 | while (EBUSY == pthread_cond_destroy(&g_srv.job_cond)) { 170 | pthread_cond_broadcast(&g_srv.job_cond); 171 | //pthread_yield(); 172 | } 173 | 174 | pthread_mutex_destroy(&g_srv.job_mutex); 175 | 176 | for (i = 0; i < g_srv.thread_count; ++i) { 177 | pthread_join(job_threads[i], NULL); 178 | } 179 | 180 | free(job_threads); 181 | 182 | // todo: free job queue items 183 | 184 | evconnlistener_free(g_srv.tcp_listener); 185 | event_free(evsig_int); 186 | event_base_free(g_srv.evbase_tcp); 187 | event_base_free(g_srv.evbase_main); 188 | 189 | if (db_destroy() < 0) { 190 | ED2KD_LOGERR("failed to destroy database"); 191 | } 192 | 193 | server_free_config(); 194 | 195 | return EXIT_SUCCESS; 196 | } 197 | -------------------------------------------------------------------------------- /src/client.c: -------------------------------------------------------------------------------- 1 | #include "client.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ed2k_proto.h" 9 | #include "server.h" 10 | #include "packet.h" 11 | #include "log.h" 12 | #include "db.h" 13 | 14 | struct shared_file_entry { 15 | /* key */ 16 | unsigned char hash[ED2K_HASH_SIZE]; 17 | /* makes this structure hashable */ 18 | UT_hash_handle hh; 19 | }; 20 | 21 | static uint32_t get_next_lowid(void) 22 | { 23 | uint32_t old_id, new_id; 24 | 25 | do { 26 | old_id = atomic_load(&g_srv.lowid_counter); 27 | new_id = old_id + 1; 28 | if (new_id > MAX_LOWID) 29 | new_id = 1; 30 | } while (!atomic_compare_exchange_strong(&g_srv.lowid_counter, &old_id, new_id)); 31 | 32 | return new_id; 33 | } 34 | 35 | struct client *client_new(void) 36 | { 37 | struct client *clnt = (struct client *) calloc(1, sizeof(*clnt)); 38 | 39 | if (atomic_fetch_add(&g_srv.user_count, 1) + 1 >= g_srv.cfg->max_clients) { 40 | evconnlistener_disable(g_srv.tcp_listener); 41 | } 42 | 43 | token_bucket_init(&clnt->limit_offer, g_srv.cfg->max_offers_limit); 44 | token_bucket_init(&clnt->limit_search, g_srv.cfg->max_searches_limit); 45 | 46 | return clnt; 47 | } 48 | 49 | void client_delete(struct client *clnt) 50 | { 51 | uint32_t old_val = 0; 52 | 53 | if (atomic_compare_exchange_strong(&clnt->deleted, &old_val, 1)) { 54 | // disable all events 55 | if (clnt->bev) 56 | bufferevent_disable(clnt->bev, EV_READ | EV_WRITE); 57 | if (clnt->bev_pc) 58 | bufferevent_disable(clnt->bev_pc, EV_READ | EV_WRITE); 59 | if (clnt->evtimer_status_notify) 60 | event_del(clnt->evtimer_status_notify); 61 | if (clnt->evtimer_portcheck) 62 | event_del(clnt->evtimer_portcheck); 63 | 64 | // delete all events 65 | if (clnt->evtimer_status_notify) { 66 | event_free(clnt->evtimer_status_notify); 67 | clnt->evtimer_status_notify = NULL; 68 | } 69 | if (clnt->evtimer_portcheck) { 70 | event_free(clnt->evtimer_portcheck); 71 | clnt->evtimer_portcheck = NULL; 72 | } 73 | if (clnt->bev_pc) { 74 | bufferevent_free(clnt->bev_pc); 75 | clnt->bev_pc = NULL; 76 | } 77 | if (clnt->bev) { 78 | bufferevent_free(clnt->bev); 79 | clnt->bev = NULL; 80 | } 81 | 82 | ED2KD_LOGDBG("client removed (%s:%d)", clnt->dbg.ip_str, clnt->port); 83 | 84 | if (clnt->file_count) { 85 | db_remove_source(clnt); 86 | atomic_fetch_sub(&g_srv.file_count, clnt->file_count); 87 | clnt->file_count = 0; 88 | } 89 | 90 | struct shared_file_entry *she, *she_tmp; 91 | HASH_ITER(hh, clnt->shared_files, she, she_tmp) { 92 | HASH_DEL(clnt->shared_files, she); 93 | free(she); 94 | } 95 | 96 | if (atomic_fetch_sub(&g_srv.user_count, 1) - 1 < g_srv.cfg->max_clients) { 97 | evconnlistener_enable(g_srv.tcp_listener); 98 | } 99 | } 100 | 101 | if (0 == atomic_load(&clnt->ref_cnt)) { 102 | free(clnt); 103 | } 104 | } 105 | 106 | void client_search_files(struct client *clnt, struct search_node *search_tree) 107 | { 108 | size_t count = MAX_SEARCH_FILES; 109 | struct evbuffer *buf = evbuffer_new(); 110 | struct packet_search_result data; 111 | 112 | data.hdr.proto = PROTO_EDONKEY; 113 | //data.length = 0; 114 | data.opcode = OP_SEARCHRESULT; 115 | //data.files_count = 0; 116 | evbuffer_add(buf, &data, sizeof(data)); 117 | 118 | if (db_search_files(search_tree, buf, &count)) { 119 | struct packet_search_result *ph = (struct packet_search_result *) evbuffer_pullup(buf, sizeof(*ph)); 120 | ph->hdr.length = evbuffer_get_length(buf) - sizeof(ph->hdr); 121 | ph->files_count = count; 122 | 123 | bufferevent_write_buffer(clnt->bev, buf); 124 | } 125 | 126 | evbuffer_free(buf); 127 | } 128 | 129 | void client_get_sources(struct client *clnt, const unsigned char *hash) 130 | { 131 | struct file_source sources[MAX_FOUND_SOURCES]; 132 | uint8_t src_count = ARRAY_SIZE(sources); 133 | 134 | if (db_get_sources(hash, sources, &src_count)) 135 | send_found_sources(clnt->bev, hash, sources, src_count); 136 | } 137 | 138 | void client_portcheck_start(struct client *clnt) 139 | { 140 | struct sockaddr_in client_sa; 141 | 142 | memset(&client_sa, 0, sizeof(client_sa)); 143 | client_sa.sin_family = AF_INET; 144 | client_sa.sin_addr.s_addr = clnt->ip; 145 | client_sa.sin_port = htons(clnt->port); 146 | 147 | clnt->bev_pc = bufferevent_socket_new(g_srv.evbase_tcp, -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE); 148 | bufferevent_setcb(clnt->bev_pc, portcheck_read_cb, NULL, portcheck_event_cb, clnt); 149 | 150 | if (bufferevent_socket_connect(clnt->bev_pc, (struct sockaddr *) &client_sa, sizeof(client_sa)) < 0) { 151 | bufferevent_free(clnt->bev_pc); 152 | clnt->bev_pc = NULL; 153 | client_portcheck_finish(clnt, PORTCHECK_FAILED); 154 | } else { 155 | clnt->evtimer_portcheck = evtimer_new(g_srv.evbase_tcp, portcheck_timeout_cb, clnt); 156 | evtimer_add(clnt->evtimer_portcheck, g_srv.portcheck_timeout_tv); 157 | } 158 | } 159 | 160 | void client_portcheck_finish(struct client *clnt, enum portcheck_result result) 161 | { 162 | if (clnt->bev_pc) { 163 | bufferevent_free(clnt->bev_pc); 164 | clnt->bev_pc = NULL; 165 | } 166 | if (clnt->evtimer_portcheck) { 167 | event_free(clnt->evtimer_portcheck); 168 | clnt->evtimer_portcheck = NULL; 169 | } 170 | clnt->portcheck_finished = 1; 171 | clnt->lowid = (PORTCHECK_SUCCESS != result); 172 | 173 | if (clnt->lowid) { 174 | static const char msg[] = "WARNING: You have a lowid. Please review your network config and/or your settings."; 175 | send_server_message(clnt->bev, msg, sizeof(msg) - 1); 176 | ED2KD_LOGDBG("port check failed (%s:%d)", clnt->dbg.ip_str, clnt->port); 177 | } 178 | 179 | if (clnt->lowid) { 180 | if (g_srv.cfg->allow_lowid) { 181 | clnt->id = get_next_lowid(); 182 | clnt->port = 0; 183 | } else { 184 | client_delete(clnt); 185 | return; 186 | } 187 | } else { 188 | clnt->id = clnt->ip; 189 | } 190 | 191 | send_id_change(clnt->bev, clnt->id); 192 | 193 | clnt->evtimer_status_notify = evtimer_new(g_srv.evbase_tcp, server_status_notify_cb, clnt); 194 | event_add(clnt->evtimer_status_notify, g_srv.status_notify_tv); 195 | } 196 | 197 | void client_share_files(struct client *clnt, struct pub_file *files, size_t count) 198 | { 199 | size_t i, real_count = 0; 200 | struct pub_file *f = files; 201 | 202 | if (clnt->file_count > g_srv.cfg->max_files_per_client) { 203 | static const char msg[] = "WARNING: You reached shared files limit"; 204 | send_server_message(clnt->bev, msg, sizeof(msg) - 1); 205 | return; 206 | } 207 | 208 | if (atomic_load(&g_srv.file_count) > g_srv.cfg->max_files) { 209 | static const char msg[] = "WARNING: Server reached shared files limit"; 210 | send_server_message(clnt->bev, msg, sizeof(msg) - 1); 211 | return; 212 | } 213 | 214 | for (i = 0; i < count; ++i) { 215 | struct shared_file_entry *she = NULL; 216 | HASH_FIND(hh, clnt->shared_files, f->hash, sizeof(f->hash), she); 217 | if (!she) { 218 | she = (struct shared_file_entry *) malloc(sizeof(*she)); 219 | memcpy(she->hash, f->hash, sizeof(she->hash)); 220 | HASH_ADD(hh, clnt->shared_files, hash, sizeof(she->hash), she); 221 | real_count++; 222 | } else { 223 | /* mark as invalid */ 224 | f->name_len = 0; 225 | } 226 | 227 | f++; 228 | } 229 | 230 | if (db_share_files(files, count, clnt)) { 231 | ED2KD_LOGDBG("client %u: published %u files, %u duplicates", clnt->id, count, count - real_count); 232 | clnt->file_count += real_count; 233 | atomic_fetch_add(&g_srv.file_count, real_count); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include "log.h" 7 | #include "ed2k_proto.h" 8 | #include "version.h" 9 | #include "util.h" 10 | 11 | #define CFG_DEFAULT_PATH "ed2kd.conf" 12 | 13 | #define CFG_LISTEN_ADDR "listen_addr" 14 | #define CFG_LISTEN_PORT "listen_port" 15 | #define CFG_LISTEN_BACKLOG "listen_backlog" 16 | #define CFG_WELCOME_MESSAGE "welcome_message" 17 | #define CFG_SERVER_HASH "server_hash" 18 | #define CFG_SERVER_NAME "server_name" 19 | #define CFG_SERVER_DESCR "server_descr" 20 | #define CFG_ALLOW_LOWID "allow_lowid" 21 | #define CFG_PORTCHECK_TIMEOUT "portcheck_timeout" 22 | #define CFG_STATUS_NOTIFY_INTERVAL "status_notify_interval" 23 | #define CFG_MAX_CLIENTS "max_clients" 24 | #define CFG_MAX_FILES "max_files" 25 | #define CFG_MAX_FILES_PER_CLIENT "max_files_per_client" 26 | #define CFG_MAX_OFFERS_LIMIT "max_offers_limit" 27 | #define CFG_MAX_SEARCHES_LIMIT "max_searches_limit" 28 | 29 | int server_load_config(const char *path) 30 | { 31 | static const char srv_ver[] = "server version" ED2KD_VER_STR " (ed2kd)"; 32 | config_t config; 33 | int ret = 1; 34 | struct server_config *server_cfg = (struct server_config *) malloc(sizeof(*server_cfg)); 35 | 36 | config_init(&config); 37 | memset(server_cfg, 0, sizeof(*server_cfg)); 38 | 39 | if (NULL == path) { 40 | path = CFG_DEFAULT_PATH; 41 | } 42 | 43 | if (config_read_file(&config, path)) { 44 | config_setting_t *root; 45 | const char *str_val; 46 | int int_val; 47 | 48 | root = config_root_setting(&config); 49 | 50 | /* listen address */ 51 | if (config_setting_lookup_string(root, CFG_LISTEN_ADDR, &str_val)) { 52 | server_cfg->listen_addr = strdup(str_val); 53 | } else { 54 | ED2KD_LOGERR("config: " 55 | CFG_LISTEN_ADDR 56 | " missing"); 57 | ret = 0; 58 | } 59 | 60 | /* listen port */ 61 | if (config_setting_lookup_int(root, CFG_LISTEN_PORT, &int_val)) { 62 | server_cfg->listen_port = (uint16_t) int_val; 63 | } else { 64 | ED2KD_LOGERR("config: " 65 | CFG_LISTEN_PORT 66 | " missing"); 67 | ret = 0; 68 | } 69 | 70 | /* listen backlog */ 71 | if (config_setting_lookup_int(root, CFG_LISTEN_BACKLOG, &int_val)) { 72 | server_cfg->listen_backlog = int_val; 73 | } else { 74 | ED2KD_LOGERR("config: " 75 | CFG_LISTEN_BACKLOG 76 | " missing"); 77 | ret = 0; 78 | } 79 | 80 | /* (optional) welcome message + predefined server version */ 81 | if (config_setting_lookup_string(root, CFG_WELCOME_MESSAGE, &str_val)) { 82 | server_cfg->welcome_msg_len = sizeof(srv_ver) + strlen(str_val) + 1; 83 | evutil_snprintf(server_cfg->welcome_msg, sizeof(server_cfg->welcome_msg), "%s\n%s", srv_ver, str_val); 84 | } else { 85 | server_cfg->welcome_msg_len = sizeof(srv_ver) - sizeof(char); 86 | strcpy(server_cfg->welcome_msg, srv_ver); 87 | } 88 | 89 | /* server hash */ 90 | if (config_setting_lookup_string(root, CFG_SERVER_HASH, &str_val)) { 91 | hex2bin(str_val, server_cfg->hash, ED2K_HASH_SIZE); 92 | } else { 93 | ED2KD_LOGERR("config: " 94 | CFG_SERVER_HASH 95 | " missing"); 96 | ret = 0; 97 | } 98 | 99 | /* server name (optional) */ 100 | if (config_setting_lookup_string(root, CFG_SERVER_NAME, &str_val)) { 101 | size_t len = strlen(str_val) - 1; 102 | server_cfg->server_name_len = MAX_SERVER_NAME_LEN > len ? len : MAX_SERVER_NAME_LEN; 103 | strncpy(server_cfg->server_name, str_val, server_cfg->server_name_len + 1); 104 | } 105 | 106 | /* server description (optional) */ 107 | if (config_setting_lookup_string(root, CFG_SERVER_DESCR, &str_val)) { 108 | size_t len = strlen(str_val) - 1; 109 | server_cfg->server_descr_len = MAX_SERVER_DESCR_LEN > len ? len : MAX_SERVER_DESCR_LEN; 110 | strncpy(server_cfg->server_descr, str_val, server_cfg->server_descr_len + 1); 111 | } 112 | 113 | /* allow lowid */ 114 | if (config_setting_lookup_int(root, CFG_ALLOW_LOWID, &int_val)) { 115 | server_cfg->allow_lowid = (int_val != 0); 116 | } else { 117 | ED2KD_LOGERR("config: " 118 | CFG_ALLOW_LOWID 119 | " missing"); 120 | ret = 0; 121 | } 122 | 123 | /* port check timeout */ 124 | if (config_setting_lookup_int(root, CFG_PORTCHECK_TIMEOUT, &int_val)) { 125 | server_cfg->portcheck_timeout_tv.tv_sec = int_val / 1000; 126 | server_cfg->portcheck_timeout_tv.tv_usec = (int_val % 1000) * 1000; 127 | } else { 128 | ED2KD_LOGERR("config: " 129 | CFG_PORTCHECK_TIMEOUT 130 | " missing"); 131 | ret = 0; 132 | } 133 | 134 | /* status notify interval */ 135 | if (config_setting_lookup_int(root, CFG_STATUS_NOTIFY_INTERVAL, &int_val)) { 136 | server_cfg->status_notify_tv.tv_sec = int_val / 1000; 137 | server_cfg->status_notify_tv.tv_usec = (int_val % 1000) * 1000; 138 | } else { 139 | ED2KD_LOGERR("config: " 140 | CFG_STATUS_NOTIFY_INTERVAL 141 | " missing"); 142 | ret = 0; 143 | } 144 | 145 | /* max clients */ 146 | if (config_setting_lookup_int(root, CFG_MAX_CLIENTS, &int_val)) { 147 | server_cfg->max_clients = int_val; 148 | } else { 149 | ED2KD_LOGERR("config: " 150 | CFG_MAX_CLIENTS 151 | " missing"); 152 | ret = 0; 153 | } 154 | 155 | /* max files */ 156 | if (config_setting_lookup_int(root, CFG_MAX_FILES, &int_val)) { 157 | server_cfg->max_files = int_val; 158 | } else { 159 | ED2KD_LOGERR("config: " 160 | CFG_MAX_FILES 161 | " missing"); 162 | ret = 0; 163 | } 164 | 165 | /* max files per client */ 166 | if (config_setting_lookup_int(root, CFG_MAX_FILES_PER_CLIENT, &int_val)) { 167 | server_cfg->max_files_per_client = int_val; 168 | } else { 169 | ED2KD_LOGERR("config: " 170 | CFG_MAX_FILES_PER_CLIENT 171 | " missing"); 172 | ret = 0; 173 | } 174 | 175 | /* max offers limit */ 176 | if (config_setting_lookup_int(root, CFG_MAX_OFFERS_LIMIT, &int_val)) { 177 | server_cfg->max_offers_limit = int_val; 178 | } else { 179 | ED2KD_LOGERR("config: " 180 | CFG_MAX_OFFERS_LIMIT 181 | " missing"); 182 | ret = 0; 183 | } 184 | 185 | /* max searches limit */ 186 | if (config_setting_lookup_int(root, CFG_MAX_SEARCHES_LIMIT, &int_val)) { 187 | server_cfg->max_searches_limit = int_val; 188 | } else { 189 | ED2KD_LOGERR("config: " 190 | CFG_MAX_SEARCHES_LIMIT 191 | " missing"); 192 | ret = 0; 193 | } 194 | } else { 195 | ED2KD_LOGWRN("config: failed to parse %s(error:%s at %d line)", path, 196 | config_error_text(&config), config_error_line(&config)); 197 | ret = 0; 198 | } 199 | 200 | config_destroy(&config); 201 | 202 | if (!ret) { 203 | free(server_cfg); 204 | } else { 205 | server_cfg->srv_tcp_flags = SRV_TCPFLG_COMPRESSION | SRV_TCPFLG_TYPETAGINTEGER | SRV_TCPFLG_LARGEFILES; 206 | evutil_inet_pton(AF_INET, server_cfg->listen_addr, &server_cfg->listen_addr_inaddr); 207 | g_srv.cfg = server_cfg; 208 | } 209 | 210 | return ret; 211 | } 212 | 213 | void server_free_config(void) 214 | { 215 | struct server_config *cfg = (struct server_config *) g_srv.cfg; 216 | g_srv.cfg = NULL; 217 | free(cfg->listen_addr); 218 | free(cfg); 219 | } 220 | -------------------------------------------------------------------------------- /src/ed2k_proto.h: -------------------------------------------------------------------------------- 1 | #ifndef ED2KD_ED2K_PROTO_H 2 | #define ED2KD_ED2K_PROTO_H 3 | 4 | /* 5 | @file ed2k_proto.h eDonkey2000 protocol details 6 | */ 7 | 8 | #include 9 | 10 | #define MAX_LOWID 0x1000000 11 | #define EDONKEYVERSION 0x3c 12 | #define ED2K_HASH_SIZE 16 13 | 14 | // server TCP flags 15 | #define SRV_TCPFLG_COMPRESSION 0x00000001 16 | #define SRV_TCPFLG_NEWTAGS 0x00000008 17 | #define SRV_TCPFLG_UNICODE 0x00000010 18 | #define SRV_TCPFLG_RELATEDSEARCH 0x00000040 19 | #define SRV_TCPFLG_TYPETAGINTEGER 0x00000080 20 | #define SRV_TCPFLG_LARGEFILES 0x00000100 21 | #define SRV_TCPFLG_TCPOBFUSCATION 0x00000400 22 | 23 | // capabilities, values for TN_SERVER_FLAGS 24 | enum client_caps { 25 | CLI_CAP_ZLIB = 0x0001, 26 | CLI_CAP_IPINLOGIN = 0x0002, 27 | CLI_CAP_AUXPORT = 0x0004, 28 | CLI_CAP_NEWTAGS = 0x0008, 29 | CLI_CAP_UNICODE = 0x0010, 30 | CLI_CAP_LARGEFILES = 0x0100, 31 | CLI_CAP_SUPPORTCRYPT = 0x0200, 32 | CLI_CAP_REQUESTCRYPT = 0x0400, 33 | CLI_CAP_REQUIRECRYPT = 0x0800 34 | }; 35 | 36 | enum packet_proto { 37 | PROTO_EDONKEY = 0xE3, 38 | PROTO_EMULE = 0xC5, 39 | PROTO_PACKED = 0xD4 40 | }; 41 | 42 | enum packet_opcode { 43 | //client2client 44 | OP_HELLO = 0x01, // [tags...] 45 | OP_HELLOANSWER = 0x4C, // [tags...] 46 | 47 | //client2server 48 | OP_LOGINREQUEST = 0x01, // [tags...] 49 | OP_REJECT = 0x05, // empty 50 | OP_GETSERVERLIST = 0x14, // empty 51 | OP_OFFERFILES = 0x15, // [[tags...]...] 52 | OP_SEARCHREQUEST = 0x16, // 53 | OP_DISCONNECT = 0x18, // empty 54 | OP_GETSOURCES = 0x19, // 55 | // //v2 (17.3) (mandatory on 17.8) 56 | // //v2large (17.9) (large files only) 57 | //OP_SEARCH_USER = 0x1A, // 58 | OP_CALLBACKREQUEST = 0x1C, // 59 | //OP_QUERY_CHATS = 0x1D, // 60 | //OP_CHAT_MESSAGE = 0x1E, // 61 | //OP_JOIN_ROOM = 0x1F, // 62 | OP_QUERY_MORE_RESULT = 0x21, // empty 63 | OP_GETSOURCES_OBFU = 0x23, // 64 | //OP_SERVERLIST = 0x32, // 65 | OP_SEARCHRESULT = 0x33, // [[tags...]...] 66 | OP_SERVERSTATUS = 0x34, // 67 | //OP_CALLBACKREQUESTED = 0x35, // 68 | OP_CALLBACK_FAIL = 0x36, // 69 | OP_SERVERMESSAGE = 0x38, // 70 | //OP_CHAT_ROOM_REQUEST = 0x39, // 71 | //OP_CHAT_BROADCAST = 0x3A, // 72 | //OP_CHAT_USER_JOIN = 0x3B, // 73 | //OP_CHAT_USER_LEAVE = 0x3C, // 74 | //OP_CHAT_USER = 0x3D, // 75 | OP_IDCHANGE = 0x40, // 76 | OP_SERVERIDENT = 0x41, // [tags...] 77 | OP_FOUNDSOURCES = 0x42 // ()[count] 78 | //OP_USERS_LIST = 0x43, 79 | //OP_FOUNDSOURCES_OBFU = 0x44 80 | }; 81 | 82 | struct file_source { 83 | uint32_t ip; 84 | uint16_t port; 85 | } __attribute__((__packed__)); 86 | 87 | struct packet_header { 88 | uint8_t proto; 89 | uint32_t length; 90 | } __attribute__((__packed__)); 91 | 92 | struct packet_server_message { 93 | struct packet_header hdr; 94 | uint8_t opcode; 95 | uint16_t msg_len; 96 | } __attribute__((__packed__)); 97 | 98 | struct packet_id_change { 99 | struct packet_header hdr; 100 | uint8_t opcode; 101 | uint32_t user_id; 102 | uint32_t tcp_flags; 103 | } __attribute__((__packed__)); 104 | 105 | struct packet_server_status { 106 | struct packet_header hdr; 107 | uint8_t opcode; 108 | uint32_t user_count; 109 | uint32_t file_count; 110 | } __attribute__((__packed__)); 111 | 112 | struct packet_server_ident { 113 | struct packet_header hdr; 114 | uint8_t opcode; 115 | unsigned char hash[ED2K_HASH_SIZE]; 116 | uint32_t ip; 117 | uint16_t port; 118 | uint32_t tag_count; 119 | } __attribute__((__packed__)); 120 | 121 | struct packet_search_result { 122 | struct packet_header hdr; 123 | uint8_t opcode; 124 | uint32_t files_count; 125 | } __attribute__((__packed__)); 126 | 127 | struct search_file_entry { 128 | unsigned char hash[ED2K_HASH_SIZE]; 129 | uint32_t id; 130 | uint16_t port; 131 | uint32_t tag_count; 132 | } __attribute__((__packed__)); 133 | 134 | struct packet_found_sources { 135 | struct packet_header hdr; 136 | uint8_t opcode; 137 | unsigned char hash[ED2K_HASH_SIZE]; 138 | uint8_t count; 139 | } __attribute__((__packed__)); 140 | 141 | struct packet_hello { 142 | struct packet_header hdr; 143 | uint8_t opcode; 144 | uint8_t hash_size; 145 | unsigned char hash[16]; 146 | uint32_t client_id; 147 | uint16_t client_port; 148 | uint32_t tag_count; 149 | struct { 150 | uint8_t type; // TT_STR5 151 | uint8_t name; // TN_NAME 152 | char value[5]; //ed2kd 153 | } tag_name; 154 | struct { 155 | uint8_t type; // TT_UINT8 & 0x7f 156 | uint8_t name; // TN_VERSION 157 | uint8_t value; // EDONKEYPROTO 158 | } tag_version; 159 | uint32_t ip; // 0 160 | uint16_t port; //0 161 | } __attribute__((__packed__)); 162 | 163 | struct tag_header { 164 | uint8_t type; 165 | uint16_t name_len; 166 | unsigned char name[1]; 167 | } __attribute__((__packed__)); 168 | 169 | struct tag_strval { 170 | uint16_t len; 171 | unsigned char str[1]; 172 | } __attribute__((__packed__)); 173 | 174 | struct short_tag { 175 | uint8_t type; 176 | uint8_t name; 177 | unsigned char data[1]; 178 | } __attribute__((__packed__)); 179 | 180 | enum tag_type { 181 | TT_HASH16 = 0x01, 182 | TT_STRING = 0x02, 183 | TT_UINT32 = 0x03, 184 | TT_FLOAT32 = 0x04, 185 | //TAGTYPE_BOOL = 0x05, 186 | //TAGTYPE_BOOLARRAY = 0x06, 187 | //TAGTYPE_BLOB = 0x07, 188 | TT_UINT16 = 0x08, 189 | TT_UINT8 = 0x09, 190 | //TAGTYPE_BSOB = 0x0A, 191 | TT_UINT64 = 0x0B, 192 | 193 | // Compressed string types 194 | TT_STR1 = 0x11, 195 | TT_STR2, 196 | TT_STR3, 197 | TT_STR4, 198 | TT_STR5, 199 | TT_STR6, 200 | TT_STR7, 201 | TT_STR8, 202 | TT_STR9, 203 | TT_STR10, 204 | TT_STR11, 205 | TT_STR12, 206 | TT_STR13, 207 | TT_STR14, 208 | TT_STR15, 209 | TT_STR16 210 | }; 211 | 212 | enum tag_name { 213 | // OP_LOGINREQUEST 214 | TN_NAME = 0x01, 215 | TN_PORT = 0x0F, 216 | TN_VERSION = 0x11, 217 | TN_SERVER_FLAGS = 0x20, 218 | TN_EMULE_VERSION = 0xFB, 219 | 220 | // OP_OFFERFILES, OP_SEARCHRESULT 221 | TN_FILENAME = 0x01, 222 | TN_FILESIZE = 0x02, 223 | TN_FILETYPE = 0x03, 224 | TN_FILEFORMAT = 0x04, 225 | TN_SOURCES = 0x15, 226 | TN_COMPLETE_SOURCES = 0x30, 227 | TN_FILESIZE_HI = 0x3A, 228 | TN_FILERATING = 0xF7, 229 | 230 | // OP_SERVERIDENT 231 | TN_SERVERNAME = 0x01, 232 | TN_DESCRIPTION = 0x0B 233 | }; 234 | 235 | // string tag names 236 | #define TNS_MEDIA_LENGTH "length" 237 | #define TNS_MEDIA_BITRATE "bitrate" 238 | #define TNS_MEDIA_CODEC "codec" 239 | 240 | enum file_type { 241 | FT_ANY = 0, 242 | FT_AUDIO = 1, 243 | FT_VIDEO = 2, 244 | FT_IMAGE = 3, 245 | FT_PROGRAM = 4, 246 | FT_DOCUMENT = 5, 247 | FT_ARCHIVE = 6, 248 | FT_CDIMAGE = 7, 249 | FT_EMULECOLLECTION = 8 250 | }; 251 | 252 | // string file types 253 | #define FTS_AUDIO "Audio" 254 | #define FTS_VIDEO "Video" 255 | #define FTS_IMAGE "Image" 256 | #define FTS_DOCUMENT "Doc" 257 | #define FTS_PROGRAM "Pro" 258 | #define FTS_ARCHIVE "Arc" 259 | #define FTS_CDIMAGE "Iso" 260 | #define FTS_EMULECOLLECTION "EmuleCollection" 261 | 262 | enum search_operator { 263 | SO_AND = 0x0000, // uint16 264 | SO_OR = 0x0100, // uint16 265 | SO_NOT = 0x0200, // uint16 266 | SO_STRING_TERM = 0x01, // uint8 267 | SO_STRING_CONSTR = 0x02, // uint8 268 | SO_UINT32 = 0x03, // uint8 269 | SO_UINT64 = 0x08 // uint8 270 | }; 271 | 272 | enum search_constraint { 273 | SC_MINSIZE = 0x02000101, 274 | SC_MAXSIZE = 0x02000102, 275 | SC_SRCAVAIL = 0x15000101, 276 | SC_SRCCMPLETE = 0x30000101, 277 | SC_MINBITRATE = 0xd4000101, 278 | SC_MINLENGTH = 0xd3000101 279 | }; 280 | 281 | #endif // ED2KD_ED2K_PROTO_H 282 | -------------------------------------------------------------------------------- /src/packet.c: -------------------------------------------------------------------------------- 1 | #include "packet.h" 2 | 3 | #include /* floor */ 4 | #include /* memcpy */ 5 | #include /* alloca */ 6 | #include /* alloca */ 7 | 8 | #include 9 | #include 10 | 11 | #include "ed2k_proto.h" 12 | #include "server.h" 13 | 14 | void send_id_change(struct bufferevent *bev, uint32_t id) 15 | { 16 | struct packet_id_change data; 17 | 18 | data.hdr.proto = PROTO_EDONKEY; 19 | data.hdr.length = sizeof(data) - sizeof(data.hdr); 20 | data.opcode = OP_IDCHANGE; 21 | data.user_id = id; 22 | data.tcp_flags = g_srv.cfg->srv_tcp_flags; 23 | 24 | bufferevent_write(bev, &data, sizeof(data)); 25 | } 26 | 27 | void send_server_message(struct bufferevent *bev, const char *msg, uint16_t len) 28 | { 29 | struct packet_server_message data; 30 | 31 | data.hdr.proto = PROTO_EDONKEY; 32 | data.hdr.length = sizeof(data) - sizeof(data.hdr) + len; 33 | data.opcode = OP_SERVERMESSAGE; 34 | data.msg_len = len; 35 | 36 | bufferevent_write(bev, &data, sizeof(data)); 37 | bufferevent_write(bev, msg, len); 38 | } 39 | 40 | void send_server_status(struct bufferevent *bev) 41 | { 42 | struct packet_server_status data; 43 | 44 | data.hdr.proto = PROTO_EDONKEY; 45 | data.hdr.length = sizeof(data) - sizeof(data.hdr); 46 | data.opcode = OP_SERVERSTATUS; 47 | data.user_count = atomic_load(&g_srv.user_count); 48 | data.file_count = atomic_load(&g_srv.file_count); 49 | 50 | bufferevent_write(bev, &data, sizeof(data)); 51 | } 52 | 53 | void send_server_ident(struct bufferevent *bev) 54 | { 55 | struct packet_server_ident data; 56 | 57 | data.hdr.proto = PROTO_EDONKEY; 58 | data.hdr.length = sizeof(data) - sizeof(data.hdr); 59 | data.opcode = OP_SERVERSTATUS; 60 | memcpy(data.hash, g_srv.cfg->hash, sizeof(data.hash)); 61 | data.ip = g_srv.cfg->listen_addr_inaddr; 62 | data.port = g_srv.cfg->listen_port; 63 | data.tag_count = (g_srv.cfg->server_name_len > 0) + (g_srv.cfg->server_descr_len > 0); 64 | 65 | if (data.tag_count > 0) { 66 | struct evbuffer *buf = evbuffer_new(); 67 | evbuffer_add(buf, &data, sizeof(data)); 68 | 69 | if (g_srv.cfg->server_name_len > 0) { 70 | struct tag_header th; 71 | struct tag_strval *tv; 72 | size_t data_len = sizeof(*tv) + g_srv.cfg->server_name_len - 1; 73 | tv = (struct tag_strval *) alloca(data_len); 74 | 75 | th.type = TT_STRING; 76 | th.name_len = 1; 77 | *th.name = TN_SERVERNAME; 78 | tv->len = g_srv.cfg->server_name_len; 79 | memcpy(tv->str, g_srv.cfg->server_name, g_srv.cfg->server_name_len); 80 | 81 | evbuffer_add(buf, &th, sizeof(th)); 82 | evbuffer_add(buf, tv, data_len); 83 | } 84 | 85 | if (g_srv.cfg->server_descr_len > 0) { 86 | struct tag_header th; 87 | struct tag_strval *tv; 88 | size_t data_len = sizeof(*tv) + g_srv.cfg->server_descr_len - 1; 89 | tv = (struct tag_strval *) alloca(data_len); 90 | 91 | th.type = TT_STRING; 92 | th.name_len = 1; 93 | *th.name = TN_DESCRIPTION; 94 | tv->len = g_srv.cfg->server_descr_len; 95 | memcpy(tv->str, g_srv.cfg->server_descr, g_srv.cfg->server_descr_len); 96 | 97 | evbuffer_add(buf, &th, sizeof(th)); 98 | evbuffer_add(buf, tv, data_len); 99 | } 100 | 101 | { 102 | struct packet_header *ph = (struct packet_header *) evbuffer_pullup(buf, sizeof(*ph)); 103 | ph->length = evbuffer_get_length(buf) - sizeof(*ph); 104 | } 105 | 106 | bufferevent_write_buffer(bev, buf); 107 | evbuffer_free(buf); 108 | } else { 109 | bufferevent_write(bev, &data, sizeof(data)); 110 | } 111 | } 112 | 113 | void send_server_list(struct bufferevent *bev) 114 | { 115 | (void) bev; 116 | // TODO: implement me :) 117 | } 118 | 119 | void send_reject(struct bufferevent *bev) 120 | { 121 | static const char data[] = {PROTO_EDONKEY, 1, 0, 0, 0, OP_REJECT}; 122 | bufferevent_write(bev, &data, sizeof(data)); 123 | } 124 | 125 | void send_callback_fail(struct bufferevent *bev) 126 | { 127 | static const char data[] = {PROTO_EDONKEY, 1, 0, 0, 0, OP_CALLBACK_FAIL}; 128 | bufferevent_write(bev, &data, sizeof(data)); 129 | } 130 | 131 | void send_found_sources(struct bufferevent *bev, const unsigned char *hash, struct file_source *sources, size_t count) 132 | { 133 | struct packet_found_sources data; 134 | size_t srcs_len = count * sizeof(*sources); 135 | 136 | data.hdr.proto = PROTO_EDONKEY; 137 | memcpy(data.hash, hash, sizeof(data.hash)); 138 | data.hdr.length = sizeof(data) - sizeof(data.hdr) + srcs_len; 139 | data.opcode = OP_FOUNDSOURCES; 140 | data.count = count; 141 | bufferevent_write(bev, &data, sizeof(data)); 142 | if (count) 143 | bufferevent_write(bev, sources, srcs_len); 144 | } 145 | 146 | void send_search_result(struct bufferevent *bev, struct evbuffer *result, size_t count) 147 | { 148 | struct packet_search_result data; 149 | 150 | data.hdr.proto = PROTO_EDONKEY; 151 | data.hdr.length = sizeof(data) - sizeof(data.hdr) + evbuffer_get_length(result); 152 | data.opcode = OP_SEARCHRESULT; 153 | data.files_count = count; 154 | evbuffer_prepend(result, &data, sizeof(data)); 155 | 156 | bufferevent_write_buffer(bev, result); 157 | } 158 | 159 | void write_search_file(struct evbuffer *buf, const struct search_file *file) 160 | { 161 | struct search_file_entry sfe; 162 | 163 | memcpy(sfe.hash, file->hash, sizeof sfe.hash); 164 | sfe.id = file->client_id; 165 | sfe.port = file->client_port; 166 | sfe.tag_count = 1 + 1 + /*(0!=file->type)+*/ (file->ext_len > 0) + 1 + 1 + (file->rated_count > 0) + (file->media_length > 0) + (file->media_length > 0) + (file->media_codec_len > 0); 167 | evbuffer_add(buf, &sfe, sizeof sfe); 168 | 169 | { 170 | struct tag_header th; 171 | struct tag_strval *tv; 172 | size_t data_len = sizeof *tv + file->name_len - 1; 173 | tv = (struct tag_strval *) alloca(data_len); 174 | 175 | th.type = TT_STRING; 176 | th.name_len = 1; 177 | *th.name = TN_FILENAME; 178 | tv->len = file->name_len; 179 | memcpy(tv->str, file->name, file->name_len); 180 | 181 | evbuffer_add(buf, &th, sizeof th); 182 | evbuffer_add(buf, tv, data_len); 183 | } 184 | 185 | { 186 | struct tag_header th; 187 | th.type = TT_UINT64; 188 | th.name_len = 1; 189 | *th.name = TN_FILESIZE; 190 | 191 | evbuffer_add(buf, &th, sizeof th); 192 | evbuffer_add(buf, &file->size, sizeof file->size); 193 | } 194 | 195 | /*if ( files[i].type ) { 196 | struct tag_header th; 197 | th.type = TT_STRING; 198 | th.name_len = 1; 199 | *th.name = TN_FILETYPE; 200 | //len 201 | //type 202 | }*/ 203 | 204 | if (file->ext_len) { 205 | struct tag_header th; 206 | struct tag_strval *tv; 207 | size_t data_len = sizeof *tv + file->ext_len - 1; 208 | tv = (struct tag_strval *) alloca(data_len); 209 | 210 | th.type = TT_STRING; 211 | th.name_len = 1; 212 | *th.name = TN_FILEFORMAT; 213 | tv->len = file->ext_len; 214 | memcpy(tv->str, file->ext, file->ext_len); 215 | 216 | evbuffer_add(buf, &th, sizeof th); 217 | evbuffer_add(buf, tv, data_len); 218 | } 219 | 220 | { 221 | struct tag_header th; 222 | th.type = TT_UINT32; 223 | th.name_len = 1; 224 | *th.name = TN_SOURCES; 225 | 226 | evbuffer_add(buf, &th, sizeof th); 227 | evbuffer_add(buf, &file->srcavail, sizeof file->srcavail); 228 | } 229 | 230 | { 231 | struct tag_header th; 232 | th.type = TT_UINT32; 233 | th.name_len = 1; 234 | *th.name = TN_COMPLETE_SOURCES; 235 | 236 | evbuffer_add(buf, &th, sizeof th); 237 | evbuffer_add(buf, &file->srccomplete, sizeof file->srccomplete); 238 | } 239 | 240 | if (file->rated_count > 0) { 241 | uint16_t data; 242 | struct tag_header th; 243 | th.type = TT_UINT16; 244 | th.name_len = 1; 245 | *th.name = TN_FILERATING; 246 | 247 | // lo-byte: percentage rated this file 248 | data = (100 * (uint8_t) ((float) file->srcavail / (float) file->rated_count)) << 8; 249 | // hi-byte: average rating 250 | data += ((uint16_t) floor((double) file->rating / (double) file->rated_count + 0.5f) * 51) & 0xFF; 251 | 252 | evbuffer_add(buf, &th, sizeof th); 253 | evbuffer_add(buf, &data, sizeof data); 254 | } 255 | 256 | if (file->media_length) { 257 | struct tag_header *th; 258 | uint16_t name_len = sizeof(TNS_MEDIA_LENGTH) - 1; 259 | 260 | size_t th_len = sizeof *th + name_len - 1; 261 | th = (struct tag_header *) alloca(th_len); 262 | th->type = TT_UINT32; 263 | th->name_len = name_len; 264 | memcpy(th->name, TNS_MEDIA_LENGTH, name_len); 265 | 266 | evbuffer_add(buf, th, th_len); 267 | evbuffer_add(buf, &file->media_length, sizeof file->media_length); 268 | } 269 | 270 | if (file->media_bitrate) { 271 | uint16_t name_len = sizeof(TNS_MEDIA_BITRATE) - 1; 272 | struct tag_header *th; 273 | size_t th_len = sizeof *th + name_len - 1; 274 | th = (struct tag_header *) alloca(th_len); 275 | th->type = TT_UINT32; 276 | th->name_len = name_len; 277 | memcpy(th->name, TNS_MEDIA_BITRATE, name_len); 278 | 279 | evbuffer_add(buf, th, th_len); 280 | evbuffer_add(buf, &file->media_bitrate, sizeof(file->media_bitrate)); 281 | } 282 | 283 | if (file->media_codec_len) { 284 | struct tag_header *th; 285 | struct tag_strval *tv; 286 | uint16_t name_len = sizeof(TNS_MEDIA_CODEC) - 1; 287 | size_t th_len = sizeof(*th) + name_len - 1; 288 | size_t tv_len = sizeof(*tv) + file->media_codec_len - 1; 289 | th = (struct tag_header *) alloca(th_len); 290 | tv = (struct tag_strval *) alloca(tv_len); 291 | 292 | th->type = TT_UINT32; 293 | th->name_len = name_len; 294 | memcpy(th->name, TNS_MEDIA_CODEC, name_len); 295 | 296 | tv->len = file->media_codec_len; 297 | memcpy(tv->str, file->media_codec, file->media_codec_len); 298 | 299 | evbuffer_add(buf, th, th_len); 300 | evbuffer_add(buf, tv, tv_len); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /eb/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../../src/ed2k_proto.h" 16 | #include "../../src/packet.h" 17 | 18 | // stringize macro 19 | #define _CSTR(x) #x 20 | #define CSTR(x) _CSTR(x) 21 | 22 | #define MAX_UNCOMPRESSED_PACKET_SIZE 300*1024 23 | #define DEFAULT_SPAWN_PAUSE 100 // msecs 24 | #define DEFAULT_ACTION_PAUSE 500 // msecs 25 | 26 | #define EB_VERSION "0.01" 27 | 28 | // definitions for fixed length fields in ed2k packets 29 | #define NICK_LEN 5 30 | #define FILENAME_LEN 5 31 | 32 | enum actions { 33 | ACTION_OFFER = 0, 34 | ACTION_QUERY, 35 | ACTION_SOURCE, 36 | ACTION_COUNT 37 | }; 38 | 39 | struct ebclient { 40 | /* Index */ 41 | int idx; 42 | /* eDonkey2000 ID */ 43 | uint32_t id; 44 | /* eDonkey2000 hash */ 45 | unsigned char hash[ED2K_HASH_SIZE]; 46 | /* connection bufferevent */ 47 | struct bufferevent *bev; 48 | /* action timer */ 49 | struct event *ev_action; 50 | /* connection established flag */ 51 | unsigned connected:1; 52 | }; 53 | 54 | struct ebinstance { 55 | /* event base */ 56 | struct event_base *evbase; 57 | /* Client spawn timer */ 58 | struct event *ev_spawn; 59 | /* Common pause between spawning clients */ 60 | const struct timeval *spawn_pause; 61 | /* Server address to connect */ 62 | struct sockaddr_in server_sa; 63 | /* Count of concurrently working clients */ 64 | int client_cnt; 65 | /* Total count of actions to perform */ 66 | int repeat_cnt; 67 | /* Count currently running clients */ 68 | int running_cnt; 69 | /* Common pause between client actions */ 70 | const struct timeval *action_pause; 71 | /* Number of selected actions */ 72 | int action_cnt; 73 | /* Array of selected actions */ 74 | int actions[ACTION_COUNT]; 75 | }; 76 | 77 | struct packet_login { 78 | struct packet_header hdr; 79 | uint8_t opcode; 80 | unsigned char hash[ED2K_HASH_SIZE]; 81 | uint32_t id; 82 | uint16_t port; 83 | uint32_t tag_count; 84 | struct { 85 | struct tag_header hdr; 86 | uint16_t len; 87 | unsigned char val[NICK_LEN]; 88 | } tag_nick; 89 | struct { 90 | struct tag_header hdr; 91 | uint16_t val; 92 | } tag_port; 93 | struct { 94 | struct tag_header hdr; 95 | uint32_t val; 96 | } tag_version; 97 | struct { 98 | struct tag_header hdr; 99 | uint32_t val; 100 | } tag_tcp_flags; 101 | } __attribute__((__packed__)); 102 | 103 | struct packet_offer_files { 104 | struct packet_header hdr; 105 | uint8_t opcode; 106 | uint32_t file_count; 107 | } __attribute__((__packed__)); 108 | 109 | struct pub_file { 110 | unsigned char hash[ED2K_HASH_SIZE]; 111 | uint32_t id; 112 | uint16_t port; 113 | uint32_t tag_count; 114 | struct { 115 | struct tag_header hdr; 116 | uint16_t len; 117 | unsigned char val[FILENAME_LEN]; 118 | } tag_name; 119 | struct { 120 | struct tag_header hdr; 121 | uint32_t val; 122 | } tag_size; 123 | struct { 124 | struct tag_header hdr; 125 | uint32_t val; 126 | } tag_rating; 127 | struct { 128 | struct tag_header hdr; 129 | uint32_t val; 130 | } tag_type; 131 | } __attribute__((__packed__)); 132 | 133 | struct ebinstance g_eb; 134 | 135 | // command line options 136 | static const char *optString = "vhs:c:w:r:p:OQS"; 137 | static const struct option longOpts[] = { 138 | {"version", no_argument, NULL, 'v'}, 139 | {"help", no_argument, NULL, 'h'}, 140 | {"server", required_argument, NULL, 's'}, 141 | {"concurrency", required_argument, NULL, 'c'}, 142 | {"spawn-pause", required_argument, NULL, 'w'}, 143 | {"repeat", required_argument, NULL, 'r'}, 144 | {"action-pause", required_argument, NULL, 'p'}, 145 | {"offer", no_argument, NULL, 'O'}, 146 | {"query", no_argument, NULL, 'Q'}, 147 | {"source", no_argument, NULL, 'S'}, 148 | {NULL, no_argument, NULL, 0} 149 | }; 150 | 151 | void get_rnd_str(unsigned char *str, size_t len) 152 | { 153 | size_t i; 154 | static const unsigned char alphanum[] = 155 | "0123456789" 156 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 157 | "abcdefghijklmnopqrstuvwxyz"; 158 | 159 | for (i = 0; i < len; ++i) { 160 | str[i] = alphanum[rand() % (sizeof(alphanum) - 1)]; 161 | } 162 | } 163 | 164 | void client_free(struct ebclient *clnt) 165 | { 166 | bufferevent_free(clnt->bev); 167 | if (clnt->ev_action) 168 | event_free(clnt->ev_action); 169 | free(clnt); 170 | 171 | g_eb.running_cnt--; 172 | if (!g_eb.running_cnt && !g_eb.client_cnt) { 173 | event_base_loopbreak(g_eb.evbase); 174 | } 175 | } 176 | 177 | void send_login_request(struct ebclient *clnt) 178 | { 179 | struct packet_login data; 180 | unsigned char nick[NICK_LEN] = {'e', 'b', 't', 's', 't'}; 181 | 182 | data.hdr.proto = PROTO_EDONKEY; 183 | data.hdr.length = sizeof(data) - sizeof(data.hdr); 184 | data.opcode = OP_LOGINREQUEST; 185 | 186 | memcpy(data.hash, clnt->hash, sizeof(data.hash)); 187 | data.id = 0; 188 | data.port = 0; // port listening no implemented 189 | data.tag_count = 4; 190 | 191 | // nick 192 | data.tag_nick.hdr.type = TT_STRING; 193 | data.tag_nick.hdr.name_len = 1; 194 | *data.tag_nick.hdr.name = TN_NAME; 195 | data.tag_nick.len = NICK_LEN; 196 | memcpy(data.tag_nick.val, nick, NICK_LEN); 197 | 198 | // port 199 | data.tag_port.hdr.type = TT_UINT16; 200 | data.tag_port.hdr.name_len = 1; 201 | *data.tag_port.hdr.name = TN_PORT; 202 | data.tag_port.val = 0; // // port listening no yet implemented 203 | 204 | // version 205 | data.tag_version.hdr.type = TT_UINT32; 206 | data.tag_version.hdr.name_len = 1; 207 | *data.tag_version.hdr.name = TN_VERSION; 208 | data.tag_version.val = EDONKEYVERSION; 209 | 210 | // tcp flags 211 | data.tag_tcp_flags.hdr.type = TT_UINT32; 212 | data.tag_tcp_flags.hdr.name_len = 1; 213 | *data.tag_tcp_flags.hdr.name = TN_SERVER_FLAGS; 214 | data.tag_tcp_flags.val = CLI_CAP_UNICODE | CLI_CAP_LARGEFILES | CLI_CAP_ZLIB; 215 | 216 | bufferevent_write(clnt->bev, &data, sizeof data); 217 | } 218 | 219 | void send_offer_files(struct ebclient *clnt) 220 | { 221 | size_t i; 222 | struct packet_offer_files data; 223 | struct packet_header *ph; 224 | struct pub_file pf; 225 | struct evbuffer *buf = evbuffer_new(); 226 | 227 | data.hdr.proto = PROTO_EDONKEY; 228 | //data.hdr.length = 0; 229 | data.opcode = OP_OFFERFILES; 230 | data.file_count = 200; 231 | 232 | evbuffer_add(buf, &data, sizeof(data)); 233 | 234 | // complete source 235 | pf.id = 0xfdfdfdfdu; 236 | pf.port = 0xfdfdu; 237 | 238 | pf.tag_count = 4; 239 | 240 | // file name 241 | pf.tag_name.hdr.type = TT_STRING; 242 | pf.tag_name.hdr.name_len = 1; 243 | *pf.tag_name.hdr.name = TN_FILENAME; 244 | pf.tag_name.len = 5; 245 | 246 | // file size 247 | pf.tag_size.hdr.type = TT_UINT32; 248 | pf.tag_size.hdr.name_len = 1; 249 | *pf.tag_size.hdr.name = TN_FILESIZE; 250 | pf.tag_size.val = 10240; 251 | 252 | // file type 253 | pf.tag_type.hdr.type = TT_UINT32; 254 | pf.tag_type.hdr.name_len = 1; 255 | *pf.tag_type.hdr.name = TN_FILETYPE; 256 | pf.tag_type.val = FT_PROGRAM; 257 | 258 | // file rating 259 | pf.tag_rating.hdr.type = TT_UINT32; 260 | pf.tag_rating.hdr.name_len = 1; 261 | *pf.tag_rating.hdr.name = TN_FILERATING; 262 | pf.tag_rating.val = 0; 263 | 264 | for (i = 0; i < data.file_count; ++i) { 265 | evutil_secure_rng_get_bytes(pf.hash, sizeof(pf.hash)); 266 | get_rnd_str(pf.tag_name.val, sizeof(pf.tag_name.val)); 267 | 268 | evbuffer_add(buf, &pf, sizeof(pf)); 269 | } 270 | 271 | ph = (struct packet_header *) evbuffer_pullup(buf, sizeof(*ph)); 272 | ph->length = evbuffer_get_length(buf) - sizeof(*ph); 273 | 274 | bufferevent_write_buffer(clnt->bev, buf); 275 | evbuffer_free(buf); 276 | } 277 | 278 | void timer_cb(evutil_socket_t fd, short what, void *ctx) 279 | { 280 | struct ebclient *clnt = (struct ebclient *) ctx; 281 | (void) fd; 282 | (void) what; 283 | 284 | if (g_eb.action_cnt && g_eb.repeat_cnt) { 285 | switch (g_eb.actions[rand() % g_eb.action_cnt]) { 286 | case ACTION_OFFER: 287 | send_offer_files(clnt); 288 | break; 289 | 290 | case ACTION_QUERY: 291 | break; 292 | 293 | case ACTION_SOURCE: 294 | break; 295 | 296 | default: 297 | assert(0); 298 | break; 299 | } 300 | 301 | g_eb.repeat_cnt--; 302 | evtimer_add(clnt->ev_action, g_eb.action_pause); 303 | } else { 304 | client_free(clnt); 305 | } 306 | } 307 | 308 | int process_id_change(struct packet_buffer *pb, struct ebclient *clnt) 309 | { 310 | uint32_t tcp_flags; 311 | (void) tcp_flags; 312 | 313 | PB_READ_UINT32(pb, clnt->id); 314 | PB_READ_UINT32(pb, tcp_flags); 315 | 316 | return 0; 317 | 318 | malformed: 319 | printf("%d# malformed OP_IDCHANGE\n", clnt->idx); 320 | return -1; 321 | } 322 | 323 | int process_packet(struct packet_buffer *pb, uint8_t opcode, struct ebclient *clnt) 324 | { 325 | switch (opcode) { 326 | case OP_IDCHANGE: 327 | PB_CHECK(process_id_change(pb, clnt) == 0); 328 | clnt->ev_action = evtimer_new(g_eb.evbase, timer_cb, clnt); 329 | evtimer_add(clnt->ev_action, g_eb.action_pause); 330 | return 0; 331 | 332 | case OP_SERVERMESSAGE: 333 | return 0; 334 | 335 | case OP_SERVERSTATUS: 336 | return 0; 337 | 338 | case OP_SERVERIDENT: 339 | return 0; 340 | 341 | case OP_FOUNDSOURCES: 342 | 343 | case OP_SEARCHRESULT: 344 | return 0; 345 | 346 | case OP_DISCONNECT: 347 | return 0; 348 | 349 | case OP_REJECT: 350 | return 0; 351 | 352 | default: 353 | // skip all unknown packets 354 | return 0; 355 | } 356 | 357 | malformed: 358 | return -1; 359 | } 360 | 361 | void read_cb(struct bufferevent *bev, void *ctx) 362 | { 363 | struct ebclient *clnt = (struct ebclient *) ctx; 364 | struct evbuffer *input = bufferevent_get_input(bev); 365 | size_t src_len = evbuffer_get_length(input); 366 | 367 | while (src_len > sizeof(struct packet_header)) { 368 | unsigned char *data; 369 | struct packet_buffer pb; 370 | size_t packet_len; 371 | int ret; 372 | const struct packet_header *ph = 373 | (struct packet_header *) evbuffer_pullup(input, sizeof *ph); 374 | 375 | if ((PROTO_PACKED != ph->proto) && (PROTO_EDONKEY != ph->proto)) { 376 | printf("%d# unknown packet protocol %c\n", clnt->idx, ph->proto); 377 | // close and remove client 378 | } 379 | 380 | // wait for full length packet 381 | packet_len = ph->length + sizeof(*ph); 382 | if (packet_len > src_len) 383 | return; 384 | 385 | data = evbuffer_pullup(input, packet_len); 386 | ph = (struct packet_header *) data; 387 | data += sizeof(*ph); 388 | 389 | if (PROTO_PACKED == ph->proto) { 390 | unsigned long unpacked_len = MAX_UNCOMPRESSED_PACKET_SIZE; 391 | unsigned char *unpacked = (unsigned char *) malloc(unpacked_len); 392 | 393 | ret = uncompress(unpacked, &unpacked_len, data + 1, ph->length - 1); 394 | if (Z_OK == ret) { 395 | PB_INIT(&pb, unpacked, unpacked_len); 396 | ret = process_packet(&pb, *data, clnt); 397 | } else { 398 | printf("%d# failed to unpack packet\n", clnt->idx); 399 | ret = -1; 400 | } 401 | free(unpacked); 402 | } else { 403 | PB_INIT(&pb, data + 1, ph->length - 1); 404 | ret = process_packet(&pb, *data, clnt); 405 | } 406 | 407 | if (ret < 0) { 408 | printf("%d# packet parsing error (opcode:%c)\n", clnt->idx, *(data + 1)); 409 | // close and remove client 410 | } 411 | 412 | evbuffer_drain(input, packet_len); 413 | src_len = evbuffer_get_length(input); 414 | } 415 | } 416 | 417 | void event_cb(struct bufferevent *bev, short events, void *ctx) 418 | { 419 | struct ebclient *clnt = (struct ebclient *) ctx; 420 | (void) bev; 421 | 422 | if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { 423 | if (!clnt->connected) { 424 | printf("%d# failed to connect!\n", clnt->idx); 425 | } else { 426 | printf("%d# got error/EOF!\n", clnt->idx); 427 | } 428 | client_free(clnt); 429 | } else if (events & BEV_EVENT_CONNECTED) { 430 | clnt->connected = 1; 431 | send_login_request(clnt); 432 | } 433 | } 434 | 435 | void spawn_cb(evutil_socket_t fd, short what, void *ctx) 436 | { 437 | static int idx = 0; 438 | (void) fd; 439 | (void) what; 440 | (void) ctx; 441 | 442 | if (g_eb.client_cnt > 0) { 443 | struct bufferevent *bev; 444 | struct ebclient *clnt; 445 | 446 | bev = bufferevent_socket_new(g_eb.evbase, -1, BEV_OPT_CLOSE_ON_FREE); 447 | clnt = (struct ebclient *) calloc(1, sizeof *clnt); 448 | clnt->idx = idx++; 449 | clnt->bev = bev; 450 | 451 | bufferevent_setcb(bev, read_cb, NULL, event_cb, (void *) clnt); 452 | bufferevent_enable(bev, EV_READ | EV_WRITE); 453 | 454 | if (bufferevent_socket_connect(bev, (struct sockaddr *) &g_eb.server_sa, sizeof(g_eb.server_sa)) < 0) { 455 | printf("%d# failed to connect\n", clnt->idx); 456 | bufferevent_free(bev); 457 | free(clnt); 458 | } 459 | 460 | g_eb.running_cnt++; 461 | g_eb.client_cnt--; 462 | if (g_eb.client_cnt) { 463 | event_add(g_eb.ev_spawn, g_eb.spawn_pause); 464 | } 465 | } 466 | } 467 | 468 | void signal_cb(evutil_socket_t fd, short what, void *ctx) 469 | { 470 | (void) fd; 471 | (void) what; 472 | (void) ctx; 473 | 474 | printf("caught SIGINT, terminating...\n"); 475 | event_base_loopexit(g_eb.evbase, NULL); 476 | } 477 | 478 | void display_version() 479 | { 480 | puts( 481 | "ed2kd benchmark (eb) v" EB_VERSION "\n" 482 | "Build on: "__DATE__ " " __TIME__ 483 | ); 484 | } 485 | 486 | void display_usage() 487 | { 488 | puts( 489 | "Options:\n" 490 | "--help, -h\tshow this help\n" 491 | "--version, -v\tprint version\n" 492 | "--server,-s \tserver address(ipv4:[port])\n" 493 | "--concurrency, -c \tconcurrent working clients\n" 494 | "--spawn-pause, -w \t pause between client spawning(default:" CSTR(DEFAULT_SPAWN_PAUSE) "ms)\n" 495 | "--repeat, -r \trepeat times\n" 496 | "--action-pause, -p \t pause between activities (default:" CSTR(DEFAULT_ACTION_PAUSE) "ms)\n" 497 | "--offer, -O\toffer files\n" 498 | "--query, -Q\tsearch queries\n" 499 | "--source, -S\tsources requests" 500 | ); 501 | } 502 | 503 | int main(int argc, char *argv[]) 504 | { 505 | int ret, opt, longIndex = 0; 506 | struct event *ev_sigint; 507 | unsigned offer_flag = 0, query_flag = 0, source_flag = 0; 508 | struct timeval tv_action = {0, 0}, tv_spawn = {0, 0}; 509 | char *server_addr = NULL; 510 | 511 | // parse command line arguments 512 | opt = getopt_long(argc, argv, optString, longOpts, &longIndex); 513 | while (opt != -1) { 514 | switch (opt) { 515 | case 'v': 516 | display_version(); 517 | return EXIT_SUCCESS; 518 | 519 | case 'h': 520 | display_usage(); 521 | return EXIT_SUCCESS; 522 | 523 | case 's': 524 | server_addr = optarg; 525 | break; 526 | 527 | case 'p': { 528 | int val = atoi(optarg); 529 | tv_action.tv_sec = val / 1000; 530 | tv_action.tv_usec = (val % 1000) * 1000; 531 | break; 532 | } 533 | 534 | case 'c': 535 | g_eb.client_cnt = atoi(optarg); 536 | break; 537 | 538 | case 'w': { 539 | int val = atoi(optarg); 540 | tv_spawn.tv_sec = val / 1000; 541 | tv_spawn.tv_usec = (val % 1000) * 1000; 542 | break; 543 | } 544 | 545 | case 'r': 546 | g_eb.repeat_cnt = atoi(optarg); 547 | break; 548 | 549 | case 'O': 550 | offer_flag = 1; 551 | break; 552 | 553 | case 'Q': 554 | query_flag = 1; 555 | break; 556 | 557 | case 'S': 558 | source_flag = 1; 559 | break; 560 | 561 | default: 562 | display_usage(); 563 | return EXIT_FAILURE; 564 | } 565 | opt = getopt_long(argc, argv, optString, longOpts, &longIndex); 566 | } 567 | 568 | ret = 0; 569 | if (server_addr) { 570 | int sa_len = sizeof(g_eb.server_sa); 571 | ret = evutil_parse_sockaddr_port(server_addr, (struct sockaddr *) &g_eb.server_sa, &sa_len); 572 | } 573 | if (!server_addr || (ret < 0) || (g_eb.repeat_cnt <= 0) || (g_eb.client_cnt <= 0)) { 574 | display_usage(); 575 | return EXIT_FAILURE; 576 | } 577 | 578 | // prepare array with user selected actions 579 | if (offer_flag) { 580 | g_eb.actions[g_eb.action_cnt++] = ACTION_OFFER; 581 | } 582 | if (query_flag) { 583 | g_eb.actions[g_eb.action_cnt++] = ACTION_QUERY; 584 | } 585 | if (source_flag) { 586 | g_eb.actions[g_eb.action_cnt] = ACTION_SOURCE; 587 | } 588 | 589 | if (!g_eb.action_cnt) { 590 | g_eb.repeat_cnt = 0; 591 | } 592 | 593 | if (tv_spawn.tv_sec == 0 && tv_spawn.tv_usec == 0) { 594 | tv_spawn.tv_sec = DEFAULT_SPAWN_PAUSE / 1000; 595 | tv_spawn.tv_usec = (DEFAULT_SPAWN_PAUSE % 1000) * 1000; 596 | } 597 | 598 | if (tv_action.tv_sec == 0 && tv_action.tv_usec == 0) { 599 | tv_action.tv_sec = DEFAULT_ACTION_PAUSE / 1000; 600 | tv_action.tv_usec = (DEFAULT_ACTION_PAUSE % 1000) * 1000; 601 | } 602 | 603 | if (evutil_secure_rng_init() < 0) { 604 | printf("Failed to seed random number generator\n"); 605 | return EXIT_FAILURE; 606 | } 607 | srand((unsigned int) time(NULL)); 608 | 609 | #ifdef WIN32 610 | { 611 | WSADATA WSAData; 612 | if ( 0 != WSAStartup(0x0201, &WSAData) ) { 613 | printf("WSAStartup failed!\n"); 614 | return EXIT_FAILURE; 615 | } 616 | } 617 | #endif 618 | 619 | g_eb.evbase = event_base_new(); 620 | if (NULL == g_eb.evbase) { 621 | printf("Failed to create main event loop\n"); 622 | return EXIT_FAILURE; 623 | } 624 | 625 | // setup signals 626 | ev_sigint = evsignal_new(g_eb.evbase, SIGINT, signal_cb, NULL); 627 | evsignal_add(ev_sigint, NULL); 628 | 629 | // setup common timers timeouts 630 | g_eb.action_pause = event_base_init_common_timeout(g_eb.evbase, &tv_action); 631 | g_eb.spawn_pause = event_base_init_common_timeout(g_eb.evbase, &tv_spawn); 632 | 633 | // setup spawn timer 634 | g_eb.ev_spawn = evtimer_new(g_eb.evbase, spawn_cb, NULL); 635 | event_add(g_eb.ev_spawn, g_eb.spawn_pause); 636 | 637 | ret = event_base_dispatch(g_eb.evbase); 638 | if (ret < 0) { 639 | printf("Main dispatch loop finished with error\n"); 640 | } 641 | else if (0 == ret) { 642 | printf("No active events in main loop\n"); 643 | } 644 | 645 | event_free(g_eb.ev_spawn); 646 | event_free(ev_sigint); 647 | event_base_free(g_eb.evbase); 648 | 649 | return EXIT_SUCCESS; 650 | } 651 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ed2k_proto.h" 10 | #include "packet.h" 11 | #include "client.h" 12 | #include "portcheck.h" 13 | #include "db.h" 14 | #include "log.h" 15 | 16 | static void dummy_cb(evutil_socket_t fd, short what, void *ctx) 17 | { 18 | (void) fd; 19 | (void) what; 20 | (void) ctx; 21 | } 22 | 23 | void *server_base_worker(void *arg) 24 | { 25 | // todo: after moving to libevent 2.1.x replace timer below with EVLOOP_NO_EXIT_ON_EMPTY flag 26 | struct event_base *evbase = (struct event_base *) arg; 27 | struct timeval tv = {500, 0}; 28 | struct event *ev_dummy = event_new(evbase, -1, EV_PERSIST, dummy_cb, 0); 29 | event_add(ev_dummy, &tv); 30 | 31 | if (event_base_dispatch(evbase) < 0) 32 | ED2KD_LOGERR("loop finished with error"); 33 | 34 | event_free(ev_dummy); 35 | return NULL; 36 | } 37 | 38 | void server_add_job(struct job *job) 39 | { 40 | pthread_mutex_lock(&g_srv.job_mutex); 41 | client_addref(job->clnt); 42 | TAILQ_INSERT_TAIL(&g_srv.jqueue, job, qentry); 43 | pthread_mutex_unlock(&g_srv.job_mutex); 44 | pthread_cond_signal(&g_srv.job_cond); 45 | } 46 | 47 | static int process_login_request(struct packet_buffer *pb, struct client *clnt) 48 | { 49 | uint32_t tag_count; 50 | 51 | // user hash 16b 52 | PB_MEMCPY(pb, clnt->hash, sizeof clnt->hash); 53 | 54 | // user id 4b 55 | PB_SEEK(pb, sizeof(uint32_t)); 56 | 57 | // user port 2b 58 | PB_READ_UINT16(pb, clnt->port); 59 | 60 | // tag count 4b 61 | PB_READ_UINT32(pb, tag_count); 62 | 63 | for (; tag_count > 0; --tag_count) { 64 | const struct tag_header *tag_hdr = (struct tag_header *) pb->ptr; 65 | 66 | // no new tags allowed here 67 | PB_CHECK((tag_hdr->type & 0x80) == 0); 68 | 69 | // only int-based tag names 70 | PB_CHECK(1 == tag_hdr->name_len); 71 | 72 | PB_SKIP_TAGHDR(pb, tag_hdr); 73 | 74 | switch (*tag_hdr->name) { 75 | case TN_NAME: { 76 | PB_CHECK(TT_STRING == tag_hdr->type); 77 | clnt->nick_len = MAX_NICK_LEN; 78 | PB_READ_STRING(pb, clnt->nick, clnt->nick_len); 79 | clnt->nick[clnt->nick_len] = 0; 80 | break; 81 | } 82 | 83 | case TN_PORT: 84 | PB_CHECK(TT_UINT16 == tag_hdr->type); 85 | PB_READ_UINT16(pb, clnt->port); 86 | break; 87 | 88 | case TN_VERSION: { 89 | uint32_t ver; 90 | PB_CHECK(TT_UINT32 == tag_hdr->type); 91 | PB_READ_UINT32(pb, ver); 92 | PB_CHECK(EDONKEYVERSION == ver); 93 | break; 94 | } 95 | 96 | case TN_SERVER_FLAGS: 97 | PB_CHECK(TT_UINT32 == tag_hdr->type); 98 | PB_READ_UINT32(pb, clnt->tcp_flags); 99 | break; 100 | 101 | case TN_EMULE_VERSION: { 102 | uint32_t emule_ver; 103 | (void) emule_ver; 104 | PB_CHECK(TT_UINT32 == tag_hdr->type); 105 | PB_READ_UINT32(pb, emule_ver); 106 | break; 107 | } 108 | 109 | default: 110 | PB_CHECK(0); 111 | } 112 | } 113 | 114 | // todo: search already connected with same ip:port 115 | 116 | client_portcheck_start(clnt); 117 | 118 | return 1; 119 | 120 | malformed: 121 | return 0; 122 | } 123 | 124 | static int process_offer_files(struct packet_buffer *pb, struct client *clnt) 125 | { 126 | size_t i; 127 | uint32_t count; 128 | struct pub_file *files, *cur_file; 129 | 130 | PB_READ_UINT32(pb, count); 131 | PB_CHECK(count <= 200); 132 | 133 | i = count; 134 | // todo: limit total files count on server 135 | 136 | cur_file = files = (struct pub_file *) calloc(count, sizeof(*files)); 137 | 138 | while (i-- > 0) { 139 | uint32_t tag_count, id; 140 | uint16_t port; 141 | 142 | PB_MEMCPY(pb, cur_file->hash, sizeof(cur_file->hash)); 143 | 144 | PB_READ_UINT32(pb, id); 145 | PB_READ_UINT16(pb, port); 146 | 147 | if ((0xfbfbfbfb == id) && (0xfbfb == port)) { 148 | cur_file->complete = 1; 149 | } 150 | 151 | PB_READ_UINT32(pb, tag_count); 152 | 153 | for (; tag_count > 0; --tag_count) { 154 | struct tag_header *tag_hdr = (struct tag_header *) pb->ptr; 155 | 156 | // todo: new tags support 157 | PB_CHECK((tag_hdr->type & 0x80) == 0); 158 | 159 | PB_SKIP_TAGHDR(pb, tag_hdr); 160 | 161 | // string-named tags 162 | if (tag_hdr->name_len > 1) { 163 | if (strncmp(TNS_MEDIA_LENGTH, (const char *) tag_hdr->name, tag_hdr->name_len) == 0) { 164 | if (TT_UINT32 == tag_hdr->type) { 165 | PB_READ_UINT32(pb, cur_file->media_length); 166 | } else if (TT_STRING == tag_hdr->type) { 167 | // todo: support string values ( hh:mm:ss ) 168 | uint16_t len; 169 | PB_READ_UINT16(pb, len); 170 | PB_SEEK(pb, len); 171 | } else { 172 | PB_CHECK(0); 173 | } 174 | } else if (strncmp(TNS_MEDIA_BITRATE, (const char *) tag_hdr->name, tag_hdr->name_len) == 0) { 175 | PB_CHECK(TT_UINT32 == tag_hdr->type); 176 | PB_READ_UINT32(pb, cur_file->media_bitrate); 177 | } else if (strncmp(TNS_MEDIA_CODEC, (const char *) tag_hdr->name, tag_hdr->name_len) == 0) { 178 | uint16_t len = MAX_MCODEC_LEN; 179 | PB_CHECK(TT_STRING == tag_hdr->type); 180 | PB_READ_STRING(pb, cur_file->media_codec, len); 181 | cur_file->media_codec[len] = 0; 182 | } else { 183 | PB_CHECK(0); 184 | } 185 | } else { 186 | switch (*tag_hdr->name) { 187 | 188 | case TN_FILENAME: 189 | PB_CHECK(TT_STRING == tag_hdr->type); 190 | PB_READ_UINT16(pb, cur_file->name_len); 191 | cur_file->name_len = cur_file->name_len > MAX_FILENAME_LEN ? MAX_FILENAME_LEN : cur_file->name_len; 192 | PB_MEMCPY(pb, cur_file->name, cur_file->name_len); 193 | cur_file->name[cur_file->name_len] = 0; 194 | break; 195 | 196 | case TN_FILESIZE: 197 | PB_CHECK(TT_UINT32 == tag_hdr->type); 198 | PB_READ_UINT32(pb, cur_file->size); 199 | break; 200 | 201 | case TN_FILESIZE_HI: { 202 | uint32_t size_hi; 203 | PB_CHECK(TT_UINT32 == tag_hdr->type); 204 | PB_READ_UINT32(pb, size_hi); 205 | cur_file->size += (uint64_t) size_hi << 32; 206 | break; 207 | } 208 | 209 | case TN_FILERATING: 210 | PB_CHECK(TT_UINT32 == tag_hdr->type); 211 | PB_READ_UINT32(pb, cur_file->rating); 212 | if (cur_file->rating > 5) { 213 | cur_file->rating = 5; 214 | } 215 | break; 216 | 217 | case TN_FILETYPE: 218 | if (TT_UINT32 == tag_hdr->type) { 219 | PB_READ_UINT32(pb, cur_file->type); 220 | } else if (TT_STRING == tag_hdr->type) { 221 | uint16_t len; 222 | PB_READ_UINT16(pb, len); 223 | cur_file->type = get_ed2k_file_type((const char *) pb->ptr, len); 224 | PB_SEEK(pb, len); 225 | } else { 226 | PB_CHECK(0); 227 | } 228 | break; 229 | 230 | default: 231 | PB_CHECK(0); 232 | } 233 | } 234 | } 235 | 236 | cur_file++; 237 | } 238 | 239 | client_share_files(clnt, files, count); 240 | 241 | return 1; 242 | 243 | malformed: 244 | return 0; 245 | } 246 | 247 | static int process_search_request(struct packet_buffer *pb, struct client *clnt) 248 | { 249 | struct search_node *n, root; 250 | n = &root; 251 | 252 | memset(&root, 0, sizeof(root)); 253 | 254 | while (n) { 255 | if ((ST_AND <= n->type) && (ST_NOT >= n->type)) { 256 | if (!n->left) { 257 | struct search_node *new_node = (struct search_node *) alloca(sizeof(struct search_node)); 258 | memset(new_node, 0, sizeof(struct search_node)); 259 | new_node->parent = n; 260 | n->left = new_node; 261 | n = new_node; 262 | continue; 263 | } else if (!n->right) { 264 | struct search_node *new_node = (struct search_node *) alloca(sizeof(struct search_node)); 265 | memset(new_node, 0, sizeof(struct search_node)); 266 | new_node->parent = n; 267 | n->right = new_node; 268 | n = new_node; 269 | continue; 270 | } else if (n->left->string_term && n->right->string_term) { 271 | n->string_term = 1; 272 | } 273 | } else if (ST_EMPTY == n->type) { 274 | if (SO_AND == PB_PTR_UINT16(pb)) { 275 | n->type = ST_AND; 276 | PB_SEEK(pb, sizeof(uint16_t)); 277 | continue; 278 | 279 | } else if (SO_OR == PB_PTR_UINT16(pb)) { 280 | n->type = ST_OR; 281 | PB_SEEK(pb, sizeof(uint16_t)); 282 | continue; 283 | 284 | } else if (SO_NOT == PB_PTR_UINT16(pb)) { 285 | n->type = ST_NOT; 286 | PB_SEEK(pb, sizeof(uint16_t)); 287 | continue; 288 | 289 | } else if (SO_STRING_TERM == PB_PTR_UINT8(pb)) { 290 | n->type = ST_STRING; 291 | PB_SEEK(pb, 1); 292 | PB_READ_UINT16(pb, n->str_len); 293 | n->str_val = (const char *) pb->ptr; 294 | PB_SEEK(pb, n->str_len); 295 | n->string_term = 1; 296 | 297 | } else if (SO_STRING_CONSTR == PB_PTR_UINT8(pb)) { 298 | uint16_t tail1; 299 | uint8_t tail2; 300 | PB_SEEK(pb, 1); 301 | PB_READ_UINT16(pb, n->str_len); 302 | n->str_val = (const char *) pb->ptr; 303 | PB_SEEK(pb, n->str_len); 304 | PB_READ_UINT16(pb, tail1); 305 | PB_READ_UINT8(pb, tail2); 306 | // todo: add macro for this magic constants 307 | if ((0x0001 == tail1) && (0x04 == tail2)) { 308 | n->type = ST_EXTENSION; 309 | } else if ((0x0001 == tail1) && (0xd5 == tail2)) { 310 | n->type = ST_CODEC; 311 | } else if ((0x0001 == tail1) && (0x03 == tail2)) { 312 | n->type = ST_TYPE; 313 | } else { 314 | PB_CHECK(0); 315 | } 316 | 317 | } else if ((SO_UINT32 == PB_PTR_UINT8(pb)) || (SO_UINT64 == PB_PTR_UINT8(pb))) { 318 | uint32_t constr; 319 | uint8_t type; 320 | PB_READ_UINT8(pb, type); 321 | 322 | if (SO_UINT32 == type) { 323 | PB_READ_UINT32(pb, n->int_val); 324 | } else { 325 | PB_READ_UINT64(pb, n->int_val); 326 | } 327 | 328 | PB_READ_UINT32(pb, constr); 329 | if (SC_MINSIZE == constr) { 330 | n->type = ST_MINSIZE; 331 | } else if (SC_MAXSIZE == constr) { 332 | n->type = ST_MAXSIZE; 333 | } else if (SC_SRCAVAIL == constr) { 334 | n->type = ST_SRCAVAIL; 335 | } else if (SC_SRCCMPLETE == constr) { 336 | n->type = ST_SRCCOMLETE; 337 | } else if (SC_MINBITRATE == constr) { 338 | n->type = ST_MINBITRATE; 339 | } else if (SC_MINLENGTH == constr) { 340 | n->type = ST_MINLENGTH; 341 | } else { 342 | PB_CHECK(0); 343 | } 344 | } 345 | 346 | } 347 | 348 | n = n->parent; 349 | } 350 | 351 | client_search_files(clnt, &root); 352 | return 1; 353 | 354 | malformed: 355 | return 0; 356 | } 357 | 358 | static int process_packet(struct packet_buffer *pb, uint8_t opcode, struct client *clnt) 359 | { 360 | PB_CHECK(clnt->portcheck_finished || (OP_LOGINREQUEST == opcode)); 361 | 362 | switch (opcode) { 363 | case OP_LOGINREQUEST: 364 | /* client already logined */ 365 | if (clnt->id) 366 | client_delete(clnt); 367 | 368 | send_server_message(clnt->bev, g_srv.cfg->welcome_msg, g_srv.cfg->welcome_msg_len); 369 | if (!g_srv.cfg->allow_lowid) { 370 | static const char msg_highid[] = "WARNING: Only HighID clients!"; 371 | send_server_message(clnt->bev, msg_highid, sizeof(msg_highid) - 1); 372 | } 373 | PB_CHECK(process_login_request(pb, clnt)); 374 | return 1; 375 | 376 | case OP_GETSERVERLIST: 377 | send_server_ident(clnt->bev); 378 | send_server_list(clnt->bev); 379 | return 1; 380 | 381 | case OP_SEARCHREQUEST: 382 | if (!token_bucket_update(&clnt->limit_search, g_srv.cfg->max_searches_limit)) { 383 | ED2KD_LOGDBG("search limit reached for %u", clnt->id); 384 | client_delete(clnt); 385 | return 0; 386 | } 387 | process_search_request(pb, clnt); 388 | return 1; 389 | 390 | case OP_QUERY_MORE_RESULT: 391 | return 1; 392 | 393 | case OP_DISCONNECT: 394 | client_delete(clnt); 395 | return 1; 396 | 397 | case OP_GETSOURCES: 398 | PB_CHECK(PB_LEFT(pb) == ED2K_HASH_SIZE); 399 | client_get_sources(clnt, pb->ptr); 400 | return 1; 401 | 402 | case OP_OFFERFILES: 403 | if (!token_bucket_update(&clnt->limit_offer, g_srv.cfg->max_offers_limit)) { 404 | ED2KD_LOGDBG("offer limit reached for %u", clnt->id); 405 | client_delete(clnt); 406 | return 0; 407 | } 408 | PB_CHECK(process_offer_files(pb, clnt)); 409 | return 1; 410 | 411 | case OP_CALLBACKREQUEST: 412 | send_callback_fail(clnt->bev); 413 | return 1; 414 | 415 | case OP_GETSOURCES_OBFU: 416 | return 1; 417 | 418 | case OP_REJECT: 419 | return 1; 420 | 421 | default: 422 | PB_CHECK(0); 423 | } 424 | 425 | malformed: 426 | ED2KD_LOGDBG("malformed tcp packet (opcode:%u)", opcode); 427 | client_delete(clnt); 428 | return 0; 429 | } 430 | 431 | static void server_read(struct client *clnt) 432 | { 433 | struct evbuffer *input = bufferevent_get_input(clnt->bev); 434 | size_t src_len = evbuffer_get_length(input); 435 | 436 | while (!clnt->deleted && src_len > sizeof(struct packet_header)) { 437 | unsigned char *data; 438 | struct packet_buffer pb; 439 | size_t packet_len; 440 | int ret; 441 | const struct packet_header *header = 442 | (struct packet_header *) evbuffer_pullup(input, sizeof(struct packet_header)); 443 | 444 | if ((PROTO_PACKED != header->proto) && (PROTO_EDONKEY != header->proto)) { 445 | ED2KD_LOGDBG("unknown packet protocol from %s:%u", clnt->dbg.ip_str, clnt->port); 446 | client_delete(clnt); 447 | return; 448 | } 449 | 450 | // wait for full length packet 451 | packet_len = header->length + sizeof(struct packet_header); 452 | if (packet_len > src_len) 453 | return; 454 | 455 | data = evbuffer_pullup(input, packet_len); 456 | header = (struct packet_header *) data; 457 | data += sizeof(struct packet_header); 458 | 459 | if (PROTO_PACKED == header->proto) { 460 | unsigned long unpacked_len = MAX_UNCOMPRESSED_PACKET_SIZE; 461 | unsigned char *unpacked = (unsigned char *) malloc(unpacked_len); 462 | 463 | ret = uncompress(unpacked, &unpacked_len, data + 1, header->length - 1); 464 | if (Z_OK == ret) { 465 | PB_INIT(&pb, unpacked, unpacked_len); 466 | ret = process_packet(&pb, *data, clnt); 467 | } else { 468 | ED2KD_LOGDBG("failed to unpack packet from %s:%u", clnt->dbg.ip_str, clnt->port); 469 | ret = 0; 470 | } 471 | free(unpacked); 472 | } else { 473 | PB_INIT(&pb, data + 1, header->length - 1); 474 | ret = process_packet(&pb, *data, clnt); 475 | } 476 | 477 | if (!ret) 478 | return; 479 | 480 | evbuffer_drain(input, packet_len); 481 | src_len = evbuffer_get_length(input); 482 | } 483 | } 484 | 485 | static void server_event(struct client *clnt, short events) 486 | { 487 | if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { 488 | ED2KD_LOGDBG("got EOF or error from %s:%u", clnt->dbg.ip_str, clnt->port); 489 | client_delete(clnt); 490 | } 491 | } 492 | 493 | void *server_job_worker(void *ctx) 494 | { 495 | (void) ctx; 496 | 497 | if (!db_open()) { 498 | ED2KD_LOGERR("failed to open database"); 499 | return NULL; 500 | } 501 | 502 | for (; ;) { 503 | struct job *job = 0; 504 | 505 | pthread_mutex_lock(&g_srv.job_mutex); 506 | for (; ;) { 507 | struct job *j, *jtmp; 508 | 509 | if (atomic_load(&g_srv.terminate)) { 510 | pthread_mutex_unlock(&g_srv.job_mutex); 511 | goto exit; 512 | } 513 | 514 | TAILQ_FOREACH_SAFE(j, &g_srv.jqueue, qentry, jtmp) { 515 | uint32_t old_val = 0; 516 | if (atomic_compare_exchange_strong(&j->clnt->locked, &old_val, 1)) { 517 | TAILQ_REMOVE(&g_srv.jqueue, j, qentry); 518 | job = j; 519 | break; 520 | } 521 | } 522 | 523 | if (job) 524 | break; 525 | 526 | pthread_cond_wait(&g_srv.job_cond, &g_srv.job_mutex); 527 | } 528 | 529 | pthread_mutex_unlock(&g_srv.job_mutex); 530 | 531 | if (!atomic_load(&job->clnt->deleted)) { 532 | switch (job->type) { 533 | 534 | case JOB_SERVER_EVENT: { 535 | struct job_event *j = (struct job_event *) job; 536 | //ED2KD_LOGDBG("JOB_SERVER_EVENT event"); 537 | server_event(j->hdr.clnt, j->events); 538 | break; 539 | } 540 | 541 | case JOB_SERVER_READ: 542 | //ED2KD_LOGDBG("JOB_SERVER_READ event"); 543 | server_read(job->clnt); 544 | break; 545 | 546 | case JOB_SERVER_STATUS_NOTIFY: 547 | //ED2KD_LOGDBG("JOB_SERVER_STATUS_NOTIFY event"); 548 | send_server_status(job->clnt->bev); 549 | event_add(job->clnt->evtimer_status_notify, g_srv.status_notify_tv); 550 | break; 551 | 552 | case JOB_PORTCHECK_EVENT: { 553 | struct job_event *j = (struct job_event *) job; 554 | //ED2KD_LOGDBG("JOB_PORTCHECK_EVENT event"); 555 | portcheck_event(job->clnt, j->events); 556 | break; 557 | } 558 | 559 | case JOB_PORTCHECK_READ: 560 | //ED2KD_LOGDBG("JOB_PORTCHECK_READ event"); 561 | portcheck_read(job->clnt); 562 | break; 563 | 564 | case JOB_PORTCHECK_TIMEOUT: 565 | portcheck_timeout(job->clnt); 566 | break; 567 | 568 | default: 569 | assert(0); 570 | break; 571 | } 572 | } 573 | 574 | atomic_store(&job->clnt->locked, 0); 575 | client_decref(job->clnt); 576 | free(job); 577 | } 578 | 579 | exit: 580 | if (!db_close()) 581 | ED2KD_LOGERR("failed to close database"); 582 | 583 | return NULL; 584 | } 585 | 586 | void server_stop(void) 587 | { 588 | event_base_loopbreak(g_srv.evbase_main); 589 | event_base_loopbreak(g_srv.evbase_tcp); 590 | atomic_store(&g_srv.terminate, 1); 591 | } 592 | -------------------------------------------------------------------------------- /src/db_sqlite.c: -------------------------------------------------------------------------------- 1 | #include "db.h" 2 | #include 3 | 4 | #include "sqlite3/sqlite3.h" 5 | #include "ed2k_proto.h" 6 | #include "packet.h" 7 | #include "log.h" 8 | #include "client.h" 9 | 10 | static uint64_t sdbm(const unsigned char *str, size_t length) 11 | { 12 | uint64_t hash = 0; 13 | size_t i; 14 | 15 | for (i = 0; i < length; ++i) 16 | hash = (*str++) + (hash << 6) + (hash << 16) - hash; 17 | 18 | return hash; 19 | } 20 | 21 | #define DB_NAME "file:memdb?mode=memory&cache=shared" 22 | #define DB_OPEN_FLAGS SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_NOMUTEX|SQLITE_OPEN_SHAREDCACHE|SQLITE_OPEN_URI 23 | #define MAX_SEARCH_QUERY_LEN 1024 24 | #define MAX_NAME_TERM_LEN 1024 25 | 26 | #define DB_CHECK(x) if (!(x)) goto failed; 27 | #define MAKE_FID(x) sdbm((x), 16) 28 | #define MAKE_SID(x) ( ((uint64_t)(x)->id<<32) | (uint64_t)(x)->port ) 29 | #define GET_SID_ID(sid) (uint32_t)((sid)>>32) 30 | #define GET_SID_PORT(sid) (uint16_t)(sid) 31 | 32 | enum query_statements { 33 | SHARE_UPD, 34 | SHARE_INS, 35 | SHARE_SRC, 36 | REMOVE_SRC, 37 | GET_SRC, 38 | STMT_COUNT 39 | }; 40 | 41 | static THREAD_LOCAL sqlite3 42 | * 43 | s_db; 44 | static THREAD_LOCAL sqlite3_stmt 45 | *s_stmt[STMT_COUNT]; 46 | 47 | int db_create(void) 48 | { 49 | static const char query[] = 50 | "PRAGMA synchronous = 0;" 51 | "PRAGMA journal_mode = OFF;" 52 | 53 | "CREATE TABLE IF NOT EXISTS files (" 54 | " fid INTEGER PRIMARY KEY," 55 | " hash BLOB NOT NULL," 56 | " name TEXT NOT NULL," 57 | " ext TEXT," 58 | " size INTEGER NOT NULL," 59 | " type INTEGER NOT NULL," 60 | " srcavail INTEGER DEFAULT 0," 61 | " srccomplete INTEGER DEFAULT 0," 62 | " rating INTEGER DEFAULT 0," 63 | " rated_count INTEGER DEFAULT 0," 64 | " mlength INTEGER," 65 | " mbitrate INTEGER," 66 | " mcodec TEXT" 67 | ");" 68 | 69 | "CREATE VIRTUAL TABLE IF NOT EXISTS fnames USING fts4 (" 70 | " content=\"files\", tokenize=unicode61, name" 71 | ");" 72 | 73 | "CREATE TABLE IF NOT EXISTS sources (" 74 | " fid INTEGER NOT NULL," 75 | " sid INTEGER NOT NULL," 76 | " complete INTEGER," 77 | " rating INTEGER" 78 | ");" 79 | "CREATE INDEX IF NOT EXISTS sources_fid_i" 80 | " ON sources(fid);" 81 | "CREATE INDEX IF NOT EXISTS sources_sid_i" 82 | " ON sources(sid);" 83 | 84 | "CREATE TRIGGER IF NOT EXISTS sources_ai AFTER INSERT ON sources BEGIN" 85 | " UPDATE files SET srcavail=srcavail+1,srccomplete=srccomplete+new.complete," 86 | " rating=rating+new.rating, rated_count = CASE WHEN new.rating<>0 THEN rated_count+1 ELSE 0 END" 87 | " WHERE fid=new.fid;" 88 | "END;" 89 | "CREATE TRIGGER IF NOT EXISTS sources_bd BEFORE DELETE ON sources BEGIN" 90 | " UPDATE files SET srcavail=srcavail-1,srccomplete=srccomplete-old.complete," 91 | " rating=rating-old.rating, rated_count = CASE WHEN old.rating<>0 THEN rated_count-1 ELSE rated_count END" 92 | " WHERE fid=old.fid;" 93 | "END;" 94 | 95 | // delete when no sources available 96 | " CREATE TRIGGER IF NOT EXISTS files_au AFTER UPDATE ON files WHEN new.srcavail=0 BEGIN" 97 | " DELETE FROM files WHERE fid=new.fid;" 98 | "END;" 99 | 100 | // update on file name change 101 | "CREATE TRIGGER IF NOT EXISTS files_fts1 BEFORE UPDATE ON files WHEN new.name<>old.name BEGIN" 102 | " DELETE FROM fnames WHERE docid=old.rowid;" 103 | "END;" 104 | "CREATE TRIGGER IF NOT EXISTS files_fts2 AFTER UPDATE ON files WHEN new.name<>old.name BEGIN" 105 | " INSERT INTO fnames(docid, name) VALUES(new.rowid, new.name);" 106 | "END;" 107 | // delete 108 | "CREATE TRIGGER IF NOT EXISTS files_fts3 BEFORE DELETE ON files BEGIN" 109 | " DELETE FROM fnames WHERE docid=old.rowid;" 110 | "END;" 111 | // insert 112 | "CREATE TRIGGER IF NOT EXISTS files_fts4 AFTER INSERT ON files BEGIN" 113 | " INSERT INTO fnames(docid, name) VALUES(new.rowid, new.name);" 114 | "END;" 115 | 116 | "DELETE FROM files;" 117 | "DELETE FROM fnames;" 118 | "DELETE FROM sources;"; 119 | 120 | int err; 121 | 122 | if (!sqlite3_threadsafe()) { 123 | ED2KD_LOGERR("sqlite3 must be threadsafe"); 124 | return 0; 125 | } 126 | 127 | err = sqlite3_open_v2(DB_NAME, &s_db, DB_OPEN_FLAGS, NULL); 128 | 129 | if (SQLITE_OK == err) { 130 | char *errmsg; 131 | 132 | err = sqlite3_exec(s_db, query, NULL, NULL, &errmsg); 133 | if (SQLITE_OK != err) { 134 | ED2KD_LOGERR("failed to execute database init script (%s)", errmsg); 135 | sqlite3_free(errmsg); 136 | return 0; 137 | } 138 | } else { 139 | ED2KD_LOGERR("failed to create DB (%s)", sqlite3_errmsg(s_db)); 140 | return 0; 141 | } 142 | 143 | return 1; 144 | } 145 | 146 | int db_open(void) 147 | { 148 | int err; 149 | const char *tail; 150 | 151 | static const char query_share_upd[] = 152 | "UPDATE files SET name=?,ext=?,size=?,type=?,mlength=?,mbitrate=?,mcodec=? WHERE fid=?"; 153 | static const char query_share_ins[] = 154 | "INSERT OR REPLACE INTO files(fid,hash,name,ext,size,type,mlength,mbitrate,mcodec) " 155 | " VALUES(?,?,?,?,?,?,?,?,?)"; 156 | static const char query_share_src[] = 157 | "INSERT INTO sources(fid,sid,complete,rating) VALUES(?,?,?,?)"; 158 | static const char query_remove_src[] = 159 | "DELETE FROM sources WHERE sid=?"; 160 | static const char query_get_src[] = 161 | "SELECT sid FROM sources WHERE fid=? LIMIT ?"; 162 | 163 | err = sqlite3_open_v2(DB_NAME, &s_db, DB_OPEN_FLAGS, NULL); 164 | if (SQLITE_OK != err) { 165 | ED2KD_LOGERR("failed to open DB (%s)", sqlite3_errmsg(s_db)); 166 | return 0; 167 | } 168 | 169 | DB_CHECK(SQLITE_OK == sqlite3_prepare_v2(s_db, query_share_upd, sizeof(query_share_upd), &s_stmt[SHARE_UPD], &tail)); 170 | DB_CHECK(SQLITE_OK == sqlite3_prepare_v2(s_db, query_share_ins, sizeof(query_share_ins), &s_stmt[SHARE_INS], &tail)); 171 | DB_CHECK(SQLITE_OK == sqlite3_prepare_v2(s_db, query_share_src, sizeof(query_share_src), &s_stmt[SHARE_SRC], &tail)); 172 | DB_CHECK(SQLITE_OK == sqlite3_prepare_v2(s_db, query_remove_src, sizeof(query_remove_src), &s_stmt[REMOVE_SRC], &tail)); 173 | DB_CHECK(SQLITE_OK == sqlite3_prepare_v2(s_db, query_get_src, sizeof(query_get_src), &s_stmt[GET_SRC], &tail)); 174 | 175 | return 1; 176 | 177 | failed: 178 | db_close(); 179 | return 0; 180 | } 181 | 182 | int db_destroy(void) 183 | { 184 | return SQLITE_OK == sqlite3_close(s_db); 185 | } 186 | 187 | int db_close(void) 188 | { 189 | size_t i; 190 | 191 | for (i = 0; i < STMT_COUNT; ++i) { 192 | if (s_stmt[i]) 193 | sqlite3_finalize(s_stmt[i]); 194 | } 195 | 196 | return SQLITE_OK == sqlite3_close(s_db); 197 | } 198 | 199 | int db_share_files(const struct pub_file *files, size_t count, const struct client *owner) 200 | { 201 | /* todo: do it in transaction */ 202 | 203 | while (count-- > 0) { 204 | sqlite3_stmt *stmt; 205 | const char *ext; 206 | int ext_len; 207 | int i; 208 | uint64_t fid; 209 | 210 | if (!files->name_len) { 211 | files++; 212 | continue; 213 | } 214 | 215 | fid = MAKE_FID(files->hash); 216 | 217 | // find extension 218 | ext = file_extension(files->name, files->name_len); 219 | if (ext) 220 | ext_len = files->name + files->name_len - ext; 221 | else 222 | ext_len = 0; 223 | 224 | i = 1; 225 | stmt = s_stmt[SHARE_UPD]; 226 | DB_CHECK(SQLITE_OK == sqlite3_reset(stmt)); 227 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, files->name, files->name_len, SQLITE_STATIC)); 228 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, ext, ext_len, SQLITE_STATIC)); 229 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, files->size)); 230 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->type)); 231 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->media_length)); 232 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->media_bitrate)); 233 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, files->media_codec, files->media_codec_len, SQLITE_STATIC)); 234 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, fid)); 235 | DB_CHECK(SQLITE_DONE == sqlite3_step(stmt)); 236 | 237 | if (!sqlite3_changes(s_db)) { 238 | i = 1; 239 | stmt = s_stmt[SHARE_INS]; 240 | DB_CHECK(SQLITE_OK == sqlite3_reset(stmt)); 241 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, fid)); 242 | DB_CHECK(SQLITE_OK == sqlite3_bind_blob(stmt, i++, files->hash, sizeof(files->hash), SQLITE_STATIC)); 243 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, files->name, files->name_len, SQLITE_STATIC)); 244 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, ext, ext_len, SQLITE_STATIC)); 245 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, files->size)); 246 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->type)); 247 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->media_length)); 248 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->media_bitrate)); 249 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, files->media_codec, files->media_codec_len, SQLITE_STATIC)); 250 | DB_CHECK(SQLITE_DONE == sqlite3_step(stmt)); 251 | } 252 | 253 | i = 1; 254 | stmt = s_stmt[SHARE_SRC]; 255 | DB_CHECK(SQLITE_OK == sqlite3_reset(stmt)); 256 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, fid)); 257 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, MAKE_SID(owner))); 258 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->complete)); 259 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, files->rating)); 260 | DB_CHECK(SQLITE_DONE == sqlite3_step(stmt)); 261 | 262 | files++; 263 | } 264 | 265 | return 1; 266 | 267 | failed: 268 | ED2KD_LOGERR("failed to add file to db (%s)", sqlite3_errmsg(s_db)); 269 | return 0; 270 | } 271 | 272 | int db_remove_source(const struct client *clnt) 273 | { 274 | sqlite3_stmt *stmt = s_stmt[REMOVE_SRC]; 275 | 276 | DB_CHECK(SQLITE_OK == sqlite3_reset(stmt)); 277 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, 1, MAKE_SID(clnt))); 278 | DB_CHECK(SQLITE_DONE == sqlite3_step(stmt)); 279 | return 1; 280 | 281 | failed: 282 | ED2KD_LOGERR("failed to remove sources from db (%s)", sqlite3_errmsg(s_db)); 283 | return 0; 284 | } 285 | 286 | int db_search_files(struct search_node *snode, struct evbuffer *buf, size_t *count) 287 | { 288 | int err; 289 | const char *tail; 290 | sqlite3_stmt *stmt = 0; 291 | size_t i; 292 | struct { 293 | char name_term[MAX_NAME_TERM_LEN + 1]; 294 | size_t name_len; 295 | uint64_t minsize; 296 | uint64_t maxsize; 297 | uint64_t srcavail; 298 | uint64_t srccomplete; 299 | uint64_t minbitrate; 300 | uint64_t minlength; 301 | struct search_node *ext_node; 302 | struct search_node *codec_node; 303 | struct search_node *type_node; 304 | } params; 305 | char query[MAX_SEARCH_QUERY_LEN + 1] = 306 | " SELECT f.hash,f.name,f.size,f.type,f.ext,f.srcavail,f.srccomplete,f.rating,f.rated_count," 307 | " (SELECT sid FROM sources WHERE fid=f.fid LIMIT 1) AS sid," 308 | " f.mlength,f.mbitrate,f.mcodec " 309 | " FROM fnames n" 310 | " JOIN files f ON f.fid = n.docid" 311 | " WHERE fnames MATCH ?"; 312 | 313 | memset(¶ms, 0, sizeof params); 314 | 315 | while (snode) { 316 | if ((ST_AND <= snode->type) && (ST_NOT >= snode->type)) { 317 | if (!snode->left_visited) { 318 | if (snode->string_term) { 319 | params.name_len++; 320 | DB_CHECK(params.name_len < sizeof params.name_term); 321 | strcat(params.name_term, "("); 322 | } 323 | snode->left_visited = 1; 324 | snode = snode->left; 325 | continue; 326 | } else if (!snode->right_visited) { 327 | if (snode->string_term) { 328 | const char *oper = 0; 329 | switch (snode->type) { 330 | case ST_AND: 331 | params.name_len += 5; 332 | oper = " AND "; 333 | break; 334 | case ST_OR: 335 | params.name_len += 4; 336 | oper = " OR "; 337 | break; 338 | case ST_NOT: 339 | params.name_len += 5; 340 | oper = " NOT "; 341 | break; 342 | 343 | default: 344 | DB_CHECK(0); 345 | } 346 | DB_CHECK(params.name_len < sizeof params.name_term); 347 | strcat(params.name_term, oper); 348 | } 349 | snode->right_visited = 1; 350 | snode = snode->right; 351 | continue; 352 | } else { 353 | if (snode->string_term) { 354 | params.name_len++; 355 | DB_CHECK(params.name_len < sizeof params.name_term); 356 | strcat(params.name_term, ")"); 357 | } 358 | } 359 | } else { 360 | switch (snode->type) { 361 | case ST_STRING: 362 | params.name_len += snode->str_len; 363 | DB_CHECK(params.name_len < sizeof params.name_term); 364 | strncat(params.name_term, snode->str_val, snode->str_len); 365 | break; 366 | case ST_EXTENSION: 367 | params.ext_node = snode; 368 | break; 369 | case ST_CODEC: 370 | params.codec_node = snode; 371 | break; 372 | case ST_MINSIZE: 373 | params.minsize = snode->int_val; 374 | break; 375 | case ST_MAXSIZE: 376 | params.maxsize = snode->int_val; 377 | break; 378 | case ST_SRCAVAIL: 379 | params.srcavail = snode->int_val; 380 | break; 381 | case ST_SRCCOMLETE: 382 | params.srccomplete = snode->int_val; 383 | break; 384 | case ST_MINBITRATE: 385 | params.minbitrate = snode->int_val; 386 | break; 387 | case ST_MINLENGTH: 388 | params.minlength = snode->int_val; 389 | break; 390 | case ST_TYPE: 391 | params.type_node = snode; 392 | break; 393 | default: 394 | DB_CHECK(0); 395 | } 396 | } 397 | 398 | snode = snode->parent; 399 | } 400 | 401 | if (params.ext_node) { 402 | strcat(query, " AND f.ext=?"); 403 | } 404 | if (params.codec_node) { 405 | strcat(query, " AND f.mcodec=?"); 406 | } 407 | if (params.minsize) { 408 | strcat(query, " AND f.size>?"); 409 | } 410 | if (params.maxsize) { 411 | strcat(query, " AND f.size?"); 415 | } 416 | if (params.srccomplete) { 417 | strcat(query, " AND f.srccomplete>?"); 418 | } 419 | if (params.minbitrate) { 420 | strcat(query, " AND f.mbitrate>?"); 421 | } 422 | if (params.minlength) { 423 | strcat(query, " AND f.mlength>?"); 424 | } 425 | if (params.type_node) { 426 | strcat(query, " AND f.type=?"); 427 | } 428 | strcat(query, " LIMIT ?"); 429 | 430 | DB_CHECK(SQLITE_OK == sqlite3_prepare_v2(s_db, query, strlen(query) + 1, &stmt, &tail)); 431 | 432 | i = 1; 433 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, params.name_term, params.name_len + 1, SQLITE_STATIC)); 434 | 435 | if (params.ext_node) { 436 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, params.ext_node->str_val, params.ext_node->str_len, SQLITE_STATIC)); 437 | } 438 | if (params.codec_node) { 439 | DB_CHECK(SQLITE_OK == sqlite3_bind_text(stmt, i++, params.codec_node->str_val, params.codec_node->str_len, SQLITE_STATIC)); 440 | } 441 | if (params.minsize) { 442 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, params.minsize)); 443 | } 444 | if (params.maxsize) { 445 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, params.maxsize)); 446 | } 447 | if (params.srcavail) { 448 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, params.srcavail)); 449 | } 450 | if (params.srccomplete) { 451 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, params.srccomplete)); 452 | } 453 | if (params.minbitrate) { 454 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, params.minbitrate)); 455 | } 456 | if (params.minlength) { 457 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, i++, params.minlength)); 458 | } 459 | if (params.type_node) { 460 | uint8_t type = get_ed2k_file_type(params.type_node->str_val, params.type_node->str_len); 461 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, type)); 462 | } 463 | 464 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, i++, *count)); 465 | 466 | i = 0; 467 | while (((err = sqlite3_step(stmt)) == SQLITE_ROW) && (i < *count)) { 468 | struct search_file sfile; 469 | uint64_t sid; 470 | int col = 0; 471 | 472 | memset(&sfile, 0, sizeof sfile); 473 | 474 | sfile.hash = (const unsigned char *) sqlite3_column_blob(stmt, col++); 475 | 476 | sfile.name_len = sqlite3_column_bytes(stmt, col); 477 | sfile.name_len = sfile.name_len > MAX_FILENAME_LEN ? MAX_FILENAME_LEN : sfile.name_len; 478 | sfile.name = (const char *) sqlite3_column_text(stmt, col++); 479 | 480 | sfile.size = sqlite3_column_int64(stmt, col++); 481 | sfile.type = sqlite3_column_int(stmt, col++); 482 | 483 | sfile.ext_len = sqlite3_column_bytes(stmt, col); 484 | sfile.ext_len = sfile.ext_len > MAX_FILEEXT_LEN ? MAX_FILEEXT_LEN : sfile.ext_len; 485 | sfile.ext = (const char *) sqlite3_column_text(stmt, col++); 486 | 487 | sfile.srcavail = sqlite3_column_int(stmt, col++); 488 | sfile.srccomplete = sqlite3_column_int(stmt, col++); 489 | sfile.rating = sqlite3_column_int(stmt, col++); 490 | sfile.rated_count = sqlite3_column_int(stmt, col++); 491 | 492 | sid = sqlite3_column_int64(stmt, col++); 493 | sfile.client_id = GET_SID_ID(sid); 494 | sfile.client_port = GET_SID_PORT(sid); 495 | 496 | sfile.media_length = sqlite3_column_int(stmt, col++); 497 | sfile.media_bitrate = sqlite3_column_int(stmt, col++); 498 | 499 | sfile.media_codec_len = sqlite3_column_bytes(stmt, col); 500 | sfile.media_codec_len = sfile.media_codec_len > MAX_FILEEXT_LEN ? MAX_FILEEXT_LEN : sfile.media_codec_len; 501 | sfile.media_codec = (const char *) sqlite3_column_text(stmt, col++); 502 | 503 | write_search_file(buf, &sfile); 504 | 505 | ++i; 506 | } 507 | 508 | DB_CHECK((i == *count) || (SQLITE_DONE == err)); 509 | 510 | *count = i; 511 | return 1; 512 | 513 | failed: 514 | if (stmt) sqlite3_finalize(stmt); 515 | ED2KD_LOGERR("failed perform search query (%s)", sqlite3_errmsg(s_db)); 516 | 517 | return 0; 518 | } 519 | 520 | int db_get_sources(const unsigned char *hash, struct file_source *sources, uint8_t *count) 521 | { 522 | sqlite3_stmt *stmt = s_stmt[GET_SRC]; 523 | uint8_t i; 524 | int err; 525 | 526 | DB_CHECK(SQLITE_OK == sqlite3_reset(stmt)); 527 | DB_CHECK(SQLITE_OK == sqlite3_bind_int64(stmt, 1, MAKE_FID(hash))); 528 | DB_CHECK(SQLITE_OK == sqlite3_bind_int(stmt, 2, *count)); 529 | 530 | i = 0; 531 | while (((err = sqlite3_step(stmt)) == SQLITE_ROW) && (i < *count)) { 532 | uint64_t sid = sqlite3_column_int64(stmt, 0); 533 | sources[i].ip = GET_SID_ID(sid); 534 | sources[i].port = GET_SID_PORT(sid); 535 | ++i; 536 | } 537 | 538 | DB_CHECK((i == *count) || (SQLITE_DONE == err)); 539 | 540 | *count = i; 541 | return 1; 542 | 543 | failed: 544 | ED2KD_LOGERR("failed to get sources from db (%s)", sqlite3_errmsg(s_db)); 545 | return 0; 546 | } 547 | -------------------------------------------------------------------------------- /src/queue.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1991, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 4. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 30 | * $FreeBSD$ 31 | */ 32 | 33 | #ifndef ED2KD_QUEUE_H 34 | #define ED2KD_QUEUE_H 35 | 36 | //#include 37 | 38 | /* 39 | * This file defines four types of data structures: singly-linked lists, 40 | * singly-linked tail queues, lists and tail queues. 41 | * 42 | * A singly-linked list is headed by a single forward pointer. The elements 43 | * are singly linked for minimum space and pointer manipulation overhead at 44 | * the expense of O(n) removal for arbitrary elements. New elements can be 45 | * added to the list after an existing element or at the head of the list. 46 | * Elements being removed from the head of the list should use the explicit 47 | * macro for this purpose for optimum efficiency. A singly-linked list may 48 | * only be traversed in the forward direction. Singly-linked lists are ideal 49 | * for applications with large datasets and few or no removals or for 50 | * implementing a LIFO queue. 51 | * 52 | * A singly-linked tail queue is headed by a pair of pointers, one to the 53 | * head of the list and the other to the tail of the list. The elements are 54 | * singly linked for minimum space and pointer manipulation overhead at the 55 | * expense of O(n) removal for arbitrary elements. New elements can be added 56 | * to the list after an existing element, at the head of the list, or at the 57 | * end of the list. Elements being removed from the head of the tail queue 58 | * should use the explicit macro for this purpose for optimum efficiency. 59 | * A singly-linked tail queue may only be traversed in the forward direction. 60 | * Singly-linked tail queues are ideal for applications with large datasets 61 | * and few or no removals or for implementing a FIFO queue. 62 | * 63 | * A list is headed by a single forward pointer (or an array of forward 64 | * pointers for a hash table header). The elements are doubly linked 65 | * so that an arbitrary element can be removed without a need to 66 | * traverse the list. New elements can be added to the list before 67 | * or after an existing element or at the head of the list. A list 68 | * may only be traversed in the forward direction. 69 | * 70 | * A tail queue is headed by a pair of pointers, one to the head of the 71 | * list and the other to the tail of the list. The elements are doubly 72 | * linked so that an arbitrary element can be removed without a need to 73 | * traverse the list. New elements can be added to the list before or 74 | * after an existing element, at the head of the list, or at the end of 75 | * the list. A tail queue may be traversed in either direction. 76 | * 77 | * For details on the use of these macros, see the queue(3) manual page. 78 | * 79 | * 80 | * SLIST LIST STAILQ TAILQ 81 | * _HEAD + + + + 82 | * _HEAD_INITIALIZER + + + + 83 | * _ENTRY + + + + 84 | * _INIT + + + + 85 | * _EMPTY + + + + 86 | * _FIRST + + + + 87 | * _NEXT + + + + 88 | * _PREV - - - + 89 | * _LAST - - + + 90 | * _FOREACH + + + + 91 | * _FOREACH_SAFE + + + + 92 | * _FOREACH_REVERSE - - - + 93 | * _FOREACH_REVERSE_SAFE - - - + 94 | * _INSERT_HEAD + + + + 95 | * _INSERT_BEFORE - + - + 96 | * _INSERT_AFTER + + + + 97 | * _INSERT_TAIL - - + + 98 | * _CONCAT - - + + 99 | * _REMOVE_AFTER + - + - 100 | * _REMOVE_HEAD + - + - 101 | * _REMOVE + + + + 102 | * _SWAP + + + + 103 | * 104 | */ 105 | #ifdef QUEUE_MACRO_DEBUG 106 | /* Store the last 2 places the queue element or head was altered */ 107 | struct qm_trace { 108 | char * lastfile; 109 | int lastline; 110 | char * prevfile; 111 | int prevline; 112 | }; 113 | 114 | #define TRACEBUF struct qm_trace trace; 115 | #define TRASHIT(x) do {(x) = (void *)-1;} while (0) 116 | #define QMD_SAVELINK(name, link) void **name = (void *)&(link) 117 | 118 | #define QMD_TRACE_HEAD(head) do { \ 119 | (head)->trace.prevline = (head)->trace.lastline; \ 120 | (head)->trace.prevfile = (head)->trace.lastfile; \ 121 | (head)->trace.lastline = __LINE__; \ 122 | (head)->trace.lastfile = __FILE__; \ 123 | } while (0) 124 | 125 | #define QMD_TRACE_ELEM(elem) do { \ 126 | (elem)->trace.prevline = (elem)->trace.lastline; \ 127 | (elem)->trace.prevfile = (elem)->trace.lastfile; \ 128 | (elem)->trace.lastline = __LINE__; \ 129 | (elem)->trace.lastfile = __FILE__; \ 130 | } while (0) 131 | 132 | #else 133 | #define QMD_TRACE_ELEM(elem) 134 | #define QMD_TRACE_HEAD(head) 135 | #define QMD_SAVELINK(name, link) 136 | #define TRACEBUF 137 | #define TRASHIT(x) 138 | #endif /* QUEUE_MACRO_DEBUG */ 139 | 140 | #if 0 141 | /* 142 | * Singly-linked List declarations. 143 | */ 144 | #define SLIST_HEAD(name, type) \ 145 | struct name { \ 146 | struct type *slh_first; /* first element */ \ 147 | } 148 | 149 | #define SLIST_HEAD_INITIALIZER(head) \ 150 | { NULL } 151 | 152 | #define SLIST_ENTRY(type) \ 153 | struct { \ 154 | struct type *sle_next; /* next element */ \ 155 | } 156 | 157 | /* 158 | * Singly-linked List functions. 159 | */ 160 | #define SLIST_EMPTY(head) ((head)->slh_first == NULL) 161 | 162 | #define SLIST_FIRST(head) ((head)->slh_first) 163 | 164 | #define SLIST_FOREACH(var, head, field) \ 165 | for ((var) = SLIST_FIRST((head)); \ 166 | (var); \ 167 | (var) = SLIST_NEXT((var), field)) 168 | 169 | #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ 170 | for ((var) = SLIST_FIRST((head)); \ 171 | (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ 172 | (var) = (tvar)) 173 | 174 | #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ 175 | for ((varp) = &SLIST_FIRST((head)); \ 176 | ((var) = *(varp)) != NULL; \ 177 | (varp) = &SLIST_NEXT((var), field)) 178 | 179 | #define SLIST_INIT(head) do { \ 180 | SLIST_FIRST((head)) = NULL; \ 181 | } while (0) 182 | 183 | #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ 184 | SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ 185 | SLIST_NEXT((slistelm), field) = (elm); \ 186 | } while (0) 187 | 188 | #define SLIST_INSERT_HEAD(head, elm, field) do { \ 189 | SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ 190 | SLIST_FIRST((head)) = (elm); \ 191 | } while (0) 192 | 193 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) 194 | 195 | #define SLIST_REMOVE(head, elm, type, field) do { \ 196 | QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ 197 | if (SLIST_FIRST((head)) == (elm)) { \ 198 | SLIST_REMOVE_HEAD((head), field); \ 199 | } \ 200 | else { \ 201 | struct type *curelm = SLIST_FIRST((head)); \ 202 | while (SLIST_NEXT(curelm, field) != (elm)) \ 203 | curelm = SLIST_NEXT(curelm, field); \ 204 | SLIST_REMOVE_AFTER(curelm, field); \ 205 | } \ 206 | TRASHIT(*oldnext); \ 207 | } while (0) 208 | 209 | #define SLIST_REMOVE_AFTER(elm, field) do { \ 210 | SLIST_NEXT(elm, field) = \ 211 | SLIST_NEXT(SLIST_NEXT(elm, field), field); \ 212 | } while (0) 213 | 214 | #define SLIST_REMOVE_HEAD(head, field) do { \ 215 | SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ 216 | } while (0) 217 | 218 | #define SLIST_SWAP(head1, head2, type) do { \ 219 | struct type *swap_first = SLIST_FIRST(head1); \ 220 | SLIST_FIRST(head1) = SLIST_FIRST(head2); \ 221 | SLIST_FIRST(head2) = swap_first; \ 222 | } while (0) 223 | #endif 224 | 225 | #if 0 226 | /* 227 | * Singly-linked Tail queue declarations. 228 | */ 229 | #define STAILQ_HEAD(name, type) \ 230 | struct name { \ 231 | struct type *stqh_first;/* first element */ \ 232 | struct type **stqh_last;/* addr of last next element */ \ 233 | } 234 | 235 | #define STAILQ_HEAD_INITIALIZER(head) \ 236 | { NULL, &(head).stqh_first } 237 | 238 | #define STAILQ_ENTRY(type) \ 239 | struct { \ 240 | struct type *stqe_next; /* next element */ \ 241 | } 242 | 243 | /* 244 | * Singly-linked Tail queue functions. 245 | */ 246 | #define STAILQ_CONCAT(head1, head2) do { \ 247 | if (!STAILQ_EMPTY((head2))) { \ 248 | *(head1)->stqh_last = (head2)->stqh_first; \ 249 | (head1)->stqh_last = (head2)->stqh_last; \ 250 | STAILQ_INIT((head2)); \ 251 | } \ 252 | } while (0) 253 | 254 | #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) 255 | 256 | #define STAILQ_FIRST(head) ((head)->stqh_first) 257 | 258 | #define STAILQ_FOREACH(var, head, field) \ 259 | for((var) = STAILQ_FIRST((head)); \ 260 | (var); \ 261 | (var) = STAILQ_NEXT((var), field)) 262 | 263 | 264 | #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ 265 | for ((var) = STAILQ_FIRST((head)); \ 266 | (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ 267 | (var) = (tvar)) 268 | 269 | #define STAILQ_INIT(head) do { \ 270 | STAILQ_FIRST((head)) = NULL; \ 271 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 272 | } while (0) 273 | 274 | #define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ 275 | if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ 276 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 277 | STAILQ_NEXT((tqelm), field) = (elm); \ 278 | } while (0) 279 | 280 | #define STAILQ_INSERT_HEAD(head, elm, field) do { \ 281 | if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ 282 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 283 | STAILQ_FIRST((head)) = (elm); \ 284 | } while (0) 285 | 286 | #define STAILQ_INSERT_TAIL(head, elm, field) do { \ 287 | STAILQ_NEXT((elm), field) = NULL; \ 288 | *(head)->stqh_last = (elm); \ 289 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 290 | } while (0) 291 | 292 | #define STAILQ_LAST(head, type, field) \ 293 | (STAILQ_EMPTY((head)) ? \ 294 | NULL : \ 295 | ((struct type *)(void *) \ 296 | ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) 297 | 298 | #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) 299 | 300 | #define STAILQ_REMOVE(head, elm, type, field) do { \ 301 | QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ 302 | if (STAILQ_FIRST((head)) == (elm)) { \ 303 | STAILQ_REMOVE_HEAD((head), field); \ 304 | } \ 305 | else { \ 306 | struct type *curelm = STAILQ_FIRST((head)); \ 307 | while (STAILQ_NEXT(curelm, field) != (elm)) \ 308 | curelm = STAILQ_NEXT(curelm, field); \ 309 | STAILQ_REMOVE_AFTER(head, curelm, field); \ 310 | } \ 311 | TRASHIT(*oldnext); \ 312 | } while (0) 313 | 314 | #define STAILQ_REMOVE_AFTER(head, elm, field) do { \ 315 | if ((STAILQ_NEXT(elm, field) = \ 316 | STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ 317 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 318 | } while (0) 319 | 320 | #define STAILQ_REMOVE_HEAD(head, field) do { \ 321 | if ((STAILQ_FIRST((head)) = \ 322 | STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ 323 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 324 | } while (0) 325 | 326 | #define STAILQ_SWAP(head1, head2, type) do { \ 327 | struct type *swap_first = STAILQ_FIRST(head1); \ 328 | struct type **swap_last = (head1)->stqh_last; \ 329 | STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ 330 | (head1)->stqh_last = (head2)->stqh_last; \ 331 | STAILQ_FIRST(head2) = swap_first; \ 332 | (head2)->stqh_last = swap_last; \ 333 | if (STAILQ_EMPTY(head1)) \ 334 | (head1)->stqh_last = &STAILQ_FIRST(head1); \ 335 | if (STAILQ_EMPTY(head2)) \ 336 | (head2)->stqh_last = &STAILQ_FIRST(head2); \ 337 | } while (0) 338 | #endif 339 | 340 | #if 0 341 | /* 342 | * List declarations. 343 | */ 344 | #define LIST_HEAD(name, type) \ 345 | struct name { \ 346 | struct type *lh_first; /* first element */ \ 347 | } 348 | 349 | #define LIST_HEAD_INITIALIZER(head) \ 350 | { NULL } 351 | 352 | #define LIST_ENTRY(type) \ 353 | struct { \ 354 | struct type *le_next; /* next element */ \ 355 | struct type **le_prev; /* address of previous next element */ \ 356 | } 357 | 358 | /* 359 | * List functions. 360 | */ 361 | #if (defined(_KERNEL) && defined(INVARIANTS)) 362 | #define QMD_LIST_CHECK_HEAD(head, field) do { \ 363 | if (LIST_FIRST((head)) != NULL && \ 364 | LIST_FIRST((head))->field.le_prev != \ 365 | &LIST_FIRST((head))) \ 366 | panic("Bad list head %p first->prev != head", (head)); \ 367 | } while (0) 368 | 369 | #define QMD_LIST_CHECK_NEXT(elm, field) do { \ 370 | if (LIST_NEXT((elm), field) != NULL && \ 371 | LIST_NEXT((elm), field)->field.le_prev != \ 372 | &((elm)->field.le_next)) \ 373 | panic("Bad link elm %p next->prev != elm", (elm)); \ 374 | } while (0) 375 | 376 | #define QMD_LIST_CHECK_PREV(elm, field) do { \ 377 | if (*(elm)->field.le_prev != (elm)) \ 378 | panic("Bad link elm %p prev->next != elm", (elm)); \ 379 | } while (0) 380 | #else 381 | #define QMD_LIST_CHECK_HEAD(head, field) 382 | #define QMD_LIST_CHECK_NEXT(elm, field) 383 | #define QMD_LIST_CHECK_PREV(elm, field) 384 | #endif /* (_KERNEL && INVARIANTS) */ 385 | 386 | #define LIST_EMPTY(head) ((head)->lh_first == NULL) 387 | 388 | #define LIST_FIRST(head) ((head)->lh_first) 389 | 390 | #define LIST_FOREACH(var, head, field) \ 391 | for ((var) = LIST_FIRST((head)); \ 392 | (var); \ 393 | (var) = LIST_NEXT((var), field)) 394 | 395 | #define LIST_FOREACH_SAFE(var, head, field, tvar) \ 396 | for ((var) = LIST_FIRST((head)); \ 397 | (var) && ((tvar) = LIST_NEXT((var), field), 1); \ 398 | (var) = (tvar)) 399 | 400 | #define LIST_INIT(head) do { \ 401 | LIST_FIRST((head)) = NULL; \ 402 | } while (0) 403 | 404 | #define LIST_INSERT_AFTER(listelm, elm, field) do { \ 405 | QMD_LIST_CHECK_NEXT(listelm, field); \ 406 | if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ 407 | LIST_NEXT((listelm), field)->field.le_prev = \ 408 | &LIST_NEXT((elm), field); \ 409 | LIST_NEXT((listelm), field) = (elm); \ 410 | (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ 411 | } while (0) 412 | 413 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ 414 | QMD_LIST_CHECK_PREV(listelm, field); \ 415 | (elm)->field.le_prev = (listelm)->field.le_prev; \ 416 | LIST_NEXT((elm), field) = (listelm); \ 417 | *(listelm)->field.le_prev = (elm); \ 418 | (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ 419 | } while (0) 420 | 421 | #define LIST_INSERT_HEAD(head, elm, field) do { \ 422 | QMD_LIST_CHECK_HEAD((head), field); \ 423 | if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ 424 | LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ 425 | LIST_FIRST((head)) = (elm); \ 426 | (elm)->field.le_prev = &LIST_FIRST((head)); \ 427 | } while (0) 428 | 429 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 430 | 431 | #define LIST_REMOVE(elm, field) do { \ 432 | QMD_SAVELINK(oldnext, (elm)->field.le_next); \ 433 | QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ 434 | QMD_LIST_CHECK_NEXT(elm, field); \ 435 | QMD_LIST_CHECK_PREV(elm, field); \ 436 | if (LIST_NEXT((elm), field) != NULL) \ 437 | LIST_NEXT((elm), field)->field.le_prev = \ 438 | (elm)->field.le_prev; \ 439 | *(elm)->field.le_prev = LIST_NEXT((elm), field); \ 440 | TRASHIT(*oldnext); \ 441 | TRASHIT(*oldprev); \ 442 | } while (0) 443 | 444 | #define LIST_SWAP(head1, head2, type, field) do { \ 445 | struct type *swap_tmp = LIST_FIRST((head1)); \ 446 | LIST_FIRST((head1)) = LIST_FIRST((head2)); \ 447 | LIST_FIRST((head2)) = swap_tmp; \ 448 | if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ 449 | swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ 450 | if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ 451 | swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ 452 | } while (0) 453 | #endif 454 | 455 | /* 456 | * Tail queue declarations. 457 | */ 458 | #define TAILQ_HEAD(name, type) \ 459 | struct name { \ 460 | struct type *tqh_first; /* first element */ \ 461 | struct type **tqh_last; /* addr of last next element */ \ 462 | TRACEBUF \ 463 | } 464 | 465 | #define TAILQ_HEAD_INITIALIZER(head) \ 466 | { NULL, &(head).tqh_first } 467 | 468 | #define TAILQ_ENTRY(type) \ 469 | struct { \ 470 | struct type *tqe_next; /* next element */ \ 471 | struct type **tqe_prev; /* address of previous next element */ \ 472 | TRACEBUF \ 473 | } 474 | 475 | /* 476 | * Tail queue functions. 477 | */ 478 | #if (defined(_KERNEL) && defined(INVARIANTS)) 479 | #define QMD_TAILQ_CHECK_HEAD(head, field) do { \ 480 | if (!TAILQ_EMPTY(head) && \ 481 | TAILQ_FIRST((head))->field.tqe_prev != \ 482 | &TAILQ_FIRST((head))) \ 483 | panic("Bad tailq head %p first->prev != head", (head)); \ 484 | } while (0) 485 | 486 | #define QMD_TAILQ_CHECK_TAIL(head, field) do { \ 487 | if (*(head)->tqh_last != NULL) \ 488 | panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ 489 | } while (0) 490 | 491 | #define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ 492 | if (TAILQ_NEXT((elm), field) != NULL && \ 493 | TAILQ_NEXT((elm), field)->field.tqe_prev != \ 494 | &((elm)->field.tqe_next)) \ 495 | panic("Bad link elm %p next->prev != elm", (elm)); \ 496 | } while (0) 497 | 498 | #define QMD_TAILQ_CHECK_PREV(elm, field) do { \ 499 | if (*(elm)->field.tqe_prev != (elm)) \ 500 | panic("Bad link elm %p prev->next != elm", (elm)); \ 501 | } while (0) 502 | #else 503 | #define QMD_TAILQ_CHECK_HEAD(head, field) 504 | #define QMD_TAILQ_CHECK_TAIL(head, headname) 505 | #define QMD_TAILQ_CHECK_NEXT(elm, field) 506 | #define QMD_TAILQ_CHECK_PREV(elm, field) 507 | #endif /* (_KERNEL && INVARIANTS) */ 508 | 509 | #define TAILQ_CONCAT(head1, head2, field) do { \ 510 | if (!TAILQ_EMPTY(head2)) { \ 511 | *(head1)->tqh_last = (head2)->tqh_first; \ 512 | (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ 513 | (head1)->tqh_last = (head2)->tqh_last; \ 514 | TAILQ_INIT((head2)); \ 515 | QMD_TRACE_HEAD(head1); \ 516 | QMD_TRACE_HEAD(head2); \ 517 | } \ 518 | } while (0) 519 | 520 | #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) 521 | 522 | #define TAILQ_FIRST(head) ((head)->tqh_first) 523 | 524 | #define TAILQ_FOREACH(var, head, field) \ 525 | for ((var) = TAILQ_FIRST((head)); \ 526 | (var); \ 527 | (var) = TAILQ_NEXT((var), field)) 528 | 529 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 530 | for ((var) = TAILQ_FIRST((head)); \ 531 | (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ 532 | (var) = (tvar)) 533 | 534 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 535 | for ((var) = TAILQ_LAST((head), headname); \ 536 | (var); \ 537 | (var) = TAILQ_PREV((var), headname, field)) 538 | 539 | #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 540 | for ((var) = TAILQ_LAST((head), headname); \ 541 | (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ 542 | (var) = (tvar)) 543 | 544 | #define TAILQ_INIT(head) do { \ 545 | TAILQ_FIRST((head)) = NULL; \ 546 | (head)->tqh_last = &TAILQ_FIRST((head)); \ 547 | QMD_TRACE_HEAD(head); \ 548 | } while (0) 549 | 550 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 551 | QMD_TAILQ_CHECK_NEXT(listelm, field); \ 552 | if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ 553 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 554 | &TAILQ_NEXT((elm), field); \ 555 | else { \ 556 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 557 | QMD_TRACE_HEAD(head); \ 558 | } \ 559 | TAILQ_NEXT((listelm), field) = (elm); \ 560 | (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ 561 | QMD_TRACE_ELEM(&(elm)->field); \ 562 | QMD_TRACE_ELEM(&listelm->field); \ 563 | } while (0) 564 | 565 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 566 | QMD_TAILQ_CHECK_PREV(listelm, field); \ 567 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 568 | TAILQ_NEXT((elm), field) = (listelm); \ 569 | *(listelm)->field.tqe_prev = (elm); \ 570 | (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ 571 | QMD_TRACE_ELEM(&(elm)->field); \ 572 | QMD_TRACE_ELEM(&listelm->field); \ 573 | } while (0) 574 | 575 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ 576 | QMD_TAILQ_CHECK_HEAD(head, field); \ 577 | if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ 578 | TAILQ_FIRST((head))->field.tqe_prev = \ 579 | &TAILQ_NEXT((elm), field); \ 580 | else \ 581 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 582 | TAILQ_FIRST((head)) = (elm); \ 583 | (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ 584 | QMD_TRACE_HEAD(head); \ 585 | QMD_TRACE_ELEM(&(elm)->field); \ 586 | } while (0) 587 | 588 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ 589 | QMD_TAILQ_CHECK_TAIL(head, field); \ 590 | TAILQ_NEXT((elm), field) = NULL; \ 591 | (elm)->field.tqe_prev = (head)->tqh_last; \ 592 | *(head)->tqh_last = (elm); \ 593 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 594 | QMD_TRACE_HEAD(head); \ 595 | QMD_TRACE_ELEM(&(elm)->field); \ 596 | } while (0) 597 | 598 | #define TAILQ_LAST(head, headname) \ 599 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 600 | 601 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 602 | 603 | #define TAILQ_PREV(elm, headname, field) \ 604 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 605 | 606 | #define TAILQ_REMOVE(head, elm, field) do { \ 607 | QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ 608 | QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ 609 | QMD_TAILQ_CHECK_NEXT(elm, field); \ 610 | QMD_TAILQ_CHECK_PREV(elm, field); \ 611 | if ((TAILQ_NEXT((elm), field)) != NULL) \ 612 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 613 | (elm)->field.tqe_prev; \ 614 | else { \ 615 | (head)->tqh_last = (elm)->field.tqe_prev; \ 616 | QMD_TRACE_HEAD(head); \ 617 | } \ 618 | *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ 619 | TRASHIT(*oldnext); \ 620 | TRASHIT(*oldprev); \ 621 | QMD_TRACE_ELEM(&(elm)->field); \ 622 | } while (0) 623 | 624 | #define TAILQ_SWAP(head1, head2, type, field) do { \ 625 | struct type *swap_first = (head1)->tqh_first; \ 626 | struct type **swap_last = (head1)->tqh_last; \ 627 | (head1)->tqh_first = (head2)->tqh_first; \ 628 | (head1)->tqh_last = (head2)->tqh_last; \ 629 | (head2)->tqh_first = swap_first; \ 630 | (head2)->tqh_last = swap_last; \ 631 | if ((swap_first = (head1)->tqh_first) != NULL) \ 632 | swap_first->field.tqe_prev = &(head1)->tqh_first; \ 633 | else \ 634 | (head1)->tqh_last = &(head1)->tqh_first; \ 635 | if ((swap_first = (head2)->tqh_first) != NULL) \ 636 | swap_first->field.tqe_prev = &(head2)->tqh_first; \ 637 | else \ 638 | (head2)->tqh_last = &(head2)->tqh_first; \ 639 | } while (0) 640 | 641 | #endif // ED2KD_QUEUE_H 642 | --------------------------------------------------------------------------------