diff --git a/interface/future.cpp b/interface/future.cpp deleted file mode 100644 index 43ef0c1..0000000 --- a/interface/future.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -enum e_future_error -{ - FUTURE_ERROR_CANCEL - FUTURE_ERROR_EXCEPTION, - FUTURE_ERROR_TIMEOUT -} - -typedef void (*future_cb_success_t)(void * result, void * user); -typedef void (*future_cb_failed_t)(enum e_future_error err, const char * what, void * user); - -struct future -{ - void * user; - future_cb_success_t * cb_success; - future_cb_failed_t * cb_failed; -}; - -struct promise -{ - struct future f; - void * ctx; -} - -typedef void * promise_t; -void future_cancel(struct future * future) -{ - future->cancel(); -} - -void promise_success(struct promisc *, void * result); -void promise_fail(); - -struct future * XXXXXRpc() -{ - cancel = ___XXXXCancel() -}; \ No newline at end of file diff --git a/interface/future.h b/interface/future.h new file mode 100644 index 0000000..ba7bd56 --- /dev/null +++ b/interface/future.h @@ -0,0 +1,20 @@ + +enum e_future_error +{ + FUTURE_ERROR_CANCEL, + FUTURE_ERROR_EXCEPTION, + FUTURE_ERROR_TIMEOUT +}; +struct promise; +struct future; +typedef void (*future_success_cb)(void * result, void * user); +typedef void (*future_failed_cb)(enum e_future_error err, const char * what, void * user); +typedef void (*promise_ctx_destroy_cb)(struct promise* p); + +struct future* future_create(future_success_cb * cb_success, future_failed_cb * cb_failed, void * user); + +struct future* promise_to_future(struct promise* p); +struct promise* future_to_promise(struct future* f); + + + diff --git a/interface/http1.h b/interface/http1.h new file mode 100644 index 0000000..24eec98 --- /dev/null +++ b/interface/http1.h @@ -0,0 +1,421 @@ +#ifndef TFE_HTTP_H +#define TFE_HTTP_H + +#include +#include +#include +#include +#include + +#include "util.h" +#include "easylogging++.h" + +class HttpConnection; +class HttpSession; +class HttpRequest; +class HttpResponse; + +class Http +{ +public: + /* 回调函数调用 */ + using connection_cb_t = std::function; + + /* 回调函数设置 */ + void SetHttpConnectionNewCallback(connection_cb_t cb) + { connection_new_cb_ = cb; } + void SetHttpConnectionCloseCallback(connection_cb_t cb) + { connection_close_cb_ = cb; } + + /* 回调函数调用 */ + void TriggerConnectionNew(HttpConnection & ct) + { return connection_new_cb_(*this, ct); } + void TriggerConnectionClose(HttpConnection & ct) + { return connection_close_cb_(*this, ct); } + + static std::unique_ptr Factory(TfeConfigParser & cfg); + +private: + connection_cb_t connection_new_cb_; + connection_cb_t connection_close_cb_; + + /* 压缩降级选项 */ + std::vector accept_encoding_strip_list_; +}; + +class HttpHeaders +{ +public: + HttpHeaders() = default; + virtual ~HttpHeaders() = default; + + using str_field_t = std::string; + using str_value_t = std::string; + + virtual void Add(const str_field_t & str_field, const str_value_t & str_value) = 0; + virtual void Set(const str_field_t & str_field, const str_value_t & str_value) = 0; + virtual void Remove(const str_field_t & str_field) = 0; + + using for_each_cb_t = std::function; + + virtual bool ForEachHeader(for_each_cb_t cb) const = 0; + virtual bool ForEachValueOfHeader(const str_field_t & str_field, for_each_cb_t cb) const = 0; +}; + +class HttpRequest +{ +public: + HttpRequest() = default; + virtual ~HttpRequest() = default; + + /* URL读取、设置接口 */ + virtual const std::string & Url() const = 0; + virtual void Url(const std::string & url) = 0; + + virtual const std::string & Uri() const = 0; + virtual void Uri(const std::string & url) = 0; + + /* HttpHeaders */ + virtual HttpHeaders & Headers() = 0; + virtual const HttpHeaders & cHeaders() const = 0; + + /* Request Body */ + using body_content_t = std::vector; + using body_content_ptr_t = std::unique_ptr; + + /* Body读取、设置接口 */ + virtual const body_content_t * Body() const = 0; + virtual void Body(body_content_ptr_t body) = 0; + + /* Body的Stolen接口 */ + virtual body_content_ptr_t StolenBody() = 0; + + /* Bypass,标记本请求为直通 + * 当请求标记为直通时,转发数据,不再调用业务处理函数 */ + virtual bool Bypass() = 0; + virtual void Bypass(bool is_bypass) = 0; + + /* ReadOnly,标记本请求为只读。 + * 当一个请求为只读请求时,业务不应修改它的内容,底层处理Readonly的请求时,应直接转发不缓存 */ + virtual bool ReadOnly() = 0; + virtual void ReadOnly(bool is_readonly) = 0; + + /* Forward,标记本请求应被转发到对端 + * 当请求标记为不转发时,该请求被丢弃 */ + virtual bool Forward() = 0; + virtual void Forward(bool is_forward) = 0; + + /* 完整标记,该请求是否已经完整可用 */ + enum section_t + { + kSectionHeader, kSectionBody, kSecionMessage + }; + virtual bool Complete(section_t section) = 0; + + /* HTTP版本 */ + using version_t = std::tuple; + virtual version_t Version() = 0; + + /* 构建接口,根据结构化数据构建HTTP请求头部 */ + virtual void Construct() = 0; + + /* 调试接口 */ + virtual std::string DumpToString() = 0; +}; + +class HttpResponse +{ +public: + enum section_t + { + kSectionHeader, + kSectionBody, + kSectionMessage, + kSectionMax + }; + + enum section_state_t + { + kStateBegin, + kStateReading, + kStateComplete, + kStateStream, + kStateCalled, + kStateStolen + }; + +public: + HttpResponse() = default; + virtual ~HttpResponse() = default; + + /* 响应码 */ + virtual int ResponseCode() = 0; + virtual void ResponseCode(int cde) = 0; + + /* HttpHeaders */ + virtual HttpHeaders & Headers() = 0; + virtual const HttpHeaders & cHeaders() const = 0; + + /* Request Body */ + using body_content_t = std::vector; + using body_content_ptr_t = std::unique_ptr; + + /* Body读取、设置接口 */ + virtual const std::vector Body() const = 0; + virtual void Body(std::vector body) = 0; + virtual std::vector StolenBody() = 0; + + /* ReadOnly,标记本请求为只读。 + * 当一个请求为只读请求时,业务不应修改它的内容,底层处理Readonly的请求时,应直接转发不缓存 */ + virtual bool ReadOnly() = 0; + virtual void ReadOnly(bool is_readonly) = 0; + + /* Forward,标记本请求应被转发到对端 + * 当请求标记为不转发时,该请求被丢弃 */ + virtual bool Forward() = 0; + virtual void Forward(bool is_forward) = 0; + + /* Bypass,标记本应答为直通 + * 当应答标记为直通时,转发数据,不再调用业务处理函数 */ + virtual bool Bypass() = 0; + virtual void Bypass(bool is_bypass) = 0; + + virtual section_state_t SectionState(section_t section) = 0; + /* 构建指令,根据Object构建对应的Memory */ + virtual void Construct() = 0; + + /* 调试接口 */ + virtual std::string DumpToString() = 0; +}; + +class HttpConnection +{ +public: + HttpConnection() = default; + virtual ~HttpConnection() = default; + + using http_connection_cb_t = std::function; + + /* 回调函数设置 */ + virtual void SetSessionNewCallback(http_connection_cb_t cb) + { session_new_cb_ = cb; } + virtual void SetSessionCloseCallback(http_connection_cb_t cb) + { session_close_cb_ = cb; } + + /* 四元组信息获取 */ + virtual const struct sockaddr * SockAddrSource() const = 0; + virtual const struct sockaddr * SockAddrDest() const = 0; + + virtual void Write(std::unique_ptr http_session) = 0; + virtual void Close() = 0; + +protected: + http_connection_cb_t session_new_cb_{nullptr}; + http_connection_cb_t session_close_cb_{nullptr}; +}; + +class HttpSession +{ +public: + explicit HttpSession(HttpConnection & connection) : http_connection_(connection) {} + virtual ~HttpSession() { __dump_session(); } + + using http_session_cb_t = std::function; + + HttpRequest & request() const + { return *request_; } + + void request(std::unique_ptr req) + { request_ = std::move(req); } + + HttpResponse & response() const + { return *response_; } + + void response(std::unique_ptr rsp) + { response_ = std::move(rsp); } + + HttpConnection & connection() const + { return http_connection_; } + + virtual void SetRequestHeadAerCallback(http_session_cb_t cb) + { request_header_cb_ = cb; } + virtual void SetRequestBodyCallback(http_session_cb_t cb) + { request_body_cb_ = cb; } + virtual void SetResponseHeaderCallback(http_session_cb_t cb) + { response_header_cb_ = cb; } + virtual void SetResponseBodyCallback(http_session_cb_t cb) + { response_body_cb_ = cb; } + + virtual void CallRequestHeaderCallback() + { __call_session_callback(request_header_cb_, tag_request_header_cb_); } + virtual void CallRequestBodyCallback() + { __call_session_callback(request_body_cb_, tag_request_body_cb_); } + virtual void CallResponseHeaderCallback() + { __call_session_callback(response_header_cb_, tag_response_header_cb_); } + virtual void CallResponseBodyCallback() + { __call_session_callback(response_body_cb_, tag_response_body_cb_); } + + enum CallbackTag + { + kCallbackTagIgnore, + kCallbackTagNormal, + kCallBackTagOnlyOnce, + kCallbackTagRepeat + }; + + virtual void SetRequestHeaderTag(enum CallbackTag tag) + { tag_request_header_cb_ = tag; } + virtual void SetRequestBodyTag(enum CallbackTag tag) + { tag_request_body_cb_ = tag; } + virtual void SetResponseHeaderTag(enum CallbackTag tag) + { tag_response_header_cb_ = tag; } + virtual void SetResponseBodyTag(enum CallbackTag tag) + { tag_response_body_cb_ = tag; } + + /* 丢弃这一Session,不转发 */ + virtual void Drop() + { + /* Disable all callbacks */ + SetRequestHeaderTag(kCallbackTagIgnore); + SetRequestBodyTag(kCallbackTagIgnore); + SetResponseHeaderTag(kCallbackTagIgnore); + SetResponseBodyTag(kCallbackTagIgnore); + + /* Tag, please drop this session */ + need_to_drop_ = true; + } + + /* 直通,不再处理这一Session中的任何内容 */ + virtual void Bypass() + { + /* Disable all callbacks */ + SetRequestHeaderTag(kCallbackTagIgnore); + SetRequestBodyTag(kCallbackTagIgnore); + SetResponseHeaderTag(kCallbackTagIgnore); + SetResponseBodyTag(kCallbackTagIgnore); + + need_to_drop_ = false; + } + + virtual bool NeedToDrop() + { + return need_to_drop_; + } + + virtual bool NeedToBypass() + { + return (tag_request_header_cb_ == kCallbackTagIgnore && + tag_request_body_cb_ == kCallbackTagIgnore && + tag_response_header_cb_ == kCallbackTagIgnore && + tag_response_body_cb_ == kCallbackTagIgnore) && (!need_to_drop_); + } + +protected: + HttpConnection & http_connection_; + std::unique_ptr request_{nullptr}; + std::unique_ptr response_{nullptr}; + std::shared_ptr context_{nullptr}; + + /* Session Callbacks */ + http_session_cb_t request_header_cb_{nullptr}; + http_session_cb_t request_body_cb_{nullptr}; + http_session_cb_t response_header_cb_{nullptr}; + http_session_cb_t response_body_cb_{nullptr}; + + /* Call tag */ + enum CallbackTag tag_request_header_cb_{kCallBackTagOnlyOnce}; + enum CallbackTag tag_request_body_cb_{kCallbackTagNormal}; + enum CallbackTag tag_response_header_cb_{kCallBackTagOnlyOnce}; + enum CallbackTag tag_response_body_cb_{kCallbackTagNormal}; + + /* Drop tag */ + bool need_to_drop_{false}; + +private: + void __call_session_callback(const http_session_cb_t & cb, enum CallbackTag & cb_tag) + { + while (cb_tag != kCallbackTagIgnore) + { + cb(*this); + if (cb_tag == kCallbackTagNormal) break; + if (cb_tag == kCallBackTagOnlyOnce) cb_tag = kCallbackTagIgnore; + if (cb_tag == kCallbackTagRepeat) cb_tag = kCallBackTagOnlyOnce; + } + } + + void __dump_session() + { + auto str_src_addr = sockaddr_to_string(http_connection_.SockAddrSource()); + auto str_dst_addr = sockaddr_to_string(http_connection_.SockAddrDest()); + + auto str_request = request_->DumpToString(); + auto str_response = response_->DumpToString(); + + std::string status{}; + if (NeedToDrop()) status += "DROP"; + if (NeedToBypass()) status += "BYPASS"; + + CLOG(DEBUG, "HttpSessionTrace") << str_src_addr << str_dst_addr + << str_request << str_response << status; + } +}; + +std::unique_ptr HttpRequestFactory(int primary_version, int second_version); +std::unique_ptr HttpResponseFactory(short major_version, short minor_version); + +#include "pxyconn.h" + +class Http1Connection : public HttpConnection +{ +public: + Http1Connection(struct bufferevent * bev_downstream, struct bufferevent * bev_upstream, + const struct sockaddr_storage & source, const struct sockaddr_storage & dest) + : bev_downstream_(bev_downstream), bev_upstream_(bev_upstream), sockaddr_source_(source), sockaddr_dest_(dest) + {} + + ~Http1Connection() = default; + + void Close() override + { need_to_close_ = true; }; + + bool NeedToClose() + { return need_to_close_; } + + int on_connection_read_request(pxy_conn_ctx_t * conn_ctx, pxy_conn_desc_t * conn_this, + pxy_conn_desc_t * conn_other); + int on_connection_read_response(pxy_conn_ctx_t * conn_ctx, pxy_conn_desc_t * conn_this, + pxy_conn_desc_t * conn_other); + int on_connection_close(pxy_conn_ctx_t * conn_ctx, struct bufferevent * bev); + + void Write(std::unique_ptr http_session) override; + + const sockaddr * SockAddrSource() const override; + const sockaddr * SockAddrDest() const override; + +private: + enum direction + { + kDirectionRequest, + kDirectionResponse + }; + + using http_sessions_t = std::list>; + http_sessions_t http_sessions_{}; + + HttpSession * create_new_session(); + HttpSession * last_uncomplete_session(enum direction dir); + + void drop_last_session(); + void drop_first_session(); + bool need_to_close_{false}; + + /* connection info */ + struct sockaddr_storage sockaddr_source_; + struct sockaddr_storage sockaddr_dest_; + + /* upstream bev */ + struct bufferevent * bev_upstream_; + struct bufferevent * bev_downstream_; +}; + +#endif //TFE_HTTP_H diff --git a/interface/tfe_connection.h b/interface/tfe_connection.h deleted file mode 100644 index 2d32eff..0000000 --- a/interface/tfe_connection.h +++ /dev/null @@ -1,52 +0,0 @@ - -#define TFE_STRING_MAX 2048 -#define TFE_SYMBOL_MAX 64 -struct tfe_conn_ctx -{ - sockaddr_t dst; - struct pxy_conn_desc src; - struct pxy_conn_desc dst; - int cur_dir//1: c2s; 2:s2c - char* sni; -}; -struct tfe_conn_inner -{ - int a; - struct tfe_conn_ctx conn_desc; - void **proto_arg; -}; - - -//Return 1 for identify as its ttraffic; -//Return 0 for unknown traffic; -typedef int proto_pend_cb_t(const struct tfe_conn_ctx* c, struct evbuffer *data, void **pme); - -enum tfe_proto_action -{ - PROTO_ATCION_FORWARD, - PROTO_ACTION_DEFER, - PROTO_ACTION_STEAL, - PROTO_ACTION_PASSTHROUGH, - PROTO_ACTION_CLOSE -}; - -typedef tfe_proto_action proto_read_cb_t(const struct tfe_conn_ctx* ctx, struct evbuffer *data, void **pme); - -typedef void proto_close_cb_t(const struct tfe_conn_ctx* ctx, int ev, void **pme); - -//typedef int proto_onwrite_cb_t(struct tfe_conn_ctx*, struct evbuffer *data, void **pme); - -struct tfe_proto_module -{ - char symbol[TFE_SYMBOL_MAX]; - proto_pend_cb_t *on_pend; - proto_read_cb_t *on_read; - proto_close_cb_t *on_close; -// proto_onwrite_cb_t *onwrite; - -}; -int tfe_io_write(struct pxy_conn_desc* dest,int dir,struct evbuffer *data); - -int tfe_xxx_proto_init(struct tfe_proto_module*m); - - diff --git a/interface/tfe_http.h b/interface/tfe_http.h index d514d61..b3e34ed 100644 --- a/interface/tfe_http.h +++ b/interface/tfe_http.h @@ -138,7 +138,7 @@ enum tfe_bussiness_action //@param event: bit AND of EV_HTTP_** //@param body_frag: NULL for no body data. -typedef tfe_bussiness_action (*http_read_func)(const struct tfe_conn_ctx* ctx, const struct tfe_http_session* session, uint64_t event,struct evbuffer *body_frag, void **pme); +typedef tfe_bussiness_action (*http_read_func)(const struct tfe_stream* stream, const struct tfe_http_session* session, uint64_t event,struct evbuffer *body_frag, void **pme); struct tfe_http_half *tfe_http_request_create(int major_version,int method, const char* uri, const char* host); struct tfe_http_half *tfe_http_response_create(int major_version,int resp_code, struct evbuff* body); diff --git a/interface/tfe_stat.cpp b/interface/tfe_stat.cpp new file mode 100644 index 0000000..c9ef42f --- /dev/null +++ b/interface/tfe_stat.cpp @@ -0,0 +1,10 @@ + +void* tfe_stat_init(void* fs_handle) +{ + +} +void tfe_stat_flush(void* handle) +{ + +} + diff --git a/interface/tfe_stat.h b/interface/tfe_stat.h new file mode 100644 index 0000000..09b886d --- /dev/null +++ b/interface/tfe_stat.h @@ -0,0 +1,23 @@ +#include "tfe_types.h" +enum TFE_STAT_FIELD +{ + STREAM_NUM=0, + STREAM_OPEN, + STREAM_CLOSE, + STREAM_ERROR, + SSL_NUM, + SSL_OPEN, + SSL_CLOSE, + SSL_ERROR, + SNI_PEAK_FAIL, + IN_BYTES, + OUT_BYTES, + TFE_STAT_MAX +}; +struct tfe_stats +{ + long long value[TFE_STAT_MAX]; + void * fs_handle; + + int fs_ids[TFE_STAT_MAX]; +}; diff --git a/interface/tfe_stream.h b/interface/tfe_stream.h new file mode 100644 index 0000000..d9dd081 --- /dev/null +++ b/interface/tfe_stream.h @@ -0,0 +1,86 @@ + +#define TFE_STRING_MAX 2048 +#define TFE_SYMBOL_MAX 64 +enum tfe_session_proto +{ + SESSION_PROTO_PLAIN=0, + SESSION_PROTO_SSL, + SESSION_PROTO_QUIC, + SESSION_PROTO_SPDY +}; +enum tfe_app_proto +{ + APP_PROTO_HTTP1, + APP_PROTO_HTTP2, + APP_PROTO_WS, //websocket + APP_PROTO_QUIC //QUIC is a protocol that cross session layer and application layer. +}; +enum tfe_conn_dir +{ + CONN_DIR_DOWNSTREAM=0, //From client to proxy, aka client-side. + CONN_DIR_UPSTREAM //From proxy to server, aka server-side. +}; +enum tfe_conn_status +{ + CONN_STATUS_NONE, + CONN_STATUS_ESTABLISHED, + CONN_STATUS_CLOSED, +}; + +/* single dst or src socket bufferevent descriptor */ +struct tfe_conn +{ + struct layer_addr addr; + enum tfe_conn_status status; + struct bufferevent *bev; +} ; + +struct tfe_stream +{ + + struct tfe_conn upstream; + struct tfe_conn downstream; + void* application_pme; +}; + + +//Return 1 for identify as its traffic; +//Return 0 for unknown traffic; +typedef int proto_pend_cb_t(const struct tfe_stream* stream, struct evbuffer *data, void **pme); + +enum tfe_proto_action +{ + PROTO_ATCION_FORWARD, + PROTO_ACTION_DEFER, + PROTO_ACTION_STEAL, + PROTO_ACTION_PASSTHROUGH, + PROTO_ACTION_CLOSE +}; +enum tfe_proto_action_opt +{ + ACTION_OPT_DEFER_TIME_TV, //value is "struct timeval " which defines in , default: time defer is not enabled + ACTION_OPT_DEFER_BYTES, //value is size_t, default: defer entire evbufer + ACTION_OPT_FOWARD_BYTES, //value is size_t, default: forward entire evbufer + ACTION_OPT_CLOSE_DIR //value is enum tfe_conn_dir, default: close both side. +}; +int tfe_proto_action_set_opt(const struct tfe_stream* stream,enum tfe_proto_action_opt type, void* value, size_t size); +typedef tfe_proto_action proto_read_cb_t(const struct tfe_stream* stream, struct evbuffer *data, enum tfe_conn_dir dir, void **pme); + +typedef void proto_close_cb_t(const struct tfe_stream* stream, int ev, void **pme); + +//typedef int proto_onwrite_cb_t(struct tfe_stream*, struct evbuffer *data, void **pme); + +struct tfe_proto_module +{ + char symbol[TFE_SYMBOL_MAX]; + proto_pend_cb_t *on_pend; + proto_read_cb_t *on_read; + proto_close_cb_t *on_close; +// proto_onwrite_cb_t *onwrite; + +}; +int tfe_io_write(struct pxy_conn_desc* dest,int dir,struct evbuffer *data); + +int tfe_xxx_proto_init(struct tfe_proto_module*m); + + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..3502f9e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,32 @@ +VPATH=../inc/ +opt: OPTFLAGS = -O +export OPTFLAGS + +CC = gcc +CCC = g++ +CFLAGS = -Wall +CFLAGS += -g -fPIC +CFLAGS += $(OPTFLAGS) +MESAFramework =-lMESA_htable -lMESA_prof_load -lwiredcfg -lMESA_handle_logger +SYS_LIB =-L./ -lpthread -levent -levent_openssl -levent_pthreads +H_DIR = ./inc_internal/ +OBJS = tfe_main.o proxy.o io_module_kni.o +TARGET = tfe_3a + +.PHONY: all clean deps test opt + +all: $(TARGET) + +opt: + $(MAKE) all +.c.o: + $(CC) -c $(CFLAGS) -I $(H_DIR) $< + +.cpp.o: + $(CCC) -c $(CFLAGS) -I $(H_DIR) $< +clean: + rm -f $(TARGETSLIB) $(TARGETSO) *.o core core.* +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(CFLAGS) $^ $(SYS_LIB) $(MESAFramework) + cp $@ ../bin + diff --git a/src/attrib.h b/src/attrib.h new file mode 100644 index 0000000..046f732 --- /dev/null +++ b/src/attrib.h @@ -0,0 +1,60 @@ +/* + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ATTRIB_H +#define ATTRIB_H + +/* + * GCC attributes and built-ins for improved compile-time error checking + * and performance optimization. + * + * All of these are fully optional and are automatically disabled on non-GCC + * and non-LLVM/clang compilers. + */ + +/* + * Attributes. + * These serve to improve the compiler warnings or optimizations. + */ + +#if !defined(__GNUC__) && !defined(__clang__) +#define __attribute__(x) +#endif + +#define UNUSED __attribute__((unused)) +#define NORET __attribute__((noreturn)) +#define PRINTF(f,a) __attribute__((format(printf,(f),(a)))) +#define SCANF(f,a) __attribute__((format(scanf,(f),(a)))) +#define WUNRES __attribute__((warn_unused_result)) +#define MALLOC __attribute__((malloc)) WUNRES +#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) +#define PURE __attribute__((pure)) + +#endif /* !ATTRIB_H */ + +/* vim: set noet ft=c: */ diff --git a/src/cert.cpp b/src/cert.cpp new file mode 100644 index 0000000..22979fe --- /dev/null +++ b/src/cert.cpp @@ -0,0 +1,342 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cert.h" + +#include "ssl.h" + +#include + +/* + * Certificate, including private key and certificate chain. + */ + +cert_t * +cert_new(void) +{ + cert_t *c; + + if (!(c = (cert_t *)malloc(sizeof(cert_t)))) + return NULL; + memset(c, 0, sizeof(cert_t)); + if (pthread_mutex_init(&c->mutex, NULL)) { + free(c); + return NULL; + } + c->references = 1; + return c; +} + +/* + * Passed OpenSSL objects are owned by cert_t; refcount will not be + * incremented, stack will not be duplicated. + */ +cert_t * +cert_new3(EVP_PKEY *key, X509 *crt, STACK_OF(X509) *chain) +{ + cert_t *c; + + if (!(c = (cert_t *)malloc(sizeof(cert_t)))) + return NULL; + if (pthread_mutex_init(&c->mutex, NULL)) { + free(c); + return NULL; + } + c->key = key; + c->crt = crt; + c->chain = chain; + c->references = 1; + return c; +} + +/* + * Passed OpenSSL objects are copied by cert_t; crt/key refcount will be + * incremented, stack will be duplicated. + */ +cert_t * +cert_new3_copy(EVP_PKEY *key, X509 *crt, STACK_OF(X509) *chain) +{ + cert_t *c; + + if (!(c = (cert_t *)malloc(sizeof(cert_t)))) + return NULL; + if (pthread_mutex_init(&c->mutex, NULL)) { + free(c); + return NULL; + } + c->key = key; + ssl_key_refcount_inc(c->key); + c->crt = crt; + ssl_x509_refcount_inc(c->crt); + c->chain = sk_X509_dup(chain); + for (int i = 0; i < sk_X509_num(c->chain); i++) { + ssl_x509_refcount_inc(sk_X509_value(c->chain, i)); + } + c->references = 1; + return c; +} + +/* + * Load cert_t from file. + */ +cert_t * +cert_new_load(const char *filename) +{ + cert_t *c; + + if (!(c = (cert_t *)malloc(sizeof(cert_t)))) + return NULL; + memset(c, 0, sizeof(cert_t)); + if (pthread_mutex_init(&c->mutex, NULL)) { + free(c); + return NULL; + } + + if (ssl_x509chain_load(&c->crt, &c->chain, filename) == -1) { + free(c); + return NULL; + } + c->key = ssl_key_load(filename); + if (!c->key) { + X509_free(c->crt); + if (c->chain) { + sk_X509_pop_free(c->chain, X509_free); + } + free(c); + return NULL; + } + c->references = 1; + return c; +} + +/* + * Increment reference count. + */ +void +cert_refcount_inc(cert_t *c) +{ + pthread_mutex_lock(&c->mutex); + c->references++; + pthread_mutex_unlock(&c->mutex); +} + +/* + * Thread-safe setter functions; they copy the value (refcounts are inc'd). + */ +void +cert_set_key(cert_t *c, EVP_PKEY *key) +{ + pthread_mutex_lock(&c->mutex); + if (c->key) { + EVP_PKEY_free(c->key); + } + c->key = key; + if (c->key) { + ssl_key_refcount_inc(c->key); + } + pthread_mutex_unlock(&c->mutex); +} +void +cert_set_crt(cert_t *c, X509 *crt) +{ + pthread_mutex_lock(&c->mutex); + if (c->crt) { + X509_free(c->crt); + } + c->crt = crt; + if (c->crt) { + ssl_x509_refcount_inc(c->crt); + } + pthread_mutex_unlock(&c->mutex); +} +void +cert_set_chain(cert_t *c, STACK_OF(X509) *chain) +{ + pthread_mutex_lock(&c->mutex); + if (c->chain) { + sk_X509_pop_free(c->chain, X509_free); + } + if (chain) { + c->chain = sk_X509_dup(chain); + for (int i = 0; i < sk_X509_num(c->chain); i++) { + ssl_x509_refcount_inc(sk_X509_value(c->chain, i)); + } + } else { + c->chain = NULL; + } + pthread_mutex_unlock(&c->mutex); +} + +/* + * Free cert including internal objects. + */ +void +cert_free(cert_t *c) +{ + pthread_mutex_lock(&c->mutex); + c->references--; + if (c->references) { + pthread_mutex_unlock(&c->mutex); + return; + } + pthread_mutex_unlock(&c->mutex); + pthread_mutex_destroy(&c->mutex); + if (c->key) { + EVP_PKEY_free(c->key); + } + if (c->crt) { + X509_free(c->crt); + } + if (c->chain) { + sk_X509_pop_free(c->chain, X509_free); + } + free(c); +} + +struct cert_mgr* cert_manager_init(const char* profile) +{ + +} + +void cert_mgr_async_get(struct future* future, struct cert_mgr* mgr, int keyring_id, X509* origin_cert, struct event_base* evbase) +{ + X509* orig_cert=SSL_get_peer_certificate(origssl); + //todo: need implement + cert_t * cert = NULL; + if (opts->tgcrtdir) + { + if (ctx->sni) + { + cert = (cert_t *) cachemgr_tgcrt_get(ctx->sni); + if (!cert) + { + char * wildcarded = ssl_wildcardify(ctx->sni); + if (!wildcarded) + { + ctx->enomem = 1; + return NULL; + } + cert = (cert_t *) cachemgr_tgcrt_get(wildcarded); + free(wildcarded); + } + if (cert && OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Target cert by SNI\n"); + } + } + else if (ctx->origcrt) + { + char ** names = ssl_x509_names(ctx->origcrt); + for (char ** p = names; *p; p++) + { + if (!cert) + { + cert = (cert_t *) cachemgr_tgcrt_get(*p); + } + if (!cert) + { + char * wildcarded = ssl_wildcardify(*p); + if (!wildcarded) + { + ctx->enomem = 1; + } + else + { + cert = (cert_t *) (wildcarded); + free(wildcarded); + } + } + free(*p); + } + free(names); + if (ctx->enomem) + { + return NULL; + } + if (cert && OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Target cert by origcrt\n"); + } + } + + if (cert) + { + ctx->immutable_cert = 1; + } + } + + if (!cert && ctx->origcrt && ctx->opts->key) + { + cert = cert_new(); + cert->crt = (X509 *) cachemgr_fkcrt_get(ctx->origcrt); + + if (cert->crt) + { + if (OPTS_DEBUG(ctx->opts)) log_dbg_printf("Certificate cache: HIT\n"); + } + else + { + if (OPTS_DEBUG(ctx->opts)) log_dbg_printf("Certificate cache: MISS\n"); + cert->crt = ssl_x509_forge(ctx->opts->cacrt, + ctx->opts->cakey, + ctx->origcrt, + ctx->opts->key, + NULL, + ctx->opts->crlurl); + cachemgr_fkcrt_set(ctx->origcrt, cert->crt); + } + + cert_set_key(cert, ctx->opts->key); + cert_set_chain(cert, ctx->opts->chain); + ctx->generated_cert = 1; + } + + if ((WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) && ctx->origcrt) + { + ctx->origcrtfpr = ssl_x509_fingerprint(ctx->origcrt, 0); + if (!ctx->origcrtfpr) + ctx->enomem = 1; + } + + if ((WANT_CONNECT_LOG(ctx) || ctx->opts->certgen_writeall) && + cert && cert->crt) + { + ctx->usedcrtfpr = ssl_x509_fingerprint(cert->crt, 0); + if (!ctx->usedcrtfpr) + ctx->enomem = 1; + } + + return cert; +} +void cert_manager_free(cert_t * cert) +{ + cert_free(cert); + return; +} + +/* vim: set noet ft=c: */ diff --git a/src/cert.h b/src/cert.h new file mode 100644 index 0000000..b80e019 --- /dev/null +++ b/src/cert.h @@ -0,0 +1,62 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CERT_H +#define CERT_H + +#include "attrib.h" + +#include +#include + +typedef struct cert { + EVP_PKEY *key; + X509 *crt; + STACK_OF(X509) * chain; + pthread_mutex_t mutex; + size_t references; +} cert_t; +struct cert_mgr{}; +struct cert_mgr* cert_manager_init(const char* profile); +void cert_mgr_async_get(struct future* future, struct cert_mgr* mgr, int keyring_id, X509* origin_cert, struct event_base* evbase); + + + +cert_t * cert_new(void) MALLOC; +cert_t * cert_new_load(const char *) MALLOC; +cert_t * cert_new3(EVP_PKEY *, X509 *, STACK_OF(X509) *) MALLOC; +cert_t * cert_new3_copy(EVP_PKEY *, X509 *, STACK_OF(X509) *) MALLOC; +void cert_refcount_inc(cert_t *) NONNULL(1); +void cert_set_key(cert_t *, EVP_PKEY *) NONNULL(1); +void cert_set_crt(cert_t *, X509 *) NONNULL(1); +void cert_set_chain(cert_t *, STACK_OF(X509) *) NONNULL(1); +void cert_free(cert_t *) NONNULL(1); + +#endif /* !CERT_H */ + +/* vim: set noet ft=c: */ diff --git a/src/future.cpp b/src/future.cpp new file mode 100644 index 0000000..5d86584 --- /dev/null +++ b/src/future.cpp @@ -0,0 +1,70 @@ +#include "future.h" +struct promise +{ + struct future f; + void * ctx; + promise_ctx_destroy_cb* cb_ctx_destroy; +}; +struct future +{ + void * user; + future_success_cb * cb_success; + future_failed_cb * cb_failed; +}; + +inline struct future* promise_to_future(struct promise* p) +{ + return p->f; +} +inline struct promise* future_to_promise(struct future* f) +{ + return (struct promise*)f; +} +struct future* future_create(future_success_cb * cb_success, future_failed_cb * cb_failed, void * user) +{ + struct promise* p=ALLOC(struct promise, 1); + p->f.user=user; + p->f.cb_success=cb_success; + p->f.cb_failed=cb_failed; + return p->f; +} +void future_destroy(struct future * f) +{ + struct promise* promise=future_to_promise(f); + if(promise->cb_ctx_destroy!=NULL) + { + promise->cb_ctx_destroy(promise); + } + memset(promise,0,sizeof(struct promise)); + free(promise); + return; +} + +void promise_failed(struct promise* p, enum e_future_error error, const char* what) +{ + p->f.cb_failed(error, what,p->f.user); + return; +} +void promise_success(struct promise* p, void *result) +{ + p->f.cb_success(result, p->f.user); + return; +} +void promise_set_ctx(struct promise* p,void* ctx, promise_ctx_destroy_cb* cb) +{ + p->ctx=ctx; + p->cb_ctx_destroy=cb; + return; +} +void* promise_get_ctx(struct promise* p) +{ + return p->ctx; +} +void* promise_dettach_ctx(struct promise* p) +{ + void* ctx=p->ctx; + p->ctx=NULL; + p->cb_ctx_destroy=NULL; + return ctx; +} + diff --git a/src/inc_for_view/MESA_handle_logger.h b/src/inc_for_view/MESA_handle_logger.h new file mode 100644 index 0000000..c615b53 --- /dev/null +++ b/src/inc_for_view/MESA_handle_logger.h @@ -0,0 +1,68 @@ +#ifndef MESA_HANDLE__LOGGER_H +#define MESA_HANDLE__LOGGER_H + +/* + * runtime_log with handle, + * based on runtime_log. + * yang wei + * create time:2014-03-24 + * version:20140324 + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define RLOG_LV_DEBUG 10 +#define RLOG_LV_INFO 20 +#define RLOG_LV_FATAL 30 + + +#define MESA_HANDLE_RUNTIME_LOG(handle, lv, mod, fmt, args...) \ + MESA_handle_runtime_log((handle), (lv), (mod), "file %s, line %d, " fmt, \ + __FILE__, __LINE__, ##args) + +/* + * name: MESA_create_runtime_log_handle + * functionality: get runtime_log handle; + * params: + * file_path: path of log file, like "./log/runtime_log"; + * level: level of log; + * returns: + * not NULL, if succeeded; + * NULL, if file is not absolute path, or failed to create log file; + */ +void *MESA_create_runtime_log_handle(const char *file_path, int level); + +/* + * name: MESA_handle_runtime_log + * functionality: appends log message to runtime log file; + * params: + * handle:handle of runtime log, which is created by MESA_create_runtime_log_handle; + * level: log level, messages with level value smaller the global var + * "runtime_log_level" are ignored; + * module: name of loggin module; + * fmt: format string; + * returns: + * none; + */ +void MESA_handle_runtime_log(void *handle, int level, const char *module, const char *fmt, ...); + +/* + * name: MESA_destroy_runtime_log_handle + * functionality: release runtime log handle memory. + * params: + * handle: runtime log handle which is going to be released; + * returns: + * none; + */ +void MESA_destroy_runtime_log_handle(void *handle); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/src/inc_for_view/MESA_htable.h b/src/inc_for_view/MESA_htable.h new file mode 100644 index 0000000..ac7c2b2 --- /dev/null +++ b/src/inc_for_view/MESA_htable.h @@ -0,0 +1,419 @@ +#ifndef __MESA_HTABLE_H_ +#define __MESA_HTABLE_H_ +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include + +/* + * general purpose hash table implementation. + * + * xiang hong + * 2002-07-28 + *History: + * 2012-03-23 zhengchao add thread safe option and link expire feature; + * 2014-01-27 lijia add reentrant feature. + */ +#define MESA_HTABLE_VERSION_MACRO (20170104) +extern const unsigned int MESA_HTABLE_VERSION_INT; + +#define MESA_HASH_DEBUG (0) + +#define COMPLEX_KEY_SWITCH (1) + +#define ELIMINATE_TYPE_NUM (1) +#define ELIMINATE_TYPE_TIME (2) +#define ELIMINATE_TYPE_MANUAL (3) /* delete oldest item by manual */ + +typedef void * MESA_htable_handle; + + +#define HASH_MALLOC(_n_) malloc(_n_) +#define HASH_FREE(_p_) free(_p_) + + +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef uint +#define uint unsigned int +#endif + +/* eliminate algorithm */ +#define HASH_ELIMINATE_ALGO_FIFO (0) /* by default */ +#define HASH_ELIMINATE_ALGO_LRU (1) + +/* + * hash key compare function prototype, see hash_key_comp(). + * return value: + * 0:key1 and key2 are equal; + * other:key1 and key2 not equal. + */ +typedef int key_comp_fun_t(const uchar * key1, uint size1, const uchar * key2, uint size2); + +/* + * hash key->index computing function prototype, see hash_key2index(). + */ +typedef uint key2index_fun_t(const MESA_htable_handle table, const uchar * key, uint size); + +typedef void MESA_htable_data_free_cbfun_t(void *data); + +typedef int MESA_htable_expire_notify_cbfun_t(void *data, int eliminate_type); + +typedef uchar* MESA_htable_complex_key_dup_cbfun_t(const uchar *key, uint key_size); + +typedef void MESA_htable_complex_key_free_cbfun_t(uchar *key, uint key_size); + +typedef long hash_cb_fun_t(void *data, const uchar *key, uint size, void *user_arg); + +/* + * thread_safe: 0:create hash table without thread safe features; + * positive:the bigger number has more performance, less collide, but less timeout accuracy. + * max number is 1024. + * recursive: 0:can't recursive call MESA_htable_xxx series function + * 1:can recursive call MESA_htable_xxx series function. + * hash_slot_size: how big do you want the table to be, must be 2^N; + * max_elem_num: the maximum elements of the HASH-table,0 means infinite; + * key_comp: hash key compare function, use default function if NULL; + * suggest implement by yourself. + * key2index: hash key->index computing function, use default function if NULL; + * suggest use MESA_htable built-in function. + * data_free: release resources function; + * data_expire_with_condition: + * if expire_time > 0 and data_expire_with_condition != NULL, + * then call this function when an element expired, and give the reason by the 'type' + * if expire_time > 0 and data_expire_with_condition is NULL, + * eliminate the item immediately; + * args: + * data: pointer to attached data; + * type: item eliminate reason, ELIMINATE_TYPE_NUM or ELIMINATE_TYPE_TIME; + * return value of 'data_expire_with_condition': + * 1: the item can be eliminated; + * 0: the item can't be eliminated, renew the item. + * eliminate_type: the algorithm of elimanate a expired element, 0:FIFO; 1:LRU. + * expire_time: the element expire time in second, 0 means infinite. + */ +typedef struct{ + unsigned int thread_safe; + int recursive; + unsigned int hash_slot_size; + unsigned int max_elem_num; + int eliminate_type; + int expire_time; + key_comp_fun_t * key_comp; + key2index_fun_t * key2index; + void (* data_free)(void *data); + int (*data_expire_with_condition)(void *data, int eliminate_type); +#if COMPLEX_KEY_SWITCH + uchar* (*complex_key_dup)(const uchar *key, uint key_size); + void (* complex_key_free)(uchar *key, uint key_size); +#endif +}MESA_htable_create_args_t; + + +/* All of the following functions return value */ +typedef enum{ + MESA_HTABLE_RET_OK = 0, /* success */ + MESA_HTABLE_RET_COMMON_ERR = -1, /* generalundefined errors */ + MESA_HTABLE_RET_ARG_ERR = -2, /* invalid args */ + MESA_HTABLE_RET_NUM_FULL = -3, /* htable number full */ + MESA_HTABLE_RET_QEMPTY = -4, /* htable empty */ + MESA_HTABLE_RET_DUP_ITEM = -5, /* duplicate item */ + MESA_HTABLE_RET_NOT_FOUND = -6, /* not found item */ + MESA_HTABLE_RET_LEN_ERR = -7, /* length error */ + MESA_HTABLE_RET_CANT_GET_LOCK = -8, /* can't get lock in non-block mode */ + MESA_HTABLE_RET_GET_LOCK_TMOUT = -9, /* get lock timeout */ +}MESA_htable_errno_t; + +/* + * You should never use this API to create a hash table, use MESA_htable_born() instead. + * name: MESA_htable_create + * functionality: allocats memory for hash slots, and initialize hash structure; + * param: + * args: argments set; + * args_len: length of argment set; + * returns: + * NULL : error; + * Non-NULL : success; + */ +MESA_htable_handle MESA_htable_create(const MESA_htable_create_args_t *args, int args_struct_len); + +/* + * get total number of HASH element. +*/ +unsigned int MESA_htable_get_elem_num(const MESA_htable_handle table); + +/* + * name: MESA_htable_destroy + * functionality: cleans up hash structure, frees memory occupied; + * param: + * table: who is the victim; + * func: callback function to clean up data attached to hash items, has higher priority level than MESA_htable_data_free_cbfun_t in initialization. + + * returns: + * always returns 0; + */ +int MESA_htable_destroy(MESA_htable_handle table, void (* func)(void *)); + +/* + * name: MESA_htable_add + * functionality: adds item to table, call hash_expire() if elem_count gets + * bigger than threshold_hi, and adjust threshold; + * param: + * table: to which table do you want to add; + * key: what is the label; + * size: how long is the label; + * data: what data do you want to attach; + * returns: + * >0: success,return hash elems' linklist size; + * 0: success. + * <0: error, refer to MESA_htable_errno_t. + */ +int MESA_htable_add(MESA_htable_handle table, const uchar * key, uint size, const void *data); + +/* + TODO, + sturct hash_status{ + uint hlist_max; + uint hlist_max_slot_index; + uint cur_index_hlist_num; + uint hash_value; + }; + + MESA_htable_add_feedback(MESA_htable_handle table, const uchar * key, uint size, const void *data, sturct hash_status *hstat); + ڷHASHһЩؼϢ, + +*/ + +#if 0 +/* + * name: hash_add_with_expire + * functionality: adds item to table, than call hash_expire() on its list + * param: + * table: to which table do you want to add; + * key: what is the label; + * size: how long is the label; + * data: what data do you want to attach; + * returns: + * >0 success,return hash elems' linklist size + * -1, duplicates found and can't add this one; + * -2, memory failure; + */ +int MESA_hash_add_with_expire_v3(MESA_htable_inner_t * table, uchar * key, uint size, void * data); + +#endif + + +/* + * name: MESA_htable_del + * functionality: deletes item from table. + * param: + * table: from which table do you want to delete; + * key : what is the label; + * size : how long is the label; + * func : callback function to clean up data attached to hash items, + if this pointer is NULL will call "data_free" in MESA_hash_create(), + * returns: + * 0 : success; + * <0: error, refer to MESA_htable_errno_t. + */ +int MESA_htable_del(MESA_htable_handle table, const uchar * key, uint size, + void (* func)(void *)); +/* + TODO: + MESA_htable_del_with_hash(MESA_htable_handle table, const uchar * key, uint size, uint hash_value, + void (* func)(void *)); + ɾʱ֮ǰhash_value, һhash㿪, +*/ + + +/* + * name: MESA_htable_del_oldest_manual + * functionality: deletes oldest item from table. + * param: + * table: from which table do you want to delete; + * func : callback function to clean up data attached to hash items, + if this pointer is NULL will call "data_free" in MESA_hash_create(), + * batch_num: delete oldest items. + * returns: + * 0, do nothing ; + * >0, delete items; + */ +int MESA_htable_del_oldest_manual(MESA_htable_handle table, void (* func)(void *), int batch_num); + +/* + * name: MESA_htable_search + * functionality: selects item from table; + * param: + * table: from which table do you want to select; + * key : what is the label; + * size : how long is the label; + * + * return: + * not NULL :pointer to attached data; + * NULL :not found(thus be careful if you are attaching NULL data on purpose). + */ +void *MESA_htable_search(const MESA_htable_handle table, const uchar * key, uint size); + +/* + * name: MESA_htable_search_cb + * functionality: selects item from table, and then call 'cb', reentrant; + * in param: + * table: from which table do you want to select; + * key : what is the label; + * size : how long is the label; + * cb : call this function when found the attached data; + * arg : the argument of "cb" function. + * out param: + * cb_ret: the return value of the function "cb". + * return: + * not NULL :pointer to attached data; + * NULL :not found(thus be careful if you are attaching NULL data on purpose). + */ +void *MESA_htable_search_cb(const MESA_htable_handle table, const uchar * key, uint size, + hash_cb_fun_t *cb, void *arg, long *cb_ret); + +/* + * name: MESA_htable_iterate + * functionality: iterates each hash item; + * params: + * table: what table is to be iterated; + * func: what do you want to do to each attached data item; + * returns: + * 0: iterates all items; + * -1: error; + */ +int MESA_htable_iterate(MESA_htable_handle table, + void (* func)(const uchar * key, uint size, void * data, void *user), void * user); + + +/* + * name: MESA_htable_iterate_bytime + * functionality: iterates each hash item by your demand; + * note: + * if 'thread_safe' more than one, this function is not correct. + * params: + * table: what table is to be iterated; + * iterate_type: 1: newest item first; 2: oldest item first; + * iterate_cb: what do you want to do to each attached data item; + * return value of iterate_cb: + * refer to ITERATE_CB_RET_xxx; + * returns: + * 0: iterates all items; + * -1: uncomplete break. + * -2: error; + */ +#define ITERATE_CB_RET_CONTINUE_FLAG (0) /* default, like MESA_htable_iterate() */ +#define ITERATE_CB_RET_BREAK_FLAG (1<<1) /* break iterate, return from MESA_htable_iterate_bytime() immediately */ +#define ITERATE_CB_RET_DEL_FLAG (1<<2) /* del this item, like but faster than call MESA_htable_del() */ +#define ITERATE_CB_RET_REVERSE_FLAG (1<<3) /* if the item is newest item, it will become the oldest item, and vice versa */ +#define ITERATE_CB_RET_REMOVE_BUT_NOT_FREE (1<<4) /* only remove the item from Hash table, but don't free the attached data, be careful */ + +#define ITERATE_TYPE_NEWEST_FIRST (1) +#define ITERATE_TYPE_OLDEST_FIRST (2) +int MESA_htable_iterate_bytime(MESA_htable_handle table, int iterate_type, + int (*iterate_cb)(const uchar * key, uint size, void * data, void *user), void * user); + +/* + args: + print_switch: + 0: disable print message; + 1: enable print message; +*/ +void MESA_htable_print_crtl(MESA_htable_handle table, int print_switch); + + +/* + Create a htable handle and Alloc memory, and set default option, + but can't running before call MESA_htable_mature(). + + return value: + not NULL: success. + NULL : error. +*/ +MESA_htable_handle MESA_htable_born(void); + +/* + MESA_htable option definition. +*/ +enum MESA_htable_opt{ + MHO_THREAD_SAFE = 0, /* must be int, 1:create hash table with thread safe features, default is 0 */ + MHO_MUTEX_NUM, /* must be int, valid only if MHO_THREAD_SAFE is not zero, max value is 1024, defalut is 1. the bigger number has more performance and less mutex collide, but less timeout accuracy */ + MHO_HASH_SLOT_SIZE, /* must be unsigned int, default is 1048576. */ + MHO_HASH_MAX_ELEMENT_NUM, /* must be unsigned int, defalut is 0, means infinite */ + MHO_EXPIRE_TIME, /* must be int, defalut is 0, means infinite */ + MHO_ELIMIMINATE_TYPE, /* must be int, valid only if MHO_EXPIRE_TIME is not zero. HASH_ELIMINATE_ALGO_FIFO or HASH_ELIMINATE_ALGO_LRU, defalut HASH_ELIMINATE_ALGO_FIFO */ + MHO_CBFUN_KEY_COMPARE, /* must be key_comp_fun_t, hash key compare function, use default function if NULL */ + MHO_CBFUN_KEY_TO_INDEX, /* must be key2index_fun_t, hash key->index computing function, use default function if NULL */ + MHO_CBFUN_DATA_FREE, /* must be MESA_htable_data_free_cbfun_t, release resources function */ + /* data_expire_notify, must be MESA_htable_expire_notify_cbfun_t, + * if expire_time > 0 and data_expire_notify != NULL, + * then call this function when an element expired, and give the reason by the 'type' + * if expire_time > 0 and data_expire_notify is NULL, + * eliminate the item immediately; + * args: + * data: pointer to attached data; + * type: item eliminate reason, ELIMINATE_TYPE_NUM or ELIMINATE_TYPE_TIME; + * return value of 'data_expire_with_condition': + * 1: the item can be eliminated; + * 0: the item can't be eliminated, renew the item. + */ + MHO_CBFUN_DATA_EXPIRE_NOTIFY, + MHO_CBFUN_COMPLEX_KEY_DUP, /* must be MESA_htable_complex_key_dup_cbfun_t, if key store in a complex struct, caller must be implement this duplicate function. */ + MHO_CBFUN_COMPLEX_KEY_FREE, /* must be MESA_htable_complex_key_free_cbfun_t, if key store in a complex struct, caller must be implement this duplicate function. */ + MHO_AUTO_UPDATE_TIME, /* must be int, create a background thread used to update current_time instead of time(NULL). 1:enable; 0:disable; default value is 0; */ + MHO_SCREEN_PRINT_CTRL, /* must be int, 1:enable screen print; 0:disable screen print; default is 1. */ + MHO_HASH_LIST_COLLIDE_THRESHOLD, /* must be int, write log when hash collide number more than this, default is 100, 0 means infinite. */ + MHO_HASH_LOG_FILE, /* must be char * with EOF, default is "./hash_list_collide.log", opt_len is strlen(optval) */ + MHO_HASH_SEARCH_MAX_TIMES, /* must be int, max compare items in once MESA_htable_search() */ + MHO_HASH_SEARCH_AVG_TIMES, /* must be double, average compare items in all previous MESA_htable_search() */ + __MHO_MAX_VAL, /* caller can't use this definition, it's value maybe changed in next version!! */ +}; + + +/* + to set features of specified MESA_htable handle. + opt_type: option type, refer to enum MESA_htable_opt; + opt_val : option value, depend on opt type; + opt_len : opt_val size, depend on opt type; + + return value: + 0 :success; + <0:error; +*/ +int MESA_htable_set_opt(MESA_htable_handle table, enum MESA_htable_opt opt_type, void *opt_val, int opt_len); + +/* + to get features of specified MESA_htable handle. + opt_type: option type, refer to enum MESA_htable_opt; + opt_val : option value, depend on opt type; + opt_len : value-result argument, opt_val size, depend on opt type; + + return value: + 0 :success; + <0:error; +*/ +int MESA_htable_get_opt(MESA_htable_handle api_table, enum MESA_htable_opt opt_type, void *opt_val, int *opt_len); + +/* + Construct htable and ready to running. + + return value: + 0 : success; + <0: error. +*/ +int MESA_htable_mature(MESA_htable_handle table); + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIB_HASH_H_INCLUDED_ */ + + diff --git a/src/inc_for_view/MESA_prof_load.h b/src/inc_for_view/MESA_prof_load.h new file mode 100644 index 0000000..ecfc50f --- /dev/null +++ b/src/inc_for_view/MESA_prof_load.h @@ -0,0 +1,189 @@ +#ifndef SLIB_LOADPROF_H +#define SLIB_LOADPROF_H +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +// Read in specified integer value +// +// Return: +// 0 : success +// < 0 : error, val is set to default +int MESA_load_profile_int_def( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + int *val, // [OUT] returned value + const int dval); // [IN] default value + + + +// Read in specified integer value +// +// Return: +// 0 : success +// -1 : failed to get the key,may be have no thie section, key or the val which the key pointed error +// -2 : error ,the val if out of range +int MESA_load_profile_int_nodef( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + int *val); // [OUT] returned value + + + + +// Read in specified unsigned integer value +// +// Return: +// 0 : success +// < 0 : error, val is set to default +int MESA_load_profile_uint_def( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + unsigned int *val, // [OUT] returned value + const unsigned int dval); // [IN] default value + + + +// Read in specified unsigned integer value +// +// Return: +// 0 : success +// -1 : failed to get the key,may be have no thie section, key or the val which the key pointed error +// -2 : error ,the val if out of range +int MESA_load_profile_uint_nodef( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + unsigned int *val); // [OUT] returned value + + + +// Read in specified short integer value +// +// Return: +// 0 : success +// < 0 : error, val is set to default +int MESA_load_profile_short_def( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + short *val, // [OUT] returned value + const short dval); // [IN] default value + + + +// Read in specified short integer value +// +// Return: +// 0 : success +// -1 : failed to get the key,may be have no thie section, key or the val which the key pointed error +// -2 : error ,the val if out of range +int MESA_load_profile_short_nodef( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + short *val); // [OUT] returned value + + + +// Read in specified string value, +// if value string is too long to return, extra chars truncated. +// prefix/postfix space chars cutted, +// space chars: ' ', '\t' '\n' '\r' +// +// Return: +// >= 0 : length of val +// -1 : failed to get the key,may be have no thie section, key or the val which the key pointed error + +int MESA_load_profile_string_nodef( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + char *str, // [OUT] returned string + const size_t size); // [IN] buffer size(bytes) + + + +// Read in specified string value, +// if value string is too long to return, extra chars truncated. +// prefix/postfix space chars cutted, +// space chars: ' ', '\t' '\n' '\r' +// +// Return: +// >= 0 : length of val +// < 0 : error, str is set to default +int MESA_load_profile_string_def( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + char *str, // [OUT] returned string + const size_t size, // [IN] buffer size(bytes) + const char *dstr); // [IN] default string + +//read muti unint number from config file, e.g "1-3,5-9" +//return : +// >=0 : success,return the number of uint read from file successfully +// -1 : failed to get the key,may be have no thie section, key or the val which the key pointed error +// -2 : error,invalid uint +int MESA_load_profile_uint_range( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + const size_t size, // [IN] the number of unit memory out pointed + unsigned int *out); // [OUT] return ipset network bytes order + +//read ips from config file +//return : +// >=0 : success,return the number of ip read from file successfully +// -1 : failed to get the key,may be have no thie section, key or the val which the key pointed error +// -2 : error,invalid ip + +#if 0 +int MESA_load_profile_ipset( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + const size_t size, // [IN] the size of memory ips point,it must equel or greater than ip_num*sizeof(unsigned int) + unsigned int *ipset); // [OUT] return ipset network bytes order + +// Write the a int into specified position of the config file,the position is decided by section and key +// Return: +// >= 0 : success +// -1 : failed to write profile,maybe fopen failed, or malloc failed +int MESA_write_profile_int( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + const int value); // [IN] the integer need write + +// Write the a float into specified position of the config file,the position is decided by section and key +// Return: +// >= 0 : success +// -1 : failed to write profile,maybe fopen failed, or malloc failed +int MESA_write_profile_float( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + const float value); // [IN] the float need write + +// Write the a string into specified position of the config file,the position is decided by section and key +// Return: +// >= 0 : success +// -1 : failed to write profile,maybe fopen failed, or malloc failed +int MESA_write_profile_string( + const char *file, // [IN] initialization file path + const char *section, // [IN] section name in initialization file + const char *key, // [IN] keyword name in initialization file + const char *value); // [IN] the string need write +#endif +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef SLIB_LOADPROF_H */ diff --git a/src/inc_for_view/Maat_command.h b/src/inc_for_view/Maat_command.h new file mode 100644 index 0000000..1ccefe8 --- /dev/null +++ b/src/inc_for_view/Maat_command.h @@ -0,0 +1,155 @@ +#ifndef H_MAAT_COMMAND_H_INCLUDE +#define H_MAAT_COMMAND_H_INCLUDE +#ifndef __cplusplus +#error("This file should be compiled with C++ compiler") +#endif +#include "Maat_rule.h" +enum MAAT_OPERATION +{ + MAAT_OP_DEL=0, + MAAT_OP_ADD +}; + +enum MAAT_REGION_TYPE +{ + REGION_EXPR, + REGION_IP, + REGION_INTERVAL, + REGION_DIGEST, + REGION_SIMILARITY +}; +enum MAAT_EXPR_TYPE +{ + EXPR_TYPE_STRING=0, + EXPR_TYPE_AND, + EXPR_TYPE_REGEX, + EXPR_TYPE_OFFSET +}; +enum MAAT_MATCH_METHOD +{ + MATCH_METHOD_SUB=0, + MATCH_METHOD_RIGHT, + MATCH_METHOD_LEFT, + MATCH_METHOD_COMPLETE +}; + +enum MAAT_CASE_TYPE +{ + UNCASE_PLAIN=0, + CASE_HEXBIN, + CASE_PLAIN +}; +enum MAAT_ADDR_TYPE +{ + ADDR_TYPE_IPv4=4, + ADDR_TYPE_IPv6=6 +}; +enum MAAT_ADDR_DIRECTION +{ + ADDR_DIR_DOUBLE=0, + ADDR_DIR_SINGLE=1 +}; +struct Maat_rgn_str_t +{ + const char *keywords; + const char *district;// optional for expr_plus, otherwise set to NULL. + enum MAAT_EXPR_TYPE expr_type; + enum MAAT_MATCH_METHOD match_method; + enum MAAT_CASE_TYPE hex_bin; +}; +struct Maat_rgn_addr_t +{ + enum MAAT_ADDR_TYPE addr_type; + const char* src_ip; + const char* mask_src_ip; + const char* dst_ip; + const char* mask_dst_ip; + unsigned short src_port; + unsigned short mask_src_port; + unsigned short dst_port; + unsigned short mask_dst_port; + unsigned short protocol; + enum MAAT_ADDR_DIRECTION direction; +}; +struct Maat_rgn_intv_t +{ + unsigned int low_boundary; + unsigned int up_boundary; +}; +struct Maat_rgn_digest_t +{ + unsigned long long orgin_len; + const char* digest_string; + short confidence_degree; +}; +struct Maat_rgn_sim_t +{ + char* target; + short threshold;// 1~100 +}; +struct Maat_region_t +{ + const char* table_name; + int region_id; //If MAAT_OPT_CMD_AUTO_NUMBERING==1, maat will assigned one. Or users must appoint a unique number. + enum MAAT_REGION_TYPE region_type; + union + { + struct Maat_rgn_str_t expr_rule; + struct Maat_rgn_addr_t ip_rule; + struct Maat_rgn_intv_t interval_rule; + struct Maat_rgn_digest_t digest_rule; + struct Maat_rgn_sim_t similarity_rule; + }; +}; +struct Maat_group_t +{ + int region_num; + int group_id; //If MAAT_OPT_CMD_AUTO_NUMBERING==1, maat will assigned one. Or users must assign a unique number. + struct Maat_region_t *regions; +}; +struct Maat_cmd_t +{ + //This Struct MUST alloced by Maat_create_cmd(), then released by Maat_free_cmd(). + struct Maat_rule_t compile; // for MAAT_OP_DEL, only compile.config_id is necessary. + int group_num; // for MAAT_OP_DEL, set to 0. + int expire_after; //expired after $expire_after$ seconds, set to 0 for never timeout. + int label_id; //>0, to be indexed and quried by Maat_cmd_select; =0 not index + struct Maat_group_t* groups;// Add regions with Maat_add_region2cmd +}; +struct Maat_line_t +{ + const char* table_name; + const char* table_line; + int rule_id; // for MAAT_OP_DEL, only rule_id is necessary. + int label_id; + int expire_after; //expired after $timeout$ seconds, set to 0 for never timeout. +}; +struct Maat_cmd_t* Maat_create_cmd(const struct Maat_rule_t* rule, int group_num); +//input: which_group 0~group_num +//input: region can be freed after added. +void Maat_add_region2cmd(struct Maat_cmd_t* cmd,int which_group,const struct Maat_region_t* region); + +void Maat_free_cmd(struct Maat_cmd_t* cmd); +int Maat_format_cmd(struct Maat_cmd_t* cmd, char* buffer, int size); +// REGION_EXPR and REGION_SIMILARITY need to escape input string. +char* Maat_str_escape(char* dst,int size,const char*src); + +//Deletion failed due to not complete synchronize with Redis. +//To make sure the delete command is excecuted, user should try again after MAAT_OPT_SCANDIR_INTERVAL_MS ms. +// The following functions are NOT thread safe. +int Maat_cmd(Maat_feather_t feather,struct Maat_cmd_t* cmd,enum MAAT_OPERATION op); + +//pipeline model +int Maat_cmd_append(Maat_feather_t feather,struct Maat_cmd_t* cmd,enum MAAT_OPERATION op); +int Maat_cmd_commit(Maat_feather_t feather); + + +int Maat_cmd_set_group(Maat_feather_t feather, int group_id, const struct Maat_region_t* region, enum MAAT_OPERATION op); +int Maat_cmd_set_line(Maat_feather_t feather,const struct Maat_line_t* line_rule, enum MAAT_OPERATION op); +int Maat_cmd_set_lines(Maat_feather_t feather,const struct Maat_line_t** line_rule, int line_num ,enum MAAT_OPERATION op); +//Return the value of key after the increment. +//If the key does not exist, it is set to 0 before performing the operation. +long long Maat_cmd_incrby(Maat_feather_t feather,const char* key, int increment); +int Maat_cmd_select(Maat_feather_t feather, int label_id, int * output_ids, unsigned int size); +#endif + diff --git a/src/inc_for_view/Maat_rule.h b/src/inc_for_view/Maat_rule.h new file mode 100644 index 0000000..d9f1d49 --- /dev/null +++ b/src/inc_for_view/Maat_rule.h @@ -0,0 +1,228 @@ + +/* +*****************Maat Network Flow Rule Manage Framework******** +* Maat is the Goddess of truth and justice in ancient Egyptian concept. +* Her feather was the measure that determined whether the souls (considered +* to reside in the heart) of the departed would reach the paradise of afterlife +* successfully. +* Author: zhengchao@iie.ac.cn,MESA +* Version 2015-11-09 digest scan +* NOTE: MUST compile with G++ +* All right reserved by Institute of Infomation Engineering,Chinese Academic of Science 2014~2018 +********************************************************* +*/ +#ifndef H_MAAT_RULE_H_INCLUDE +#define H_MAAT_RULE_H_INCLUDE +#ifndef __cplusplus +#error("This file should be compiled with C++ compiler") +#endif +#include "stream.h" +enum MAAT_CHARSET +{ + CHARSET_NONE=0, + CHARSET_GBK, + CHARSET_BIG5, + CHARSET_UNICODE, + CHARSET_UTF8, // 4 + CHARSET_BIN, //5 + CHARSET_UNICODE_ASCII_ESC, // Unicode Escape format, prefix backslash-u hex, e.g. "\u627;" + CHARSET_UNICODE_ASCII_ALIGNED,//Unicode Escape format, prefix backslash-u with 4 bytes aligned, e.g. "\u0627" + CHARSET_UNICODE_NCR_DEC, //SGML Numeric character reference,decimal base, e.g. "ا" + CHARSET_UNICODE_NCR_HEX, //SGML Numeric character reference,hexdecimal base, e.g. "ا" + CHARSET_URL_ENCODE_GB2312, //URL encode with GB2312, e.g. the chinese word "china" was encoded to %D6%D0%B9%FA + CHARSET_URL_ENCODE_UTF8 //11, URL encode with UTF8,e.g. the chinese word "china" was encoded to %E4%B8%AD%E5%9B%BD +}; +enum MAAT_ACTION +{ + MAAT_ACTION_BLOCK=0, + MAAT_ACTION_MONIT, + MAAT_ACTION_WHITE +}; +enum MAAT_POS_TYPE +{ + MAAT_POSTYPE_EXPR=0, + MAAT_POSTYPE_REGEX +}; +typedef void* scan_status_t; +typedef void* stream_para_t; +typedef void* Maat_feather_t; + + +#define MAX_SERVICE_DEFINE_LEN 128 +struct Maat_rule_t +{ + int config_id; + int service_id; + char do_log; + char do_blacklist; + char action; + char resevered; + int serv_def_len; + char service_defined[MAX_SERVICE_DEFINE_LEN]; +}; +#define MAAT_RULE_UPDATE_TYPE_FULL 1 +#define MAAT_RULE_UPDATE_TYPE_INC 2 +typedef void Maat_start_callback_t(int update_type,void* u_para); +typedef void Maat_update_callback_t(int table_id,const char* table_line,void* u_para); +typedef void Maat_finish_callback_t(void* u_para); + + + + + +//--------------------HITTING DETAIL DESCRIPTION BEGIN + +#define MAAT_MAX_HIT_RULE_NUM 8 +#define MAAT_MAX_EXPR_ITEM_NUM 8 +#define MAAT_MAX_HIT_POS_NUM 8 +#define MAAT_MAX_REGEX_GROUP_NUM 8 + +//NOTE position buffer as hitting_regex_pos and hit_pos,are ONLY valid before next scan or Maat_stream_scan_string_end +struct regex_pos_t +{ + int group_num; + int hitting_regex_len; + const char* hitting_regex_pos; + int grouping_len[MAAT_MAX_REGEX_GROUP_NUM]; + const char* grouping_pos[MAAT_MAX_REGEX_GROUP_NUM]; +}; +struct str_pos_t +{ + int hit_len; + const char* hit_pos; +}; +struct sub_item_pos_t +{ + enum MAAT_POS_TYPE ruletype; + int hit_cnt; + union + { + struct regex_pos_t regex_pos[MAAT_MAX_HIT_POS_NUM]; + struct str_pos_t substr_pos[MAAT_MAX_HIT_POS_NUM]; + }; +}; + +struct Maat_region_pos_t +{ + + int region_id; + int sub_item_num; + struct sub_item_pos_t sub_item_pos[MAAT_MAX_EXPR_ITEM_NUM]; +}; + +struct Maat_hit_detail_t +{ + int config_id;//set <0 if half hit; + int hit_region_cnt; + struct Maat_region_pos_t region_pos[MAAT_MAX_HIT_RULE_NUM]; +}; +//--------------------HITTING DETAIL DESCRIPTION END + +//Abondon interface ,left for compatible. +Maat_feather_t Maat_summon_feather(int max_thread_num, + const char* table_info_path, + const char* ful_cfg_dir, + const char* inc_cfg_dir, + void*logger);//MESA_handle_logger +//Abondon interface ,left for compatible. +Maat_feather_t Maat_summon_feather_json(int max_thread_num, + const char* table_info_path, + const char* json_rule, + void* logger); + +Maat_feather_t Maat_feather(int max_thread_num,const char* table_info_path,void* logger); +int Maat_initiate_feather(Maat_feather_t feather); + +enum MAAT_INIT_OPT +{ + MAAT_OPT_SCANDIR_INTERVAL_MS=1, //VALUE is interger, SIZE=sizeof(int). DEFAULT:1,000 milliseconds. + MAAT_OPT_EFFECT_INVERVAL_MS, //VALUE is interger, SIZE=sizeof(int). DEFAULT:60,000 milliseconds. + MAAT_OPT_FULL_CFG_DIR, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_INC_CFG_DIR, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_JSON_FILE_PATH, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1.DEFAULT: no default. + MAAT_OPT_STAT_ON, //VALUE is NULL,SIZE is 0. MAAT_OPT_STAT_FILE_PATH must be set. Default: stat OFF. + MAAT_OPT_PERF_ON, //VALUE is NULL,SIZE is 0. MAAT_OPT_STAT_FILE_PATH must be set. Default: stat OFF. + MAAT_OPT_STAT_FILE_PATH, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: no default. + MAAT_OPT_SCAN_DETAIL, //VALUE is interger *, SIZE=sizeof(int). 0: not return any detail;1: return hit pos, not include regex grouping; + // 2 return hit pos and regex grouping pos;DEFAULT:0 + MAAT_OPT_INSTANCE_NAME, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1, no more than 11 bytes.DEFAULT: MAAT_$tableinfo_path$. + MAAT_OPT_DECRYPT_KEY, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. No DEFAULT. + MAAT_OPT_REDIS_IP, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. No DEFAULT. + MAAT_OPT_REDIS_PORT, //VALUE is a unsigned short or a signed int, host order, SIZE= sizeof(unsigned short) or sizeof(int). No DEFAULT. + MAAT_OPT_REDIS_INDEX, //VALUE is interger *, 0~15, SIZE=sizeof(int). DEFAULT: 0. + MAAT_OPT_CMD_AUTO_NUMBERING, //VALUE is interger *, 1 or 0, SIZE=sizeof(int). DEFAULT: 1. + MAAT_OPT_DEFERRED_LOAD //VALUE is NULL,SIZE is 0. Default: Deffered initialization OFF. +}; +//return -1 if failed, return 0 on success; +int Maat_set_feather_opt(Maat_feather_t feather,enum MAAT_INIT_OPT type,const void* value,int size); +void Maat_burn_feather(Maat_feather_t feather); + +//return table_id(>=0) if success,otherwise return -1; +int Maat_table_register(Maat_feather_t feather,const char* table_name); +//return 1 if success,otherwise return -1 incase invalid table_id or registed function number exceed 32; +int Maat_table_callback_register(Maat_feather_t feather,short table_id, + Maat_start_callback_t *start,//MAAT_RULE_UPDATE_TYPE_*,u_para + Maat_update_callback_t *update,//table line ,u_para + Maat_finish_callback_t *finish,//u_para + void* u_para); + +enum MAAT_SCAN_OPT +{ + MAAT_SET_SCAN_DISTRICT=1, //VALUE is a const char*,SIZE= strlen(string).DEFAULT: no default. + MAAT_SET_SCAN_LAST_REGION //VALUE is NULL, SIZE=0. This option indicates that the follow scan is the last region of current scan cobination. +}; +//return 0 if success, return -1 when failed; +int Maat_set_scan_status(Maat_feather_t feather,scan_status_t* mid,enum MAAT_SCAN_OPT type,const void* value,int size); + +//Return hit rule number, return -1 when error occurs,return -2 when hit current region +//mid MUST set to NULL before fist call +int Maat_scan_intval(Maat_feather_t feather,int table_id + ,unsigned int intval + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t *mid,int thread_num); +int Maat_scan_addr(Maat_feather_t feather,int table_id + ,struct ipaddr* addr + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t *mid,int thread_num); +int Maat_scan_proto_addr(Maat_feather_t feather,int table_id + ,struct ipaddr* addr,unsigned short int proto + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t *mid,int thread_num); +int Maat_full_scan_string(Maat_feather_t feather,int table_id + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int* found_pos,int rule_num + ,scan_status_t* mid,int thread_num); +//hite_detail could be NULL if unconcern +int Maat_full_scan_string_detail(Maat_feather_t feather,int table_id + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int rule_num,struct Maat_hit_detail_t *hit_detail,int detail_num + ,int* detail_ret,scan_status_t* mid,int thread_num); + +stream_para_t Maat_stream_scan_string_start(Maat_feather_t feather,int table_id,int thread_num); +int Maat_stream_scan_string(stream_para_t* stream_para + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int* found_pos,int rule_num + ,scan_status_t* mid); +//hited_detail could be NULL if unconcern +int Maat_stream_scan_string_detail(stream_para_t* stream_para + ,enum MAAT_CHARSET charset,const char* data,int data_len + ,struct Maat_rule_t*result,int rule_num,struct Maat_hit_detail_t *hit_detail,int detail_num + ,int* detail_ret,scan_status_t* mid); +void Maat_stream_scan_string_end(stream_para_t* stream_para); + +stream_para_t Maat_stream_scan_digest_start(Maat_feather_t feather,int table_id,unsigned long long total_len,int thread_num); +int Maat_stream_scan_digest(stream_para_t* stream_para + ,const char* data,int data_len,unsigned long long offset + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t* mid); +void Maat_stream_scan_digest_end(stream_para_t* stream_para); + +int Maat_similar_scan_string(Maat_feather_t feather,int table_id + ,const char* data,int data_len + ,struct Maat_rule_t*result,int rule_num + ,scan_status_t* mid,int thread_num); + +void Maat_clean_status(scan_status_t* mid); + +#endif // H_MAAT_RULE_H_INCLUDE + diff --git a/src/inc_for_view/event2/buffer.h b/src/inc_for_view/event2/buffer.h new file mode 100644 index 0000000..468588b --- /dev/null +++ b/src/inc_for_view/event2/buffer.h @@ -0,0 +1,1076 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFER_H_INCLUDED_ +#define EVENT2_BUFFER_H_INCLUDED_ + +/** @file event2/buffer.h + + Functions for buffering data for network sending or receiving. + + An evbuffer can be used for preparing data before sending it to + the network or conversely for reading data from the network. + Evbuffers try to avoid memory copies as much as possible. As a + result, evbuffers can be used to pass data around without actually + incurring the overhead of copying the data. + + A new evbuffer can be allocated with evbuffer_new(), and can be + freed with evbuffer_free(). Most users will be using evbuffers via + the bufferevent interface. To access a bufferevent's evbuffers, use + bufferevent_get_input() and bufferevent_get_output(). + + There are several guidelines for using evbuffers. + + - if you already know how much data you are going to add as a result + of calling evbuffer_add() multiple times, it makes sense to use + evbuffer_expand() first to make sure that enough memory is allocated + before hand. + + - evbuffer_add_buffer() adds the contents of one buffer to the other + without incurring any unnecessary memory copies. + + - evbuffer_add() and evbuffer_add_buffer() do not mix very well: + if you use them, you will wind up with fragmented memory in your + buffer. + + - For high-performance code, you may want to avoid copying data into and out + of buffers. You can skip the copy step by using + evbuffer_reserve_space()/evbuffer_commit_space() when writing into a + buffer, and evbuffer_peek() when reading. + + In Libevent 2.0 and later, evbuffers are represented using a linked + list of memory chunks, with pointers to the first and last chunk in + the chain. + + As the contents of an evbuffer can be stored in multiple different + memory blocks, it cannot be accessed directly. Instead, evbuffer_pullup() + can be used to force a specified number of bytes to be contiguous. This + will cause memory reallocation and memory copies if the data is split + across multiple blocks. It is more efficient, however, to use + evbuffer_peek() if you don't require that the memory to be contiguous. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_UIO_H +#include +#endif +#include + +/** + An evbuffer is an opaque data type for efficiently buffering data to be + sent or received on the network. + + @see event2/event.h for more information +*/ +struct evbuffer +#ifdef EVENT_IN_DOXYGEN_ +{} +#endif +; + +/** + Pointer to a position within an evbuffer. + + Used when repeatedly searching through a buffer. Calling any function + that modifies or re-packs the buffer contents may invalidate all + evbuffer_ptrs for that buffer. Do not modify or contruct these values + except with evbuffer_ptr_set. + + An evbuffer_ptr can represent any position from the start of a buffer up + to a position immediately after the end of a buffer. + + @see evbuffer_ptr_set() + */ +struct evbuffer_ptr { + ev_ssize_t pos; + + /* Do not alter or rely on the values of fields: they are for internal + * use */ + struct { + void *chain; + size_t pos_in_chain; + } internal_; +}; + +/** Describes a single extent of memory inside an evbuffer. Used for + direct-access functions. + + @see evbuffer_reserve_space, evbuffer_commit_space, evbuffer_peek + */ +#ifdef EVENT__HAVE_SYS_UIO_H +#define evbuffer_iovec iovec +/* Internal use -- defined only if we are using the native struct iovec */ +#define EVBUFFER_IOVEC_IS_NATIVE_ +#else +struct evbuffer_iovec { + /** The start of the extent of memory. */ + void *iov_base; + /** The length of the extent of memory. */ + size_t iov_len; +}; +#endif + +/** + Allocate storage for a new evbuffer. + + @return a pointer to a newly allocated evbuffer struct, or NULL if an error + occurred + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evbuffer_new(void); +/** + Deallocate storage for an evbuffer. + + @param buf pointer to the evbuffer to be freed + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_free(struct evbuffer *buf); + +/** + Enable locking on an evbuffer so that it can safely be used by multiple + threads at the same time. + + NOTE: when locking is enabled, the lock will be held when callbacks are + invoked. This could result in deadlock if you aren't careful. Plan + accordingly! + + @param buf An evbuffer to make lockable. + @param lock A lock object, or NULL if we should allocate our own. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_enable_locking(struct evbuffer *buf, void *lock); + +/** + Acquire the lock on an evbuffer. Has no effect if locking was not enabled + with evbuffer_enable_locking. +*/ +EVENT2_EXPORT_SYMBOL +void evbuffer_lock(struct evbuffer *buf); + +/** + Release the lock on an evbuffer. Has no effect if locking was not enabled + with evbuffer_enable_locking. +*/ +EVENT2_EXPORT_SYMBOL +void evbuffer_unlock(struct evbuffer *buf); + + +/** If this flag is set, then we will not use evbuffer_peek(), + * evbuffer_remove(), evbuffer_remove_buffer(), and so on to read bytes + * from this buffer: we'll only take bytes out of this buffer by + * writing them to the network (as with evbuffer_write_atmost), by + * removing them without observing them (as with evbuffer_drain), + * or by copying them all out at once (as with evbuffer_add_buffer). + * + * Using this option allows the implementation to use sendfile-based + * operations for evbuffer_add_file(); see that function for more + * information. + * + * This flag is on by default for bufferevents that can take advantage + * of it; you should never actually need to set it on a bufferevent's + * output buffer. + */ +#define EVBUFFER_FLAG_DRAINS_TO_FD 1 + +/** Change the flags that are set for an evbuffer by adding more. + * + * @param buffer the evbuffer that the callback is watching. + * @param cb the callback whose status we want to change. + * @param flags One or more EVBUFFER_FLAG_* options + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_set_flags(struct evbuffer *buf, ev_uint64_t flags); +/** Change the flags that are set for an evbuffer by removing some. + * + * @param buffer the evbuffer that the callback is watching. + * @param cb the callback whose status we want to change. + * @param flags One or more EVBUFFER_FLAG_* options + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_clear_flags(struct evbuffer *buf, ev_uint64_t flags); + +/** + Returns the total number of bytes stored in the evbuffer + + @param buf pointer to the evbuffer + @return the number of bytes stored in the evbuffer +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_get_length(const struct evbuffer *buf); + +/** + Returns the number of contiguous available bytes in the first buffer chain. + + This is useful when processing data that might be split into multiple + chains, or that might all be in the first chain. Calls to + evbuffer_pullup() that cause reallocation and copying of data can thus be + avoided. + + @param buf pointer to the evbuffer + @return 0 if no data is available, otherwise the number of available bytes + in the first buffer chain. +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_get_contiguous_space(const struct evbuffer *buf); + +/** + Expands the available space in an evbuffer. + + Expands the available space in the evbuffer to at least datlen, so that + appending datlen additional bytes will not require any new allocations. + + @param buf the evbuffer to be expanded + @param datlen the new minimum length requirement + @return 0 if successful, or -1 if an error occurred +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_expand(struct evbuffer *buf, size_t datlen); + +/** + Reserves space in the last chain or chains of an evbuffer. + + Makes space available in the last chain or chains of an evbuffer that can + be arbitrarily written to by a user. The space does not become + available for reading until it has been committed with + evbuffer_commit_space(). + + The space is made available as one or more extents, represented by + an initial pointer and a length. You can force the memory to be + available as only one extent. Allowing more extents, however, makes the + function more efficient. + + Multiple subsequent calls to this function will make the same space + available until evbuffer_commit_space() has been called. + + It is an error to do anything that moves around the buffer's internal + memory structures before committing the space. + + NOTE: The code currently does not ever use more than two extents. + This may change in future versions. + + @param buf the evbuffer in which to reserve space. + @param size how much space to make available, at minimum. The + total length of the extents may be greater than the requested + length. + @param vec an array of one or more evbuffer_iovec structures to + hold pointers to the reserved extents of memory. + @param n_vec The length of the vec array. Must be at least 1; + 2 is more efficient. + @return the number of provided extents, or -1 on error. + @see evbuffer_commit_space() +*/ +EVENT2_EXPORT_SYMBOL +int +evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size, + struct evbuffer_iovec *vec, int n_vec); + +/** + Commits previously reserved space. + + Commits some of the space previously reserved with + evbuffer_reserve_space(). It then becomes available for reading. + + This function may return an error if the pointer in the extents do + not match those returned from evbuffer_reserve_space, or if data + has been added to the buffer since the space was reserved. + + If you want to commit less data than you got reserved space for, + modify the iov_len pointer of the appropriate extent to a smaller + value. Note that you may have received more space than you + requested if it was available! + + @param buf the evbuffer in which to reserve space. + @param vec one or two extents returned by evbuffer_reserve_space. + @param n_vecs the number of extents. + @return 0 on success, -1 on error + @see evbuffer_reserve_space() +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_commit_space(struct evbuffer *buf, + struct evbuffer_iovec *vec, int n_vecs); + +/** + Append data to the end of an evbuffer. + + @param buf the evbuffer to be appended to + @param data pointer to the beginning of the data buffer + @param datlen the number of bytes to be copied from the data buffer + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen); + + +/** + Read data from an evbuffer and drain the bytes read. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param data the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen); + +/** + Read data from an evbuffer, and leave the buffer unchanged. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param data_out the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t evbuffer_copyout(struct evbuffer *buf, void *data_out, size_t datlen); + +/** + Read data from the middle of an evbuffer, and leave the buffer unchanged. + + If more bytes are requested than are available in the evbuffer, we + only extract as many bytes as were available. + + @param buf the evbuffer to be read from + @param pos the position to start reading from + @param data_out the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read, or -1 if we can't drain the buffer. + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t evbuffer_copyout_from(struct evbuffer *buf, const struct evbuffer_ptr *pos, void *data_out, size_t datlen); + +/** + Read data from an evbuffer into another evbuffer, draining + the bytes from the source buffer. This function avoids copy + operations to the extent possible. + + If more bytes are requested than are available in src, the src + buffer is drained completely. + + @param src the evbuffer to be read from + @param dst the destination evbuffer to store the result into + @param datlen the maximum numbers of bytes to transfer + @return the number of bytes read + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst, + size_t datlen); + +/** Used to tell evbuffer_readln what kind of line-ending to look for. + */ +enum evbuffer_eol_style { + /** Any sequence of CR and LF characters is acceptable as an + * EOL. + * + * Note that this style can produce ambiguous results: the + * sequence "CRLF" will be treated as a single EOL if it is + * all in the buffer at once, but if you first read a CR from + * the network and later read an LF from the network, it will + * be treated as two EOLs. + */ + EVBUFFER_EOL_ANY, + /** An EOL is an LF, optionally preceded by a CR. This style is + * most useful for implementing text-based internet protocols. */ + EVBUFFER_EOL_CRLF, + /** An EOL is a CR followed by an LF. */ + EVBUFFER_EOL_CRLF_STRICT, + /** An EOL is a LF. */ + EVBUFFER_EOL_LF, + /** An EOL is a NUL character (that is, a single byte with value 0) */ + EVBUFFER_EOL_NUL +}; + +/** + * Read a single line from an evbuffer. + * + * Reads a line terminated by an EOL as determined by the evbuffer_eol_style + * argument. Returns a newly allocated nul-terminated string; the caller must + * free the returned value. The EOL is not included in the returned string. + * + * @param buffer the evbuffer to read from + * @param n_read_out if non-NULL, points to a size_t that is set to the + * number of characters in the returned string. This is useful for + * strings that can contain NUL characters. + * @param eol_style the style of line-ending to use. + * @return pointer to a single line, or NULL if an error occurred + */ +EVENT2_EXPORT_SYMBOL +char *evbuffer_readln(struct evbuffer *buffer, size_t *n_read_out, + enum evbuffer_eol_style eol_style); + +/** + Move all data from one evbuffer into another evbuffer. + + This is a destructive add. The data from one buffer moves into + the other buffer. However, no unnecessary memory copies occur. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + + @see evbuffer_remove_buffer() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf); + +/** + Copy data from one evbuffer into another evbuffer. + + This is a non-destructive add. The data from one buffer is copied + into the other buffer. However, no unnecessary memory copies occur. + + Note that buffers already containing buffer references can't be added + to other buffers. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_buffer_reference(struct evbuffer *outbuf, + struct evbuffer *inbuf); + +/** + A cleanup function for a piece of memory added to an evbuffer by + reference. + + @see evbuffer_add_reference() + */ +typedef void (*evbuffer_ref_cleanup_cb)(const void *data, + size_t datalen, void *extra); + +/** + Reference memory into an evbuffer without copying. + + The memory needs to remain valid until all the added data has been + read. This function keeps just a reference to the memory without + actually incurring the overhead of a copy. + + @param outbuf the output buffer + @param data the memory to reference + @param datlen how memory to reference + @param cleanupfn callback to be invoked when the memory is no longer + referenced by this evbuffer. + @param cleanupfn_arg optional argument to the cleanup callback + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_reference(struct evbuffer *outbuf, + const void *data, size_t datlen, + evbuffer_ref_cleanup_cb cleanupfn, void *cleanupfn_arg); + +/** + Copy data from a file into the evbuffer for writing to a socket. + + This function avoids unnecessary data copies between userland and + kernel. If sendfile is available and the EVBUFFER_FLAG_DRAINS_TO_FD + flag is set, it uses those functions. Otherwise, it tries to use + mmap (or CreateFileMapping on Windows). + + The function owns the resulting file descriptor and will close it + when finished transferring data. + + The results of using evbuffer_remove() or evbuffer_pullup() on + evbuffers whose data was added using this function are undefined. + + For more fine-grained control, use evbuffer_add_file_segment. + + @param outbuf the output buffer + @param fd the file descriptor + @param offset the offset from which to read data + @param length how much data to read, or -1 to read as much as possible. + (-1 requires that 'fd' support fstat.) + @return 0 if successful, or -1 if an error occurred +*/ + +EVENT2_EXPORT_SYMBOL +int evbuffer_add_file(struct evbuffer *outbuf, int fd, ev_off_t offset, + ev_off_t length); + +/** + An evbuffer_file_segment holds a reference to a range of a file -- + possibly the whole file! -- for use in writing from an evbuffer to a + socket. It could be implemented with mmap, sendfile, splice, or (if all + else fails) by just pulling all the data into RAM. A single + evbuffer_file_segment can be added more than once, and to more than one + evbuffer. + */ +struct evbuffer_file_segment; + +/** + Flag for creating evbuffer_file_segment: If this flag is set, then when + the evbuffer_file_segment is freed and no longer in use by any + evbuffer, the underlying fd is closed. + */ +#define EVBUF_FS_CLOSE_ON_FREE 0x01 +/** + Flag for creating evbuffer_file_segment: Disable memory-map based + implementations. + */ +#define EVBUF_FS_DISABLE_MMAP 0x02 +/** + Flag for creating evbuffer_file_segment: Disable direct fd-to-fd + implementations (including sendfile and splice). + + You might want to use this option if data needs to be taken from the + evbuffer by any means other than writing it to the network: the sendfile + backend is fast, but it only works for sending files directly to the + network. + */ +#define EVBUF_FS_DISABLE_SENDFILE 0x04 +/** + Flag for creating evbuffer_file_segment: Do not allocate a lock for this + segment. If this option is set, then neither the segment nor any + evbuffer it is added to may ever be accessed from more than one thread + at a time. + */ +#define EVBUF_FS_DISABLE_LOCKING 0x08 + +/** + A cleanup function for a evbuffer_file_segment added to an evbuffer + for reference. + */ +typedef void (*evbuffer_file_segment_cleanup_cb)( + struct evbuffer_file_segment const* seg, int flags, void* arg); + +/** + Create and return a new evbuffer_file_segment for reading data from a + file and sending it out via an evbuffer. + + This function avoids unnecessary data copies between userland and + kernel. Where available, it uses sendfile or splice. + + The file descriptor must not be closed so long as any evbuffer is using + this segment. + + The results of using evbuffer_remove() or evbuffer_pullup() or any other + function that reads bytes from an evbuffer on any evbuffer containing + the newly returned segment are undefined, unless you pass the + EVBUF_FS_DISABLE_SENDFILE flag to this function. + + @param fd an open file to read from. + @param offset an index within the file at which to start reading + @param length how much data to read, or -1 to read as much as possible. + (-1 requires that 'fd' support fstat.) + @param flags any number of the EVBUF_FS_* flags + @return a new evbuffer_file_segment, or NULL on failure. + **/ +EVENT2_EXPORT_SYMBOL +struct evbuffer_file_segment *evbuffer_file_segment_new( + int fd, ev_off_t offset, ev_off_t length, unsigned flags); + +/** + Free an evbuffer_file_segment + + It is safe to call this function even if the segment has been added to + one or more evbuffers. The evbuffer_file_segment will not be freed + until no more references to it exist. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_file_segment_free(struct evbuffer_file_segment *seg); + +/** + Add cleanup callback and argument for the callback to an + evbuffer_file_segment. + + The cleanup callback will be invoked when no more references to the + evbuffer_file_segment exist. + **/ +EVENT2_EXPORT_SYMBOL +void evbuffer_file_segment_add_cleanup_cb(struct evbuffer_file_segment *seg, + evbuffer_file_segment_cleanup_cb cb, void* arg); + +/** + Insert some or all of an evbuffer_file_segment at the end of an evbuffer + + Note that the offset and length parameters of this function have a + different meaning from those provided to evbuffer_file_segment_new: When + you create the segment, the offset is the offset _within the file_, and + the length is the length _of the segment_, whereas when you add a + segment to an evbuffer, the offset is _within the segment_ and the + length is the length of the _part of the segment you want to use. + + In other words, if you have a 10 KiB file, and you create an + evbuffer_file_segment for it with offset 20 and length 1000, it will + refer to bytes 20..1019 inclusive. If you then pass this segment to + evbuffer_add_file_segment and specify an offset of 20 and a length of + 50, you will be adding bytes 40..99 inclusive. + + @param buf the evbuffer to append to + @param seg the segment to add + @param offset the offset within the segment to start from + @param length the amount of data to add, or -1 to add it all. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_file_segment(struct evbuffer *buf, + struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length); + +/** + Append a formatted string to the end of an evbuffer. + + The string is formated as printf. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ... arguments that will be passed to printf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + + @see evutil_printf(), evbuffer_add_vprintf() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif +; + +/** + Append a va_list formatted string to the end of an evbuffer. + + @param buf the evbuffer that will be appended to + @param fmt a format string + @param ap a varargs va_list argument array that will be passed to vprintf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 0))) +#endif +; + + +/** + Remove a specified number of bytes data from the beginning of an evbuffer. + + @param buf the evbuffer to be drained + @param len the number of bytes to drain from the beginning of the buffer + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_drain(struct evbuffer *buf, size_t len); + + +/** + Write the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_write(struct evbuffer *buffer, evutil_socket_t fd); + +/** + Write some of the contents of an evbuffer to a file descriptor. + + The evbuffer will be drained after the bytes have been successfully written. + + @param buffer the evbuffer to be written and drained + @param fd the file descriptor to be written to + @param howmuch the largest allowable number of bytes to write, or -1 + to write as many bytes as we can. + @return the number of bytes written, or -1 if an error occurred + @see evbuffer_read() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_write_atmost(struct evbuffer *buffer, evutil_socket_t fd, + ev_ssize_t howmuch); + +/** + Read from a file descriptor and store the result in an evbuffer. + + @param buffer the evbuffer to store the result + @param fd the file descriptor to read from + @param howmuch the number of bytes to be read + @return the number of bytes read, or -1 if an error occurred + @see evbuffer_write() + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch); + +/** + Search for a string within an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @param start NULL or a pointer to a valid struct evbuffer_ptr. + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence of the string in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start); + +/** + Search for a string within part of an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @param start NULL or a pointer to a valid struct evbuffer_ptr that + indicates where we should start searching. + @param end NULL or a pointer to a valid struct evbuffer_ptr that + indicates where we should stop searching. + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence of the string in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search_range(struct evbuffer *buffer, const char *what, size_t len, const struct evbuffer_ptr *start, const struct evbuffer_ptr *end); + +/** + Defines how to adjust an evbuffer_ptr by evbuffer_ptr_set() + + @see evbuffer_ptr_set() */ +enum evbuffer_ptr_how { + /** Sets the pointer to the position; can be called on with an + uninitialized evbuffer_ptr. */ + EVBUFFER_PTR_SET, + /** Advances the pointer by adding to the current position. */ + EVBUFFER_PTR_ADD +}; + +/** + Sets the search pointer in the buffer to position. + + There are two ways to use this function: you can call + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_SET) + to move 'pos' to a position 'N' bytes after the start of the buffer, or + evbuffer_ptr_set(buf, &pos, N, EVBUFFER_PTR_ADD) + to move 'pos' forward by 'N' bytes. + + If evbuffer_ptr is not initialized, this function can only be called + with EVBUFFER_PTR_SET. + + An evbuffer_ptr can represent any position from the start of the buffer to + a position immediately after the end of the buffer. + + @param buffer the evbuffer to be search + @param ptr a pointer to a struct evbuffer_ptr + @param position the position at which to start the next search + @param how determines how the pointer should be manipulated. + @returns 0 on success or -1 otherwise +*/ +EVENT2_EXPORT_SYMBOL +int +evbuffer_ptr_set(struct evbuffer *buffer, struct evbuffer_ptr *ptr, + size_t position, enum evbuffer_ptr_how how); + +/** + Search for an end-of-line string within an evbuffer. + + @param buffer the evbuffer to be searched + @param start NULL or a pointer to a valid struct evbuffer_ptr to start + searching at. + @param eol_len_out If non-NULL, the pointed-to value will be set to + the length of the end-of-line string. + @param eol_style The kind of EOL to look for; see evbuffer_readln() for + more information + @return a struct evbuffer_ptr whose 'pos' field has the offset of the + first occurrence EOL in the buffer after 'start'. The 'pos' + field of the result is -1 if the string was not found. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_ptr evbuffer_search_eol(struct evbuffer *buffer, + struct evbuffer_ptr *start, size_t *eol_len_out, + enum evbuffer_eol_style eol_style); + +/** Function to peek at data inside an evbuffer without removing it or + copying it out. + + Pointers to the data are returned by filling the 'vec_out' array + with pointers to one or more extents of data inside the buffer. + + The total data in the extents that you get back may be more than + you requested (if there is more data last extent than you asked + for), or less (if you do not provide enough evbuffer_iovecs, or if + the buffer does not have as much data as you asked to see). + + @param buffer the evbuffer to peek into, + @param len the number of bytes to try to peek. If len is negative, we + will try to fill as much of vec_out as we can. If len is negative + and vec_out is not provided, we return the number of evbuffer_iovecs + that would be needed to get all the data in the buffer. + @param start_at an evbuffer_ptr indicating the point at which we + should start looking for data. NULL means, "At the start of the + buffer." + @param vec_out an array of evbuffer_iovec + @param n_vec the length of vec_out. If 0, we only count how many + extents would be necessary to point to the requested amount of + data. + @return The number of extents needed. This may be less than n_vec + if we didn't need all the evbuffer_iovecs we were given, or more + than n_vec if we would need more to return all the data that was + requested. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_peek(struct evbuffer *buffer, ev_ssize_t len, + struct evbuffer_ptr *start_at, + struct evbuffer_iovec *vec_out, int n_vec); + + +/** Structure passed to an evbuffer_cb_func evbuffer callback + + @see evbuffer_cb_func, evbuffer_add_cb() + */ +struct evbuffer_cb_info { + /** The number of bytes in this evbuffer when callbacks were last + * invoked. */ + size_t orig_size; + /** The number of bytes added since callbacks were last invoked. */ + size_t n_added; + /** The number of bytes removed since callbacks were last invoked. */ + size_t n_deleted; +}; + +/** Type definition for a callback that is invoked whenever data is added or + removed from an evbuffer. + + An evbuffer may have one or more callbacks set at a time. The order + in which they are executed is undefined. + + A callback function may add more callbacks, or remove itself from the + list of callbacks, or add or remove data from the buffer. It may not + remove another callback from the list. + + If a callback adds or removes data from the buffer or from another + buffer, this can cause a recursive invocation of your callback or + other callbacks. If you ask for an infinite loop, you might just get + one: watch out! + + @param buffer the buffer whose size has changed + @param info a structure describing how the buffer changed. + @param arg a pointer to user data +*/ +typedef void (*evbuffer_cb_func)(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *arg); + +struct evbuffer_cb_entry; +/** Add a new callback to an evbuffer. + + Subsequent calls to evbuffer_add_cb() add new callbacks. To remove this + callback, call evbuffer_remove_cb or evbuffer_remove_cb_entry. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified, + or NULL to remove all callbacks. + @param cbarg an argument to be provided to the callback function + @return a handle to the callback on success, or NULL on failure. + */ +EVENT2_EXPORT_SYMBOL +struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg); + +/** Remove a callback from an evbuffer, given a handle returned from + evbuffer_add_cb. + + Calling this function invalidates the handle. + + @return 0 if a callback was removed, or -1 if no matching callback was + found. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_cb_entry(struct evbuffer *buffer, + struct evbuffer_cb_entry *ent); + +/** Remove a callback from an evbuffer, given the function and argument + used to add it. + + @return 0 if a callback was removed, or -1 if no matching callback was + found. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb_func cb, void *cbarg); + +/** If this flag is not set, then a callback is temporarily disabled, and + * should not be invoked. + * + * @see evbuffer_cb_set_flags(), evbuffer_cb_clear_flags() + */ +#define EVBUFFER_CB_ENABLED 1 + +/** Change the flags that are set for a callback on a buffer by adding more. + + @param buffer the evbuffer that the callback is watching. + @param cb the callback whose status we want to change. + @param flags EVBUFFER_CB_ENABLED to re-enable the callback. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_cb_set_flags(struct evbuffer *buffer, + struct evbuffer_cb_entry *cb, ev_uint32_t flags); + +/** Change the flags that are set for a callback on a buffer by removing some + + @param buffer the evbuffer that the callback is watching. + @param cb the callback whose status we want to change. + @param flags EVBUFFER_CB_ENABLED to disable the callback. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_cb_clear_flags(struct evbuffer *buffer, + struct evbuffer_cb_entry *cb, ev_uint32_t flags); + +#if 0 +/** Postpone calling a given callback until unsuspend is called later. + + This is different from disabling the callback, since the callback will get + invoked later if the buffer size changes between now and when we unsuspend + it. + + @param the buffer that the callback is watching. + @param cb the callback we want to suspend. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_cb_suspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb); +/** Stop postponing a callback that we postponed with evbuffer_cb_suspend. + + If data was added to or removed from the buffer while the callback was + suspended, the callback will get called once now. + + @param the buffer that the callback is watching. + @param cb the callback we want to stop suspending. + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_cb_unsuspend(struct evbuffer *buffer, struct evbuffer_cb_entry *cb); +#endif + +/** + Makes the data at the beginning of an evbuffer contiguous. + + @param buf the evbuffer to make contiguous + @param size the number of bytes to make contiguous, or -1 to make the + entire buffer contiguous. + @return a pointer to the contiguous memory array, or NULL if param size + requested more data than is present in the buffer. +*/ + +EVENT2_EXPORT_SYMBOL +unsigned char *evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size); + +/** + Prepends data to the beginning of the evbuffer + + @param buf the evbuffer to which to prepend data + @param data a pointer to the memory to prepend + @param size the number of bytes to prepend + @return 0 if successful, or -1 otherwise +*/ + +EVENT2_EXPORT_SYMBOL +int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t size); + +/** + Prepends all data from the src evbuffer to the beginning of the dst + evbuffer. + + @param dst the evbuffer to which to prepend data + @param src the evbuffer to prepend; it will be emptied as a result + @return 0 if successful, or -1 otherwise +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_prepend_buffer(struct evbuffer *dst, struct evbuffer* src); + +/** + Prevent calls that modify an evbuffer from succeeding. A buffer may + frozen at the front, at the back, or at both the front and the back. + + If the front of a buffer is frozen, operations that drain data from + the front of the buffer, or that prepend data to the buffer, will + fail until it is unfrozen. If the back a buffer is frozen, operations + that append data from the buffer will fail until it is unfrozen. + + @param buf The buffer to freeze + @param at_front If true, we freeze the front of the buffer. If false, + we freeze the back. + @return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int evbuffer_freeze(struct evbuffer *buf, int at_front); +/** + Re-enable calls that modify an evbuffer. + + @param buf The buffer to un-freeze + @param at_front If true, we unfreeze the front of the buffer. If false, + we unfreeze the back. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_unfreeze(struct evbuffer *buf, int at_front); + +struct event_base; +/** + Force all the callbacks on an evbuffer to be run, not immediately after + the evbuffer is altered, but instead from inside the event loop. + + This can be used to serialize all the callbacks to a single thread + of execution. + */ +EVENT2_EXPORT_SYMBOL +int evbuffer_defer_callbacks(struct evbuffer *buffer, struct event_base *base); + +/** + Append data from 1 or more iovec's to an evbuffer + + Calculates the number of bytes needed for an iovec structure and guarantees + all data will fit into a single chain. Can be used in lieu of functionality + which calls evbuffer_add() constantly before being used to increase + performance. + + @param buffer the destination buffer + @param vec the source iovec + @param n_vec the number of iovec structures. + @return the number of bytes successfully written to the output buffer. +*/ +EVENT2_EXPORT_SYMBOL +size_t evbuffer_add_iovec(struct evbuffer * buffer, struct evbuffer_iovec * vec, int n_vec); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFER_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/buffer_compat.h b/src/inc_for_view/event2/buffer_compat.h new file mode 100644 index 0000000..24f828c --- /dev/null +++ b/src/inc_for_view/event2/buffer_compat.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EVENT2_BUFFER_COMPAT_H_INCLUDED_ +#define EVENT2_BUFFER_COMPAT_H_INCLUDED_ + +#include + +/** @file event2/buffer_compat.h + + Obsolete and deprecated versions of the functions in buffer.h: provided + only for backward compatibility. + */ + + +/** + Obsolete alias for evbuffer_readln(buffer, NULL, EVBUFFER_EOL_ANY). + + @deprecated This function is deprecated because its behavior is not correct + for almost any protocol, and also because it's wholly subsumed by + evbuffer_readln(). + + @param buffer the evbuffer to read from + @return pointer to a single line, or NULL if an error occurred + +*/ +EVENT2_EXPORT_SYMBOL +char *evbuffer_readline(struct evbuffer *buffer); + +/** Type definition for a callback that is invoked whenever data is added or + removed from an evbuffer. + + An evbuffer may have one or more callbacks set at a time. The order + in which they are executed is undefined. + + A callback function may add more callbacks, or remove itself from the + list of callbacks, or add or remove data from the buffer. It may not + remove another callback from the list. + + If a callback adds or removes data from the buffer or from another + buffer, this can cause a recursive invocation of your callback or + other callbacks. If you ask for an infinite loop, you might just get + one: watch out! + + @param buffer the buffer whose size has changed + @param old_len the previous length of the buffer + @param new_len the current length of the buffer + @param arg a pointer to user data +*/ +typedef void (*evbuffer_cb)(struct evbuffer *buffer, size_t old_len, size_t new_len, void *arg); + +/** + Replace all callbacks on an evbuffer with a single new callback, or + remove them. + + Subsequent calls to evbuffer_setcb() replace callbacks set by previous + calls. Setting the callback to NULL removes any previously set callback. + + @deprecated This function is deprecated because it clears all previous + callbacks set on the evbuffer, which can cause confusing behavior if + multiple parts of the code all want to add their own callbacks on a + buffer. Instead, use evbuffer_add(), evbuffer_del(), and + evbuffer_setflags() to manage your own evbuffer callbacks without + interfering with callbacks set by others. + + @param buffer the evbuffer to be monitored + @param cb the callback function to invoke when the evbuffer is modified, + or NULL to remove all callbacks. + @param cbarg an argument to be provided to the callback function + */ +EVENT2_EXPORT_SYMBOL +void evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg); + + +/** + Find a string within an evbuffer. + + @param buffer the evbuffer to be searched + @param what the string to be searched for + @param len the length of the search string + @return a pointer to the beginning of the search string, or NULL if the search failed. + */ +EVENT2_EXPORT_SYMBOL +unsigned char *evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len); + +/** deprecated in favor of calling the functions directly */ +#define EVBUFFER_LENGTH(x) evbuffer_get_length(x) +/** deprecated in favor of calling the functions directly */ +#define EVBUFFER_DATA(x) evbuffer_pullup((x), -1) + +#endif + diff --git a/src/inc_for_view/event2/bufferevent.h b/src/inc_for_view/event2/bufferevent.h new file mode 100644 index 0000000..825918e --- /dev/null +++ b/src/inc_for_view/event2/bufferevent.h @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_H_INCLUDED_ + +/** + @file event2/bufferevent.h + + Functions for buffering data for network sending or receiving. Bufferevents + are higher level than evbuffers: each has an underlying evbuffer for reading + and one for writing, and callbacks that are invoked under certain + circumstances. + + A bufferevent provides input and output buffers that get filled and + drained automatically. The user of a bufferevent no longer deals + directly with the I/O, but instead is reading from input and writing + to output buffers. + + Once initialized, the bufferevent structure can be used repeatedly + with bufferevent_enable() and bufferevent_disable(). + + When reading is enabled, the bufferevent will try to read from the + file descriptor onto its input buffer, and call the read callback. + When writing is enabled, the bufferevent will try to write data onto its + file descriptor when the output buffer has enough data, and call the write + callback when the output buffer is sufficiently drained. + + Bufferevents come in several flavors, including: + +
+
Socket-based bufferevents
+
A bufferevent that reads and writes data onto a network + socket. Created with bufferevent_socket_new().
+ +
Paired bufferevents
+
A pair of bufferevents that send and receive data to one + another without touching the network. Created with + bufferevent_pair_new().
+ +
Filtering bufferevents
+
A bufferevent that transforms data, and sends or receives it + over another underlying bufferevent. Created with + bufferevent_filter_new().
+ +
SSL-backed bufferevents
+
A bufferevent that uses the openssl library to send and + receive data over an encrypted connection. Created with + bufferevent_openssl_socket_new() or + bufferevent_openssl_filter_new().
+
+ */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** @name Bufferevent event codes + + These flags are passed as arguments to a bufferevent's event callback. + + @{ +*/ +#define BEV_EVENT_READING 0x01 /**< error encountered while reading */ +#define BEV_EVENT_WRITING 0x02 /**< error encountered while writing */ +#define BEV_EVENT_EOF 0x10 /**< eof file reached */ +#define BEV_EVENT_ERROR 0x20 /**< unrecoverable error encountered */ +#define BEV_EVENT_TIMEOUT 0x40 /**< user-specified timeout reached */ +#define BEV_EVENT_CONNECTED 0x80 /**< connect operation finished. */ +/**@}*/ + +/** + An opaque type for handling buffered IO + + @see event2/bufferevent.h + */ +struct bufferevent +#ifdef EVENT_IN_DOXYGEN_ +{} +#endif +; +struct event_base; +struct evbuffer; +struct sockaddr; + +/** + A read or write callback for a bufferevent. + + The read callback is triggered when new data arrives in the input + buffer and the amount of readable data exceed the low watermark + which is 0 by default. + + The write callback is triggered if the write buffer has been + exhausted or fell below its low watermark. + + @param bev the bufferevent that triggered the callback + @param ctx the user-specified context for this bufferevent + */ +typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx); + +/** + An event/error callback for a bufferevent. + + The event callback is triggered if either an EOF condition or another + unrecoverable error was encountered. + + For bufferevents with deferred callbacks, this is a bitwise OR of all errors + that have happened on the bufferevent since the last callback invocation. + + @param bev the bufferevent for which the error condition was reached + @param what a conjunction of flags: BEV_EVENT_READING or BEV_EVENT_WRITING + to indicate if the error was encountered on the read or write path, + and one of the following flags: BEV_EVENT_EOF, BEV_EVENT_ERROR, + BEV_EVENT_TIMEOUT, BEV_EVENT_CONNECTED. + + @param ctx the user-specified context for this bufferevent +*/ +typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx); + +/** Options that can be specified when creating a bufferevent */ +enum bufferevent_options { + /** If set, we close the underlying file + * descriptor/bufferevent/whatever when this bufferevent is freed. */ + BEV_OPT_CLOSE_ON_FREE = (1<<0), + + /** If set, and threading is enabled, operations on this bufferevent + * are protected by a lock */ + BEV_OPT_THREADSAFE = (1<<1), + + /** If set, callbacks are run deferred in the event loop. */ + BEV_OPT_DEFER_CALLBACKS = (1<<2), + + /** If set, callbacks are executed without locks being held on the + * bufferevent. This option currently requires that + * BEV_OPT_DEFER_CALLBACKS also be set; a future version of Libevent + * might remove the requirement.*/ + BEV_OPT_UNLOCK_CALLBACKS = (1<<3) +}; + +/** + Create a new socket bufferevent over an existing socket. + + @param base the event base to associate with the new bufferevent. + @param fd the file descriptor from which data is read and written to. + This file descriptor is not allowed to be a pipe(2). + It is safe to set the fd to -1, so long as you later + set it with bufferevent_setfd or bufferevent_socket_connect(). + @param options Zero or more BEV_OPT_* flags + @return a pointer to a newly allocated bufferevent struct, or NULL if an + error occurred + @see bufferevent_free() + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options); + +/** + Launch a connect() attempt with a socket-based bufferevent. + + When the connect succeeds, the eventcb will be invoked with + BEV_EVENT_CONNECTED set. + + If the bufferevent does not already have a socket set, we allocate a new + socket here and make it nonblocking before we begin. + + If no address is provided, we assume that the socket is already connecting, + and configure the bufferevent so that a BEV_EVENT_CONNECTED event will be + yielded when it is done connecting. + + @param bufev an existing bufferevent allocated with + bufferevent_socket_new(). + @param addr the address we should connect to + @param socklen The length of the address + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_connect(struct bufferevent *, const struct sockaddr *, int); + +struct evdns_base; +/** + Resolve the hostname 'hostname' and connect to it as with + bufferevent_socket_connect(). + + @param bufev An existing bufferevent allocated with bufferevent_socket_new() + @param evdns_base Optionally, an evdns_base to use for resolving hostnames + asynchronously. May be set to NULL for a blocking resolve. + @param family A preferred address family to resolve addresses to, or + AF_UNSPEC for no preference. Only AF_INET, AF_INET6, and AF_UNSPEC are + supported. + @param hostname The hostname to resolve; see below for notes on recognized + formats + @param port The port to connect to on the resolved address. + @return 0 if successful, -1 on failure. + + Recognized hostname formats are: + + www.example.com (hostname) + 1.2.3.4 (ipv4address) + ::1 (ipv6address) + [::1] ([ipv6address]) + + Performance note: If you do not provide an evdns_base, this function + may block while it waits for a DNS response. This is probably not + what you want. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_connect_hostname(struct bufferevent *, + struct evdns_base *, int, const char *, int); + +/** + Return the error code for the last failed DNS lookup attempt made by + bufferevent_socket_connect_hostname(). + + @param bev The bufferevent object. + @return DNS error code. + @see evutil_gai_strerror() +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_socket_get_dns_error(struct bufferevent *bev); + +/** + Assign a bufferevent to a specific event_base. + + NOTE that only socket bufferevents support this function. + + @param base an event_base returned by event_init() + @param bufev a bufferevent struct returned by bufferevent_new() + or bufferevent_socket_new() + @return 0 if successful, or -1 if an error occurred + @see bufferevent_new() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev); + +/** + Return the event_base used by a bufferevent +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *bufferevent_get_base(struct bufferevent *bev); + +/** + Assign a priority to a bufferevent. + + Only supported for socket bufferevents. + + @param bufev a bufferevent struct + @param pri the priority to be assigned + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_priority_set(struct bufferevent *bufev, int pri); + +/** + Return the priority of a bufferevent. + + Only supported for socket bufferevents + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_get_priority(const struct bufferevent *bufev); + +/** + Deallocate the storage associated with a bufferevent structure. + + If there is pending data to write on the bufferevent, it probably won't be + flushed before the bufferevent is freed. + + @param bufev the bufferevent structure to be freed. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_free(struct bufferevent *bufev); + + +/** + Changes the callbacks for a bufferevent. + + @param bufev the bufferevent object for which to change callbacks + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param eventcb callback to invoke when there is an event on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @see bufferevent_new() + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_setcb(struct bufferevent *bufev, + bufferevent_data_cb readcb, bufferevent_data_cb writecb, + bufferevent_event_cb eventcb, void *cbarg); + +/** + Retrieves the callbacks for a bufferevent. + + @param bufev the bufferevent to examine. + @param readcb_ptr if readcb_ptr is nonnull, *readcb_ptr is set to the current + read callback for the bufferevent. + @param writecb_ptr if writecb_ptr is nonnull, *writecb_ptr is set to the + current write callback for the bufferevent. + @param eventcb_ptr if eventcb_ptr is nonnull, *eventcb_ptr is set to the + current event callback for the bufferevent. + @param cbarg_ptr if cbarg_ptr is nonnull, *cbarg_ptr is set to the current + callback argument for the bufferevent. + @see buffervent_setcb() +*/ +EVENT2_EXPORT_SYMBOL +void bufferevent_getcb(struct bufferevent *bufev, + bufferevent_data_cb *readcb_ptr, + bufferevent_data_cb *writecb_ptr, + bufferevent_event_cb *eventcb_ptr, + void **cbarg_ptr); + +/** + Changes the file descriptor on which the bufferevent operates. + Not supported for all bufferevent types. + + @param bufev the bufferevent object for which to change the file descriptor + @param fd the file descriptor to operate on +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd); + +/** + Returns the file descriptor associated with a bufferevent, or -1 if + no file descriptor is associated with the bufferevent. + */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t bufferevent_getfd(struct bufferevent *bufev); + +/** + Returns the underlying bufferevent associated with a bufferevent (if + the bufferevent is a wrapper), or NULL if there is no underlying bufferevent. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_get_underlying(struct bufferevent *bufev); + +/** + Write data to a bufferevent buffer. + + The bufferevent_write() function can be used to write data to the file + descriptor. The data is appended to the output buffer and written to the + descriptor automatically as it becomes available for writing. + + @param bufev the bufferevent to be written to + @param data a pointer to the data to be written + @param size the length of the data, in bytes + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write_buffer() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_write(struct bufferevent *bufev, + const void *data, size_t size); + + +/** + Write data from an evbuffer to a bufferevent buffer. The evbuffer is + being drained as a result. + + @param bufev the bufferevent to be written to + @param buf the evbuffer to be written + @return 0 if successful, or -1 if an error occurred + @see bufferevent_write() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf); + + +/** + Read data from a bufferevent buffer. + + The bufferevent_read() function is used to read data from the input buffer. + + @param bufev the bufferevent to be read from + @param data pointer to a buffer that will store the data + @param size the size of the data buffer, in bytes + @return the amount of data read, in bytes. + */ +EVENT2_EXPORT_SYMBOL +size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size); + +/** + Read data from a bufferevent buffer into an evbuffer. This avoids + memory copies. + + @param bufev the bufferevent to be read from + @param buf the evbuffer to which to add data + @return 0 if successful, or -1 if an error occurred. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_read_buffer(struct bufferevent *bufev, struct evbuffer *buf); + +/** + Returns the input buffer. + + The user MUST NOT set the callback on this buffer. + + @param bufev the bufferevent from which to get the evbuffer + @return the evbuffer object for the input buffer + */ + +EVENT2_EXPORT_SYMBOL +struct evbuffer *bufferevent_get_input(struct bufferevent *bufev); + +/** + Returns the output buffer. + + The user MUST NOT set the callback on this buffer. + + When filters are being used, the filters need to be manually + triggered if the output buffer was manipulated. + + @param bufev the bufferevent from which to get the evbuffer + @return the evbuffer object for the output buffer + */ + +EVENT2_EXPORT_SYMBOL +struct evbuffer *bufferevent_get_output(struct bufferevent *bufev); + +/** + Enable a bufferevent. + + @param bufev the bufferevent to be enabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_disable() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_enable(struct bufferevent *bufev, short event); + +/** + Disable a bufferevent. + + @param bufev the bufferevent to be disabled + @param event any combination of EV_READ | EV_WRITE. + @return 0 if successful, or -1 if an error occurred + @see bufferevent_enable() + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_disable(struct bufferevent *bufev, short event); + +/** + Return the events that are enabled on a given bufferevent. + + @param bufev the bufferevent to inspect + @return A combination of EV_READ | EV_WRITE + */ +EVENT2_EXPORT_SYMBOL +short bufferevent_get_enabled(struct bufferevent *bufev); + +/** + Set the read and write timeout for a bufferevent. + + A bufferevent's timeout will fire the first time that the indicated + amount of time has elapsed since a successful read or write operation, + during which the bufferevent was trying to read or write. + + (In other words, if reading or writing is disabled, or if the + bufferevent's read or write operation has been suspended because + there's no data to write, or not enough banwidth, or so on, the + timeout isn't active. The timeout only becomes active when we we're + willing to actually read or write.) + + Calling bufferevent_enable or setting a timeout for a bufferevent + whose timeout is already pending resets its timeout. + + If the timeout elapses, the corresponding operation (EV_READ or + EV_WRITE) becomes disabled until you re-enable it again. The + bufferevent's event callback is called with the + BEV_EVENT_TIMEOUT|BEV_EVENT_READING or + BEV_EVENT_TIMEOUT|BEV_EVENT_WRITING. + + @param bufev the bufferevent to be modified + @param timeout_read the read timeout, or NULL + @param timeout_write the write timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_timeouts(struct bufferevent *bufev, + const struct timeval *timeout_read, const struct timeval *timeout_write); + +/** + Sets the watermarks for read and write events. + + On input, a bufferevent does not invoke the user read callback unless + there is at least low watermark data in the buffer. If the read buffer + is beyond the high watermark, the bufferevent stops reading from the network. + + On output, the user write callback is invoked whenever the buffered data + falls below the low watermark. Filters that write to this bufev will try + not to write more bytes to this buffer than the high watermark would allow, + except when flushing. + + @param bufev the bufferevent to be modified + @param events EV_READ, EV_WRITE or both + @param lowmark the lower watermark to set + @param highmark the high watermark to set +*/ + +EVENT2_EXPORT_SYMBOL +void bufferevent_setwatermark(struct bufferevent *bufev, short events, + size_t lowmark, size_t highmark); + +/** + Retrieves the watermarks for read or write events. + Returns non-zero if events contains not only EV_READ or EV_WRITE. + Returns zero if events equal EV_READ or EV_WRITE + + @param bufev the bufferevent to be examined + @param events EV_READ or EV_WRITE + @param lowmark receives the lower watermark if not NULL + @param highmark receives the high watermark if not NULL +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_getwatermark(struct bufferevent *bufev, short events, + size_t *lowmark, size_t *highmark); + +/** + Acquire the lock on a bufferevent. Has no effect if locking was not + enabled with BEV_OPT_THREADSAFE. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_lock(struct bufferevent *bufev); + +/** + Release the lock on a bufferevent. Has no effect if locking was not + enabled with BEV_OPT_THREADSAFE. + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_unlock(struct bufferevent *bufev); + + +/** + * Public interface to manually increase the reference count of a bufferevent + * this is useful in situations where a user may reference the bufferevent + * somewhere eles (unknown to libevent) + * + * @param bufev the bufferevent to increase the refcount on + * + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_incref(struct bufferevent *bufev); + +/** + * Public interface to manually decrement the reference count of a bufferevent + * + * Warning: make sure you know what you're doing. This is mainly used in + * conjunction with bufferevent_incref(). This will free up all data associated + * with a bufferevent if the reference count hits 0. + * + * @param bufev the bufferevent to decrement the refcount on + * + * @return 1 if the bufferevent was freed, otherwise 0 (still referenced) + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_decref(struct bufferevent *bufev); + +/** + Flags that can be passed into filters to let them know how to + deal with the incoming data. +*/ +enum bufferevent_flush_mode { + /** usually set when processing data */ + BEV_NORMAL = 0, + + /** want to checkpoint all data sent. */ + BEV_FLUSH = 1, + + /** encountered EOF on read or done sending data */ + BEV_FINISHED = 2 +}; + +/** + Triggers the bufferevent to produce more data if possible. + + @param bufev the bufferevent object + @param iotype either EV_READ or EV_WRITE or both. + @param mode either BEV_NORMAL or BEV_FLUSH or BEV_FINISHED + @return -1 on failure, 0 if no data was produces, 1 if data was produced + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_flush(struct bufferevent *bufev, + short iotype, + enum bufferevent_flush_mode mode); + +/** + Flags for bufferevent_trigger(_event) that modify when and how to trigger + the callback. +*/ +enum bufferevent_trigger_options { + /** trigger the callback regardless of the watermarks */ + BEV_TRIG_IGNORE_WATERMARKS = (1<<16), + + /** defer even if the callbacks are not */ + BEV_TRIG_DEFER_CALLBACKS = BEV_OPT_DEFER_CALLBACKS + + /* (Note: for internal reasons, these need to be disjoint from + * bufferevent_options, except when they mean the same thing. */ +}; + +/** + Triggers bufferevent data callbacks. + + The function will honor watermarks unless options contain + BEV_TRIG_IGNORE_WATERMARKS. If the options contain BEV_OPT_DEFER_CALLBACKS, + the callbacks are deferred. + + @param bufev the bufferevent object + @param iotype either EV_READ or EV_WRITE or both. + @param options + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_trigger(struct bufferevent *bufev, short iotype, + int options); + +/** + Triggers the bufferevent event callback. + + If the options contain BEV_OPT_DEFER_CALLBACKS, the callbacks are deferred. + + @param bufev the bufferevent object + @param what the flags to pass onto the event callback + @param options + */ +EVENT2_EXPORT_SYMBOL +void bufferevent_trigger_event(struct bufferevent *bufev, short what, + int options); + +/** + @name Filtering support + + @{ +*/ +/** + Values that filters can return. + */ +enum bufferevent_filter_result { + /** everything is okay */ + BEV_OK = 0, + + /** the filter needs to read more data before output */ + BEV_NEED_MORE = 1, + + /** the filter encountered a critical error, no further data + can be processed. */ + BEV_ERROR = 2 +}; + +/** A callback function to implement a filter for a bufferevent. + + @param src An evbuffer to drain data from. + @param dst An evbuffer to add data to. + @param limit A suggested upper bound of bytes to write to dst. + The filter may ignore this value, but doing so means that + it will overflow the high-water mark associated with dst. + -1 means "no limit". + @param mode Whether we should write data as may be convenient + (BEV_NORMAL), or flush as much data as we can (BEV_FLUSH), + or flush as much as we can, possibly including an end-of-stream + marker (BEV_FINISH). + @param ctx A user-supplied pointer. + + @return BEV_OK if we wrote some data; BEV_NEED_MORE if we can't + produce any more output until we get some input; and BEV_ERROR + on an error. + */ +typedef enum bufferevent_filter_result (*bufferevent_filter_cb)( + struct evbuffer *src, struct evbuffer *dst, ev_ssize_t dst_limit, + enum bufferevent_flush_mode mode, void *ctx); + +/** + Allocate a new filtering bufferevent on top of an existing bufferevent. + + @param underlying the underlying bufferevent. + @param input_filter The filter to apply to data we read from the underlying + bufferevent + @param output_filter The filer to apply to data we write to the underlying + bufferevent + @param options A bitfield of bufferevent options. + @param free_context A function to use to free the filter context when + this bufferevent is freed. + @param ctx A context pointer to pass to the filter functions. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_filter_new(struct bufferevent *underlying, + bufferevent_filter_cb input_filter, + bufferevent_filter_cb output_filter, + int options, + void (*free_context)(void *), + void *ctx); +/**@}*/ + +/** + Allocate a pair of linked bufferevents. The bufferevents behave as would + two bufferevent_sock instances connected to opposite ends of a + socketpair(), except that no internal socketpair is allocated. + + @param base The event base to associate with the socketpair. + @param options A set of options for this bufferevent + @param pair A pointer to an array to hold the two new bufferevent objects. + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_pair_new(struct event_base *base, int options, + struct bufferevent *pair[2]); + +/** + Given one bufferevent returned by bufferevent_pair_new(), returns the + other one if it still exists. Otherwise returns NULL. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev); + +/** + Abstract type used to configure rate-limiting on a bufferevent or a group + of bufferevents. + */ +struct ev_token_bucket_cfg; + +/** + A group of bufferevents which are configured to respect the same rate + limit. +*/ +struct bufferevent_rate_limit_group; + +/** Maximum configurable rate- or burst-limit. */ +#define EV_RATE_LIMIT_MAX EV_SSIZE_MAX + +/** + Initialize and return a new object to configure the rate-limiting behavior + of bufferevents. + + @param read_rate The maximum number of bytes to read per tick on + average. + @param read_burst The maximum number of bytes to read in any single tick. + @param write_rate The maximum number of bytes to write per tick on + average. + @param write_burst The maximum number of bytes to write in any single tick. + @param tick_len The length of a single tick. Defaults to one second. + Any fractions of a millisecond are ignored. + + Note that all rate-limits hare are currently best-effort: future versions + of Libevent may implement them more tightly. + */ +EVENT2_EXPORT_SYMBOL +struct ev_token_bucket_cfg *ev_token_bucket_cfg_new( + size_t read_rate, size_t read_burst, + size_t write_rate, size_t write_burst, + const struct timeval *tick_len); + +/** Free all storage held in 'cfg'. + + Note: 'cfg' is not currently reference-counted; it is not safe to free it + until no bufferevent is using it. + */ +EVENT2_EXPORT_SYMBOL +void ev_token_bucket_cfg_free(struct ev_token_bucket_cfg *cfg); + +/** + Set the rate-limit of a the bufferevent 'bev' to the one specified in + 'cfg'. If 'cfg' is NULL, disable any per-bufferevent rate-limiting on + 'bev'. + + Note that only some bufferevent types currently respect rate-limiting. + They are: socket-based bufferevents (normal and IOCP-based), and SSL-based + bufferevents. + + Return 0 on sucess, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_rate_limit(struct bufferevent *bev, + struct ev_token_bucket_cfg *cfg); + +/** + Create a new rate-limit group for bufferevents. A rate-limit group + constrains the maximum number of bytes sent and received, in toto, + by all of its bufferevents. + + @param base An event_base to run any necessary timeouts for the group. + Note that all bufferevents in the group do not necessarily need to share + this event_base. + @param cfg The rate-limit for this group. + + Note that all rate-limits hare are currently best-effort: future versions + of Libevent may implement them more tightly. + + Note also that only some bufferevent types currently respect rate-limiting. + They are: socket-based bufferevents (normal and IOCP-based), and SSL-based + bufferevents. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent_rate_limit_group *bufferevent_rate_limit_group_new( + struct event_base *base, + const struct ev_token_bucket_cfg *cfg); +/** + Change the rate-limiting settings for a given rate-limiting group. + + Return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_set_cfg( + struct bufferevent_rate_limit_group *, + const struct ev_token_bucket_cfg *); + +/** + Change the smallest quantum we're willing to allocate to any single + bufferevent in a group for reading or writing at a time. + + The rationale is that, because of TCP/IP protocol overheads and kernel + behavior, if a rate-limiting group is so tight on bandwidth that you're + only willing to send 1 byte per tick per bufferevent, you might instead + want to batch up the reads and writes so that you send N bytes per + 1/N of the bufferevents (chosen at random) each tick, so you still wind + up send 1 byte per tick per bufferevent on average, but you don't send + so many tiny packets. + + The default min-share is currently 64 bytes. + + Returns 0 on success, -1 on faulre. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_set_min_share( + struct bufferevent_rate_limit_group *, size_t); + +/** + Free a rate-limiting group. The group must have no members when + this function is called. +*/ +EVENT2_EXPORT_SYMBOL +void bufferevent_rate_limit_group_free(struct bufferevent_rate_limit_group *); + +/** + Add 'bev' to the list of bufferevents whose aggregate reading and writing + is restricted by 'g'. If 'g' is NULL, remove 'bev' from its current group. + + A bufferevent may belong to no more than one rate-limit group at a time. + If 'bev' is already a member of a group, it will be removed from its old + group before being added to 'g'. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_add_to_rate_limit_group(struct bufferevent *bev, + struct bufferevent_rate_limit_group *g); + +/** Remove 'bev' from its current rate-limit group (if any). */ +EVENT2_EXPORT_SYMBOL +int bufferevent_remove_from_rate_limit_group(struct bufferevent *bev); + +/** + Set the size limit for single read operation. + + Set to 0 for a reasonable default. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size); + +/** + Set the size limit for single write operation. + + Set to 0 for a reasonable default. + + Return 0 on success and -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size); + +/** Get the current size limit for single read operation. */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev); + +/** Get the current size limit for single write operation. */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev); + +/** + @name Rate limit inspection + + Return the current read or write bucket size for a bufferevent. + If it is not configured with a per-bufferevent ratelimit, return + EV_SSIZE_MAX. This function does not inspect the group limit, if any. + Note that it can return a negative value if the bufferevent has been + made to read or write more than its limit. + + @{ + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_read_limit(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_write_limit(struct bufferevent *bev); +/*@}*/ + +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_to_read(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_get_max_to_write(struct bufferevent *bev); + +EVENT2_EXPORT_SYMBOL +const struct ev_token_bucket_cfg *bufferevent_get_token_bucket_cfg(const struct bufferevent * bev); + +/** + @name Group Rate limit inspection + + Return the read or write bucket size for a bufferevent rate limit + group. Note that it can return a negative value if bufferevents in + the group have been made to read or write more than their limits. + + @{ + */ +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_rate_limit_group_get_read_limit( + struct bufferevent_rate_limit_group *); +EVENT2_EXPORT_SYMBOL +ev_ssize_t bufferevent_rate_limit_group_get_write_limit( + struct bufferevent_rate_limit_group *); +/*@}*/ + +/** + @name Rate limit manipulation + + Subtract a number of bytes from a bufferevent's read or write bucket. + The decrement value can be negative, if you want to manually refill + the bucket. If the change puts the bucket above or below zero, the + bufferevent will resume or suspend reading writing as appropriate. + These functions make no change in the buckets for the bufferevent's + group, if any. + + Returns 0 on success, -1 on internal error. + + @{ + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_decrement_read_limit(struct bufferevent *bev, ev_ssize_t decr); +EVENT2_EXPORT_SYMBOL +int bufferevent_decrement_write_limit(struct bufferevent *bev, ev_ssize_t decr); +/*@}*/ + +/** + @name Group rate limit manipulation + + Subtract a number of bytes from a bufferevent rate-limiting group's + read or write bucket. The decrement value can be negative, if you + want to manually refill the bucket. If the change puts the bucket + above or below zero, the bufferevents in the group will resume or + suspend reading writing as appropriate. + + Returns 0 on success, -1 on internal error. + + @{ + */ +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_decrement_read( + struct bufferevent_rate_limit_group *, ev_ssize_t); +EVENT2_EXPORT_SYMBOL +int bufferevent_rate_limit_group_decrement_write( + struct bufferevent_rate_limit_group *, ev_ssize_t); +/*@}*/ + + +/** + * Inspect the total bytes read/written on a group. + * + * Set the variable pointed to by total_read_out to the total number of bytes + * ever read on grp, and the variable pointed to by total_written_out to the + * total number of bytes ever written on grp. */ +EVENT2_EXPORT_SYMBOL +void bufferevent_rate_limit_group_get_totals( + struct bufferevent_rate_limit_group *grp, + ev_uint64_t *total_read_out, ev_uint64_t *total_written_out); + +/** + * Reset the total bytes read/written on a group. + * + * Reset the number of bytes read or written on grp as given by + * bufferevent_rate_limit_group_reset_totals(). */ +EVENT2_EXPORT_SYMBOL +void +bufferevent_rate_limit_group_reset_totals( + struct bufferevent_rate_limit_group *grp); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/bufferevent_compat.h b/src/inc_for_view/event2/bufferevent_compat.h new file mode 100644 index 0000000..6548204 --- /dev/null +++ b/src/inc_for_view/event2/bufferevent_compat.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2007-2012 Niels Provos, Nick Mathewson + * Copyright (c) 2000-2007 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_COMPAT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_COMPAT_H_INCLUDED_ + +#define evbuffercb bufferevent_data_cb +#define everrorcb bufferevent_event_cb + +/** + Create a new bufferevent for an fd. + + This function is deprecated. Use bufferevent_socket_new and + bufferevent_set_callbacks instead. + + Libevent provides an abstraction on top of the regular event callbacks. + This abstraction is called a buffered event. A buffered event provides + input and output buffers that get filled and drained automatically. The + user of a buffered event no longer deals directly with the I/O, but + instead is reading from input and writing to output buffers. + + Once initialized, the bufferevent structure can be used repeatedly with + bufferevent_enable() and bufferevent_disable(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + If multiple bases are in use, bufferevent_base_set() must be called before + enabling the bufferevent for the first time. + + @deprecated This function is deprecated because it uses the current + event base, and as such can be error prone for multithreaded programs. + Use bufferevent_socket_new() instead. + + @param fd the file descriptor from which data is read and written to. + This file descriptor is not allowed to be a pipe(2). + @param readcb callback to invoke when there is data to be read, or NULL if + no callback is desired + @param writecb callback to invoke when the file descriptor is ready for + writing, or NULL if no callback is desired + @param errorcb callback to invoke when there is an error on the file + descriptor + @param cbarg an argument that will be supplied to each of the callbacks + (readcb, writecb, and errorcb) + @return a pointer to a newly allocated bufferevent struct, or NULL if an + error occurred + @see bufferevent_base_set(), bufferevent_free() + */ +struct bufferevent *bufferevent_new(evutil_socket_t fd, + evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); + + +/** + Set the read and write timeout for a buffered event. + + @param bufev the bufferevent to be modified + @param timeout_read the read timeout + @param timeout_write the write timeout + */ +void bufferevent_settimeout(struct bufferevent *bufev, + int timeout_read, int timeout_write); + +#define EVBUFFER_READ BEV_EVENT_READING +#define EVBUFFER_WRITE BEV_EVENT_WRITING +#define EVBUFFER_EOF BEV_EVENT_EOF +#define EVBUFFER_ERROR BEV_EVENT_ERROR +#define EVBUFFER_TIMEOUT BEV_EVENT_TIMEOUT + +/** macro for getting access to the input buffer of a bufferevent */ +#define EVBUFFER_INPUT(x) bufferevent_get_input(x) +/** macro for getting access to the output buffer of a bufferevent */ +#define EVBUFFER_OUTPUT(x) bufferevent_get_output(x) + +#endif diff --git a/src/inc_for_view/event2/bufferevent_ssl.h b/src/inc_for_view/event2/bufferevent_ssl.h new file mode 100644 index 0000000..bf39b84 --- /dev/null +++ b/src/inc_for_view/event2/bufferevent_ssl.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ + +/** @file event2/bufferevent_ssl.h + + OpenSSL support for bufferevents. + */ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is what openssl's SSL objects are underneath. */ +struct ssl_st; + +/** + The state of an SSL object to be used when creating a new + SSL bufferevent. + */ +enum bufferevent_ssl_state { + BUFFEREVENT_SSL_OPEN = 0, + BUFFEREVENT_SSL_CONNECTING = 1, + BUFFEREVENT_SSL_ACCEPTING = 2 +}; + +#if defined(EVENT__HAVE_OPENSSL) || defined(EVENT_IN_DOXYGEN_) +/** + Create a new SSL bufferevent to send its data over another bufferevent. + + @param base An event_base to use to detect reading and writing. It + must also be the base for the underlying bufferevent. + @param underlying A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_openssl_filter_new(struct event_base *base, + struct bufferevent *underlying, + struct ssl_st *ssl, + enum bufferevent_ssl_state state, + int options); + +/** + Create a new SSL bufferevent to send its data over an SSL * on a socket. + + @param base An event_base to use to detect reading and writing + @param fd A socket to use for this SSL + @param ssl A SSL* object from openssl. + @param state The current state of the SSL connection + @param options One or more bufferevent_options + @return A new bufferevent on success, or NULL on failure. +*/ +EVENT2_EXPORT_SYMBOL +struct bufferevent * +bufferevent_openssl_socket_new(struct event_base *base, + evutil_socket_t fd, + struct ssl_st *ssl, + enum bufferevent_ssl_state state, + int options); + +/** Control how to report dirty SSL shutdowns. + + If the peer (or the network, or an attacker) closes the TCP + connection before closing the SSL channel, and the protocol is SSL >= v3, + this is a "dirty" shutdown. If allow_dirty_shutdown is 0 (default), + this is reported as BEV_EVENT_ERROR. + + If instead allow_dirty_shutdown=1, a dirty shutdown is reported as + BEV_EVENT_EOF. + + (Note that if the protocol is < SSLv3, you will always receive + BEV_EVENT_EOF, since SSL 2 and earlier cannot distinguish a secure + connection close from a dirty one. This is one reason (among many) + not to use SSL 2.) +*/ + +EVENT2_EXPORT_SYMBOL +int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev); +EVENT2_EXPORT_SYMBOL +void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, + int allow_dirty_shutdown); + +/** Return the underlying openssl SSL * object for an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +struct ssl_st * +bufferevent_openssl_get_ssl(struct bufferevent *bufev); + +/** Tells a bufferevent to begin SSL renegotiation. */ +EVENT2_EXPORT_SYMBOL +int bufferevent_ssl_renegotiate(struct bufferevent *bev); + +/** Return the most recent OpenSSL error reported on an SSL bufferevent. */ +EVENT2_EXPORT_SYMBOL +unsigned long bufferevent_get_openssl_error(struct bufferevent *bev); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_SSL_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/bufferevent_struct.h b/src/inc_for_view/event2/bufferevent_struct.h new file mode 100644 index 0000000..e84c082 --- /dev/null +++ b/src/inc_for_view/event2/bufferevent_struct.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ +#define EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ + +/** @file event2/bufferevent_struct.h + + Data structures for bufferevents. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + @deprecated Use of bufferevent_struct.h is completely deprecated; these + structures are only exposed for backward compatibility with programs + written before Libevent 2.0 that used them. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include +/* For struct event */ +#include + +struct event_watermark { + size_t low; + size_t high; +}; + +/** + Shared implementation of a bufferevent. + + This type is exposed only because it was exposed in previous versions, + and some people's code may rely on manipulating it. Otherwise, you + should really not rely on the layout, size, or contents of this structure: + it is fairly volatile, and WILL change in future versions of the code. +**/ +struct bufferevent { + /** Event base for which this bufferevent was created. */ + struct event_base *ev_base; + /** Pointer to a table of function pointers to set up how this + bufferevent behaves. */ + const struct bufferevent_ops *be_ops; + + /** A read event that triggers when a timeout has happened or a socket + is ready to read data. Only used by some subtypes of + bufferevent. */ + struct event ev_read; + /** A write event that triggers when a timeout has happened or a socket + is ready to write data. Only used by some subtypes of + bufferevent. */ + struct event ev_write; + + /** An input buffer. Only the bufferevent is allowed to add data to + this buffer, though the user is allowed to drain it. */ + struct evbuffer *input; + + /** An input buffer. Only the bufferevent is allowed to drain data + from this buffer, though the user is allowed to add it. */ + struct evbuffer *output; + + struct event_watermark wm_read; + struct event_watermark wm_write; + + bufferevent_data_cb readcb; + bufferevent_data_cb writecb; + /* This should be called 'eventcb', but renaming it would break + * backward compatibility */ + bufferevent_event_cb errorcb; + void *cbarg; + + struct timeval timeout_read; + struct timeval timeout_write; + + /** Events that are currently enabled: currently EV_READ and EV_WRITE + are supported. */ + short enabled; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_BUFFEREVENT_STRUCT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/dns.h b/src/inc_for_view/event2/dns.h new file mode 100644 index 0000000..17cd86a --- /dev/null +++ b/src/inc_for_view/event2/dns.h @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The original DNS code is due to Adam Langley with heavy + * modifications by Nick Mathewson. Adam put his DNS software in the + * public domain. You can find his original copyright below. Please, + * aware that the code as part of Libevent is governed by the 3-clause + * BSD license above. + * + * This software is Public Domain. To view a copy of the public domain dedication, + * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to + * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + * + * I ask and expect, but do not require, that all derivative works contain an + * attribution similar to: + * Parts developed by Adam Langley + * + * You may wish to replace the word "Parts" with something else depending on + * the amount of original code. + * + * (Derivative works does not include programs which link against, run or include + * the source verbatim in their source distributions) + */ + +/** @file event2/dns.h + * + * Welcome, gentle reader + * + * Async DNS lookups are really a whole lot harder than they should be, + * mostly stemming from the fact that the libc resolver has never been + * very good at them. Before you use this library you should see if libc + * can do the job for you with the modern async call getaddrinfo_a + * (see http://www.imperialviolet.org/page25.html#e498). Otherwise, + * please continue. + * + * The library keeps track of the state of nameservers and will avoid + * them when they go down. Otherwise it will round robin between them. + * + * Quick start guide: + * #include "evdns.h" + * void callback(int result, char type, int count, int ttl, + * void *addresses, void *arg); + * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); + * evdns_resolve("www.hostname.com", 0, callback, NULL); + * + * When the lookup is complete the callback function is called. The + * first argument will be one of the DNS_ERR_* defines in evdns.h. + * Hopefully it will be DNS_ERR_NONE, in which case type will be + * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time + * which the data can be cached for (in seconds), addresses will point + * to an array of uint32_t's and arg will be whatever you passed to + * evdns_resolve. + * + * Searching: + * + * In order for this library to be a good replacement for glibc's resolver it + * supports searching. This involves setting a list of default domains, in + * which names will be queried for. The number of dots in the query name + * determines the order in which this list is used. + * + * Searching appears to be a single lookup from the point of view of the API, + * although many DNS queries may be generated from a single call to + * evdns_resolve. Searching can also drastically slow down the resolution + * of names. + * + * To disable searching: + * 1. Never set it up. If you never call evdns_resolv_conf_parse or + * evdns_search_add then no searching will occur. + * + * 2. If you do call evdns_resolv_conf_parse then don't pass + * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it). + * + * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag. + * + * The order of searches depends on the number of dots in the name. If the + * number is greater than the ndots setting then the names is first tried + * globally. Otherwise each search domain is appended in turn. + * + * The ndots setting can either be set from a resolv.conf, or by calling + * evdns_search_ndots_set. + * + * For example, with ndots set to 1 (the default) and a search domain list of + * ["myhome.net"]: + * Query: www + * Order: www.myhome.net, www. + * + * Query: www.abc + * Order: www.abc., www.abc.myhome.net + * + * Internals: + * + * Requests are kept in two queues. The first is the inflight queue. In + * this queue requests have an allocated transaction id and nameserver. + * They will soon be transmitted if they haven't already been. + * + * The second is the waiting queue. The size of the inflight ring is + * limited and all other requests wait in waiting queue for space. This + * bounds the number of concurrent requests so that we don't flood the + * nameserver. Several algorithms require a full walk of the inflight + * queue and so bounding its size keeps thing going nicely under huge + * (many thousands of requests) loads. + * + * If a nameserver loses too many requests it is considered down and we + * try not to use it. After a while we send a probe to that nameserver + * (a lookup for google.com) and, if it replies, we consider it working + * again. If the nameserver fails a probe we wait longer to try again + * with the next probe. + */ + +#ifndef EVENT2_DNS_H_INCLUDED_ +#define EVENT2_DNS_H_INCLUDED_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* For integer types. */ +#include + +/** Error codes 0-5 are as described in RFC 1035. */ +#define DNS_ERR_NONE 0 +/** The name server was unable to interpret the query */ +#define DNS_ERR_FORMAT 1 +/** The name server was unable to process this query due to a problem with the + * name server */ +#define DNS_ERR_SERVERFAILED 2 +/** The domain name does not exist */ +#define DNS_ERR_NOTEXIST 3 +/** The name server does not support the requested kind of query */ +#define DNS_ERR_NOTIMPL 4 +/** The name server refuses to reform the specified operation for policy + * reasons */ +#define DNS_ERR_REFUSED 5 +/** The reply was truncated or ill-formatted */ +#define DNS_ERR_TRUNCATED 65 +/** An unknown error occurred */ +#define DNS_ERR_UNKNOWN 66 +/** Communication with the server timed out */ +#define DNS_ERR_TIMEOUT 67 +/** The request was canceled because the DNS subsystem was shut down. */ +#define DNS_ERR_SHUTDOWN 68 +/** The request was canceled via a call to evdns_cancel_request */ +#define DNS_ERR_CANCEL 69 +/** There were no answers and no error condition in the DNS packet. + * This can happen when you ask for an address that exists, but a record + * type that doesn't. */ +#define DNS_ERR_NODATA 70 + +#define DNS_IPv4_A 1 +#define DNS_PTR 2 +#define DNS_IPv6_AAAA 3 + +#define DNS_QUERY_NO_SEARCH 1 + +#define DNS_OPTION_SEARCH 1 +#define DNS_OPTION_NAMESERVERS 2 +#define DNS_OPTION_MISC 4 +#define DNS_OPTION_HOSTSFILE 8 +#define DNS_OPTIONS_ALL 15 + +/* Obsolete name for DNS_QUERY_NO_SEARCH */ +#define DNS_NO_SEARCH DNS_QUERY_NO_SEARCH + +/** + * The callback that contains the results from a lookup. + * - result is one of the DNS_ERR_* values (DNS_ERR_NONE for success) + * - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA + * - count contains the number of addresses of form type + * - ttl is the number of seconds the resolution may be cached for. + * - addresses needs to be cast according to type. It will be an array of + * 4-byte sequences for ipv4, or an array of 16-byte sequences for ipv6, + * or a nul-terminated string for PTR. + */ +typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg); + +struct evdns_base; +struct event_base; + +/** Flag for evdns_base_new: process resolv.conf. */ +#define EVDNS_BASE_INITIALIZE_NAMESERVERS 1 +/** Flag for evdns_base_new: Do not prevent the libevent event loop from + * exiting when we have no active dns requests. */ +#define EVDNS_BASE_DISABLE_WHEN_INACTIVE 0x8000 + +/** + Initialize the asynchronous DNS library. + + This function initializes support for non-blocking name resolution by + calling evdns_resolv_conf_parse() on UNIX and + evdns_config_windows_nameservers() on Windows. + + @param event_base the event base to associate the dns client with + @param flags any of EVDNS_BASE_INITIALIZE_NAMESERVERS| + EVDNS_BASE_DISABLE_WHEN_INACTIVE + @return evdns_base object if successful, or NULL if an error occurred. + @see evdns_base_free() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_base * evdns_base_new(struct event_base *event_base, int initialize_nameservers); + + +/** + Shut down the asynchronous DNS resolver and terminate all active requests. + + If the 'fail_requests' option is enabled, all active requests will return + an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise, + the requests will be silently discarded. + + @param evdns_base the evdns base to free + @param fail_requests if zero, active requests will be aborted; if non-zero, + active requests will return DNS_ERR_SHUTDOWN. + @see evdns_base_new() + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_free(struct evdns_base *base, int fail_requests); + +/** + Remove all hosts entries that have been loaded into the event_base via + evdns_base_load_hosts or via event_base_resolv_conf_parse. + + @param evdns_base the evdns base to remove outdated host addresses from + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_clear_host_addresses(struct evdns_base *base); + +/** + Convert a DNS error code to a string. + + @param err the DNS error code + @return a string containing an explanation of the error code +*/ +EVENT2_EXPORT_SYMBOL +const char *evdns_err_to_string(int err); + + +/** + Add a nameserver. + + The address should be an IPv4 address in network byte order. + The type of address is chosen so that it matches in_addr.s_addr. + + @param base the evdns_base to which to add the name server + @param address an IP address in network byte order + @return 0 if successful, or -1 if an error occurred + @see evdns_base_nameserver_ip_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_nameserver_add(struct evdns_base *base, + unsigned long int address); + +/** + Get the number of configured nameservers. + + This returns the number of configured nameservers (not necessarily the + number of running nameservers). This is useful for double-checking + whether our calls to the various nameserver configuration functions + have been successful. + + @param base the evdns_base to which to apply this operation + @return the number of configured nameservers + @see evdns_base_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_count_nameservers(struct evdns_base *base); + +/** + Remove all configured nameservers, and suspend all pending resolves. + + Resolves will not necessarily be re-attempted until evdns_base_resume() is called. + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_resume() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_clear_nameservers_and_suspend(struct evdns_base *base); + + +/** + Resume normal operation and continue any suspended resolve requests. + + Re-attempt resolves left in limbo after an earlier call to + evdns_base_clear_nameservers_and_suspend(). + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_clear_nameservers_and_suspend() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_resume(struct evdns_base *base); + +/** + Add a nameserver by string address. + + This function parses a n IPv4 or IPv6 address from a string and adds it as a + nameserver. It supports the following formats: + - [IPv6Address]:port + - [IPv6Address] + - IPv6Address + - IPv4Address:port + - IPv4Address + + If no port is specified, it defaults to 53. + + @param base the evdns_base to which to apply this operation + @return 0 if successful, or -1 if an error occurred + @see evdns_base_nameserver_add() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_nameserver_ip_add(struct evdns_base *base, + const char *ip_as_string); + +/** + Add a nameserver by sockaddr. + **/ +EVENT2_EXPORT_SYMBOL +int +evdns_base_nameserver_sockaddr_add(struct evdns_base *base, + const struct sockaddr *sa, ev_socklen_t len, unsigned flags); + +struct evdns_request; + +/** + Lookup an A record for a given name. + + @param base the evdns_base to which to apply this operation + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_ipv4(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup an AAAA record for a given name. + + @param base the evdns_base to which to apply this operation + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_ipv6(struct evdns_base *base, const char *name, int flags, evdns_callback_type callback, void *ptr); + +struct in_addr; +struct in6_addr; + +/** + Lookup a PTR record for a given IP address. + + @param base the evdns_base to which to apply this operation + @param in an IPv4 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_reverse(struct evdns_base *base, const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); + + +/** + Lookup a PTR record for a given IPv6 address. + + @param base the evdns_base to which to apply this operation + @param in an IPv6 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return an evdns_request object if successful, or NULL if an error occurred. + @see evdns_resolve_reverse_ipv6(), evdns_cancel_request() + */ +EVENT2_EXPORT_SYMBOL +struct evdns_request *evdns_base_resolve_reverse_ipv6(struct evdns_base *base, const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Cancels a pending DNS resolution request. + + @param base the evdns_base that was used to make the request + @param req the evdns_request that was returned by calling a resolve function + @see evdns_base_resolve_ipv4(), evdns_base_resolve_ipv6, evdns_base_resolve_reverse +*/ +EVENT2_EXPORT_SYMBOL +void evdns_cancel_request(struct evdns_base *base, struct evdns_request *req); + +/** + Set the value of a configuration option. + + The currently available configuration options are: + + ndots, timeout, max-timeouts, max-inflight, attempts, randomize-case, + bind-to, initial-probe-timeout, getaddrinfo-allow-skew. + + In versions before Libevent 2.0.3-alpha, the option name needed to end with + a colon. + + @param base the evdns_base to which to apply this operation + @param option the name of the configuration option to be modified + @param val the value to be set + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_set_option(struct evdns_base *base, const char *option, const char *val); + + +/** + Parse a resolv.conf file. + + The 'flags' parameter determines what information is parsed from the + resolv.conf file. See the man page for resolv.conf for the format of this + file. + + The following directives are not parsed from the file: sortlist, rotate, + no-check-names, inet6, debug. + + If this function encounters an error, the possible return values are: 1 = + failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of + memory, 5 = short read from file, 6 = no nameservers listed in the file + + @param base the evdns_base to which to apply this operation + @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC| + DNS_OPTION_HOSTSFILE|DNS_OPTIONS_ALL + @param filename the path to the resolv.conf file + @return 0 if successful, or various positive error codes if an error + occurred (see above) + @see resolv.conf(3), evdns_config_windows_nameservers() + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_resolv_conf_parse(struct evdns_base *base, int flags, const char *const filename); + +/** + Load an /etc/hosts-style file from 'hosts_fname' into 'base'. + + If hosts_fname is NULL, add minimal entries for localhost, and nothing + else. + + Note that only evdns_getaddrinfo uses the /etc/hosts entries. + + This function does not replace previously loaded hosts entries; to do that, + call evdns_base_clear_host_addresses first. + + Return 0 on success, negative on failure. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_base_load_hosts(struct evdns_base *base, const char *hosts_fname); + +/** + Obtain nameserver information using the Windows API. + + Attempt to configure a set of nameservers based on platform settings on + a win32 host. Preferentially tries to use GetNetworkParams; if that fails, + looks in the registry. + + @return 0 if successful, or -1 if an error occurred + @see evdns_resolv_conf_parse() + */ +#ifdef _WIN32 +EVENT2_EXPORT_SYMBOL +int evdns_base_config_windows_nameservers(struct evdns_base *); +#define EVDNS_BASE_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED +#endif + + +/** + Clear the list of search domains. + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_clear(struct evdns_base *base); + + +/** + Add a domain to the list of search domains + + @param domain the domain to be added to the search list + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_add(struct evdns_base *base, const char *domain); + + +/** + Set the 'ndots' parameter for searches. + + Sets the number of dots which, when found in a name, causes + the first query to be without any search domain. + + @param ndots the new ndots parameter + */ +EVENT2_EXPORT_SYMBOL +void evdns_base_search_ndots_set(struct evdns_base *base, const int ndots); + +/** + A callback that is invoked when a log message is generated + + @param is_warning indicates if the log message is a 'warning' + @param msg the content of the log message + */ +typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg); + + +/** + Set the callback function to handle DNS log messages. If this + callback is not set, evdns log messages are handled with the regular + Libevent logging system. + + @param fn the callback to be invoked when a log message is generated + */ +EVENT2_EXPORT_SYMBOL +void evdns_set_log_fn(evdns_debug_log_fn_type fn); + +/** + Set a callback that will be invoked to generate transaction IDs. By + default, we pick transaction IDs based on the current clock time, which + is bad for security. + + @param fn the new callback, or NULL to use the default. + + NOTE: This function has no effect in Libevent 2.0.4-alpha and later, + since Libevent now provides its own secure RNG. + */ +EVENT2_EXPORT_SYMBOL +void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void)); + +/** + Set a callback used to generate random bytes. By default, we use + the same function as passed to evdns_set_transaction_id_fn to generate + bytes two at a time. If a function is provided here, it's also used + to generate transaction IDs. + + NOTE: This function has no effect in Libevent 2.0.4-alpha and later, + since Libevent now provides its own secure RNG. +*/ +EVENT2_EXPORT_SYMBOL +void evdns_set_random_bytes_fn(void (*fn)(char *, size_t)); + +/* + * Functions used to implement a DNS server. + */ + +struct evdns_server_request; +struct evdns_server_question; + +/** + A callback to implement a DNS server. The callback function receives a DNS + request. It should then optionally add a number of answers to the reply + using the evdns_server_request_add_*_reply functions, before calling either + evdns_server_request_respond to send the reply back, or + evdns_server_request_drop to decline to answer the request. + + @param req A newly received request + @param user_data A pointer that was passed to + evdns_add_server_port_with_base(). + */ +typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *); +#define EVDNS_ANSWER_SECTION 0 +#define EVDNS_AUTHORITY_SECTION 1 +#define EVDNS_ADDITIONAL_SECTION 2 + +#define EVDNS_TYPE_A 1 +#define EVDNS_TYPE_NS 2 +#define EVDNS_TYPE_CNAME 5 +#define EVDNS_TYPE_SOA 6 +#define EVDNS_TYPE_PTR 12 +#define EVDNS_TYPE_MX 15 +#define EVDNS_TYPE_TXT 16 +#define EVDNS_TYPE_AAAA 28 + +#define EVDNS_QTYPE_AXFR 252 +#define EVDNS_QTYPE_ALL 255 + +#define EVDNS_CLASS_INET 1 + +/* flags that can be set in answers; as part of the err parameter */ +#define EVDNS_FLAGS_AA 0x400 +#define EVDNS_FLAGS_RD 0x080 + +/** Create a new DNS server port. + + @param base The event base to handle events for the server port. + @param socket A UDP socket to accept DNS requests. + @param flags Always 0 for now. + @param callback A function to invoke whenever we get a DNS request + on the socket. + @param user_data Data to pass to the callback. + @return an evdns_server_port structure for this server port. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_server_port *evdns_add_server_port_with_base(struct event_base *base, evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data); +/** Close down a DNS server port, and free associated structures. */ +EVENT2_EXPORT_SYMBOL +void evdns_close_server_port(struct evdns_server_port *port); + +/** Sets some flags in a reply we're building. + Allows setting of the AA or RD flags + */ +EVENT2_EXPORT_SYMBOL +void evdns_server_request_set_flags(struct evdns_server_request *req, int flags); + +/* Functions to add an answer to an in-progress DNS reply. + */ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int dns_class, int ttl, int datalen, int is_name, const char *data); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); +EVENT2_EXPORT_SYMBOL +int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); + +/** + Send back a response to a DNS request, and free the request structure. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_respond(struct evdns_server_request *req, int err); +/** + Free a DNS request without sending back a reply. +*/ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_drop(struct evdns_server_request *req); +struct sockaddr; +/** + Get the address that made a DNS request. + */ +EVENT2_EXPORT_SYMBOL +int evdns_server_request_get_requesting_addr(struct evdns_server_request *req, struct sockaddr *sa, int addr_len); + +/** Callback for evdns_getaddrinfo. */ +typedef void (*evdns_getaddrinfo_cb)(int result, struct evutil_addrinfo *res, void *arg); + +struct evdns_base; +struct evdns_getaddrinfo_request; +/** Make a non-blocking getaddrinfo request using the dns_base in 'dns_base'. + * + * If we can answer the request immediately (with an error or not!), then we + * invoke cb immediately and return NULL. Otherwise we return + * an evdns_getaddrinfo_request and invoke cb later. + * + * When the callback is invoked, we pass as its first argument the error code + * that getaddrinfo would return (or 0 for no error). As its second argument, + * we pass the evutil_addrinfo structures we found (or NULL on error). We + * pass 'arg' as the third argument. + * + * Limitations: + * + * - The AI_V4MAPPED and AI_ALL flags are not currently implemented. + * - For ai_socktype, we only handle SOCKTYPE_STREAM, SOCKTYPE_UDP, and 0. + * - For ai_protocol, we only handle IPPROTO_TCP, IPPROTO_UDP, and 0. + */ +EVENT2_EXPORT_SYMBOL +struct evdns_getaddrinfo_request *evdns_getaddrinfo( + struct evdns_base *dns_base, + const char *nodename, const char *servname, + const struct evutil_addrinfo *hints_in, + evdns_getaddrinfo_cb cb, void *arg); + +/* Cancel an in-progress evdns_getaddrinfo. This MUST NOT be called after the + * getaddrinfo's callback has been invoked. The resolves will be canceled, + * and the callback will be invoked with the error EVUTIL_EAI_CANCEL. */ +EVENT2_EXPORT_SYMBOL +void evdns_getaddrinfo_cancel(struct evdns_getaddrinfo_request *req); + +/** + Retrieve the address of the 'idx'th configured nameserver. + + @param base The evdns_base to examine. + @param idx The index of the nameserver to get the address of. + @param sa A location to receive the server's address. + @param len The number of bytes available at sa. + + @return the number of bytes written into sa on success. On failure, returns + -1 if idx is greater than the number of configured nameservers, or a + value greater than 'len' if len was not high enough. + */ +EVENT2_EXPORT_SYMBOL +int evdns_base_get_nameserver_addr(struct evdns_base *base, int idx, + struct sockaddr *sa, ev_socklen_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* !EVENT2_DNS_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/dns_compat.h b/src/inc_for_view/event2/dns_compat.h new file mode 100644 index 0000000..965fd65 --- /dev/null +++ b/src/inc_for_view/event2/dns_compat.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_DNS_COMPAT_H_INCLUDED_ +#define EVENT2_DNS_COMPAT_H_INCLUDED_ + +/** @file event2/dns_compat.h + + Potentially non-threadsafe versions of the functions in dns.h: provided + only for backwards compatibility. + + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + Initialize the asynchronous DNS library. + + This function initializes support for non-blocking name resolution by + calling evdns_resolv_conf_parse() on UNIX and + evdns_config_windows_nameservers() on Windows. + + @deprecated This function is deprecated because it always uses the current + event base, and is easily confused by multiple calls to event_init(), and + so is not safe for multithreaded use. Additionally, it allocates a global + structure that only one thread can use. The replacement is + evdns_base_new(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_shutdown() + */ +int evdns_init(void); + +struct evdns_base; +/** + Return the global evdns_base created by event_init() and used by the other + deprecated functions. + + @deprecated This function is deprecated because use of the global + evdns_base is error-prone. + */ +struct evdns_base *evdns_get_global_base(void); + +/** + Shut down the asynchronous DNS resolver and terminate all active requests. + + If the 'fail_requests' option is enabled, all active requests will return + an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise, + the requests will be silently discarded. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_shutdown(). + + @param fail_requests if zero, active requests will be aborted; if non-zero, + active requests will return DNS_ERR_SHUTDOWN. + @see evdns_init() + */ +void evdns_shutdown(int fail_requests); + +/** + Add a nameserver. + + The address should be an IPv4 address in network byte order. + The type of address is chosen so that it matches in_addr.s_addr. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_nameserver_add(). + + @param address an IP address in network byte order + @return 0 if successful, or -1 if an error occurred + @see evdns_nameserver_ip_add() + */ +int evdns_nameserver_add(unsigned long int address); + +/** + Get the number of configured nameservers. + + This returns the number of configured nameservers (not necessarily the + number of running nameservers). This is useful for double-checking + whether our calls to the various nameserver configuration functions + have been successful. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_count_nameservers(). + + @return the number of configured nameservers + @see evdns_nameserver_add() + */ +int evdns_count_nameservers(void); + +/** + Remove all configured nameservers, and suspend all pending resolves. + + Resolves will not necessarily be re-attempted until evdns_resume() is called. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_clear_nameservers_and_suspend(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_resume() + */ +int evdns_clear_nameservers_and_suspend(void); + +/** + Resume normal operation and continue any suspended resolve requests. + + Re-attempt resolves left in limbo after an earlier call to + evdns_clear_nameservers_and_suspend(). + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resume(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_clear_nameservers_and_suspend() + */ +int evdns_resume(void); + +/** + Add a nameserver. + + This wraps the evdns_nameserver_add() function by parsing a string as an IP + address and adds it as a nameserver. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_nameserver_ip_add(). + + @return 0 if successful, or -1 if an error occurred + @see evdns_nameserver_add() + */ +int evdns_nameserver_ip_add(const char *ip_as_string); + +/** + Lookup an A record for a given name. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_ipv4(). + + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup an AAAA record for a given name. + + @param name a DNS hostname + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr); + +struct in_addr; +struct in6_addr; + +/** + Lookup a PTR record for a given IP address. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_reverse(). + + @param in an IPv4 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Lookup a PTR record for a given IPv6 address. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolve_reverse_ipv6(). + + @param in an IPv6 address + @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query. + @param callback a callback function to invoke when the request is completed + @param ptr an argument to pass to the callback function + @return 0 if successful, or -1 if an error occurred + @see evdns_resolve_reverse_ipv6() + */ +int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr); + +/** + Set the value of a configuration option. + + The currently available configuration options are: + + ndots, timeout, max-timeouts, max-inflight, and attempts + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_set_option(). + + @param option the name of the configuration option to be modified + @param val the value to be set + @param flags Ignored. + @return 0 if successful, or -1 if an error occurred + */ +int evdns_set_option(const char *option, const char *val, int flags); + +/** + Parse a resolv.conf file. + + The 'flags' parameter determines what information is parsed from the + resolv.conf file. See the man page for resolv.conf for the format of this + file. + + The following directives are not parsed from the file: sortlist, rotate, + no-check-names, inet6, debug. + + If this function encounters an error, the possible return values are: 1 = + failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of + memory, 5 = short read from file, 6 = no nameservers listed in the file + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_resolv_conf_parse(). + + @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC| + DNS_OPTIONS_ALL + @param filename the path to the resolv.conf file + @return 0 if successful, or various positive error codes if an error + occurred (see above) + @see resolv.conf(3), evdns_config_windows_nameservers() + */ +int evdns_resolv_conf_parse(int flags, const char *const filename); + +/** + Clear the list of search domains. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_clear(). + */ +void evdns_search_clear(void); + +/** + Add a domain to the list of search domains + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_add(). + + @param domain the domain to be added to the search list + */ +void evdns_search_add(const char *domain); + +/** + Set the 'ndots' parameter for searches. + + Sets the number of dots which, when found in a name, causes + the first query to be without any search domain. + + @deprecated This function is deprecated because it does not allow the + caller to specify which evdns_base it applies to. The recommended + function is evdns_base_search_ndots_set(). + + @param ndots the new ndots parameter + */ +void evdns_search_ndots_set(const int ndots); + +/** + As evdns_server_new_with_base. + + @deprecated This function is deprecated because it does not allow the + caller to specify which even_base it uses. The recommended + function is evdns_add_server_port_with_base(). + +*/ +struct evdns_server_port *evdns_add_server_port(evutil_socket_t socket, int flags, evdns_request_callback_fn_type callback, void *user_data); + +#ifdef _WIN32 +int evdns_config_windows_nameservers(void); +#define EVDNS_CONFIG_WINDOWS_NAMESERVERS_IMPLEMENTED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/dns_struct.h b/src/inc_for_view/event2/dns_struct.h new file mode 100644 index 0000000..593a8a7 --- /dev/null +++ b/src/inc_for_view/event2/dns_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_DNS_STRUCT_H_INCLUDED_ +#define EVENT2_DNS_STRUCT_H_INCLUDED_ + +/** @file event2/dns_struct.h + + Data structures for dns. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/* + * Structures used to implement a DNS server. + */ + +struct evdns_server_request { + int flags; + int nquestions; + struct evdns_server_question **questions; +}; +struct evdns_server_question { + int type; +#ifdef __cplusplus + int dns_question_class; +#else + /* You should refer to this field as "dns_question_class". The + * name "class" works in C for backward compatibility, and will be + * removed in a future version. (1.5 or later). */ + int class; +#define dns_question_class class +#endif + char name[1]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_DNS_STRUCT_H_INCLUDED_ */ + diff --git a/src/inc_for_view/event2/event.h b/src/inc_for_view/event2/event.h new file mode 100644 index 0000000..6e0a4f0 --- /dev/null +++ b/src/inc_for_view/event2/event.h @@ -0,0 +1,1675 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_EVENT_H_INCLUDED_ +#define EVENT2_EVENT_H_INCLUDED_ + +/** + @mainpage + + @section intro Introduction + + Libevent is an event notification library for developing scalable network + servers. The Libevent API provides a mechanism to execute a callback + function when a specific event occurs on a file descriptor or after a + timeout has been reached. Furthermore, Libevent also support callbacks due + to signals or regular timeouts. + + Libevent is meant to replace the event loop found in event driven network + servers. An application just needs to call event_base_dispatch() and then add or + remove events dynamically without having to change the event loop. + + + Currently, Libevent supports /dev/poll, kqueue(2), select(2), poll(2), + epoll(4), and evports. The internal event mechanism is completely + independent of the exposed event API, and a simple update of Libevent can + provide new functionality without having to redesign the applications. As a + result, Libevent allows for portable application development and provides + the most scalable event notification mechanism available on an operating + system. Libevent can also be used for multithreaded programs. Libevent + should compile on Linux, *BSD, Mac OS X, Solaris and, Windows. + + @section usage Standard usage + + Every program that uses Libevent must include the + header, and pass the -levent flag to the linker. (You can instead link + -levent_core if you only want the main event and buffered IO-based code, + and don't want to link any protocol code.) + + @section setup Library setup + + Before you call any other Libevent functions, you need to set up the + library. If you're going to use Libevent from multiple threads in a + multithreaded application, you need to initialize thread support -- + typically by using evthread_use_pthreads() or + evthread_use_windows_threads(). See for more + information. + + This is also the point where you can replace Libevent's memory + management functions with event_set_mem_functions, and enable debug mode + with event_enable_debug_mode(). + + @section base Creating an event base + + Next, you need to create an event_base structure, using event_base_new() + or event_base_new_with_config(). The event_base is responsible for + keeping track of which events are "pending" (that is to say, being + watched to see if they become active) and which events are "active". + Every event is associated with a single event_base. + + @section event Event notification + + For each file descriptor that you wish to monitor, you must create an + event structure with event_new(). (You may also declare an event + structure and call event_assign() to initialize the members of the + structure.) To enable notification, you add the structure to the list + of monitored events by calling event_add(). The event structure must + remain allocated as long as it is active, so it should generally be + allocated on the heap. + + @section loop Dispatching events. + + Finally, you call event_base_dispatch() to loop and dispatch events. + You can also use event_base_loop() for more fine-grained control. + + Currently, only one thread can be dispatching a given event_base at a + time. If you want to run events in multiple threads at once, you can + either have a single event_base whose events add work to a work queue, + or you can create multiple event_base objects. + + @section bufferevent I/O Buffers + + Libevent provides a buffered I/O abstraction on top of the regular event + callbacks. This abstraction is called a bufferevent. A bufferevent + provides input and output buffers that get filled and drained + automatically. The user of a buffered event no longer deals directly + with the I/O, but instead is reading from input and writing to output + buffers. + + Once initialized via bufferevent_socket_new(), the bufferevent structure + can be used repeatedly with bufferevent_enable() and + bufferevent_disable(). Instead of reading and writing directly to a + socket, you would call bufferevent_read() and bufferevent_write(). + + When read enabled the bufferevent will try to read from the file descriptor + and call the read callback. The write callback is executed whenever the + output buffer is drained below the write low watermark, which is 0 by + default. + + See for more information. + + @section timers Timers + + Libevent can also be used to create timers that invoke a callback after a + certain amount of time has expired. The evtimer_new() macro returns + an event struct to use as a timer. To activate the timer, call + evtimer_add(). Timers can be deactivated by calling evtimer_del(). + (These macros are thin wrappers around event_new(), event_add(), + and event_del(); you can also use those instead.) + + @section evdns Asynchronous DNS resolution + + Libevent provides an asynchronous DNS resolver that should be used instead + of the standard DNS resolver functions. See the + functions for more detail. + + @section evhttp Event-driven HTTP servers + + Libevent provides a very simple event-driven HTTP server that can be + embedded in your program and used to service HTTP requests. + + To use this capability, you need to include the header in your + program. See that header for more information. + + @section evrpc A framework for RPC servers and clients + + Libevent provides a framework for creating RPC servers and clients. It + takes care of marshaling and unmarshaling all data structures. + + @section api API Reference + + To browse the complete documentation of the libevent API, click on any of + the following links. + + event2/event.h + The primary libevent header + + event2/thread.h + Functions for use by multithreaded programs + + event2/buffer.h and event2/bufferevent.h + Buffer management for network reading and writing + + event2/util.h + Utility functions for portable nonblocking network code + + event2/dns.h + Asynchronous DNS resolution + + event2/http.h + An embedded libevent-based HTTP server + + event2/rpc.h + A framework for creating RPC servers and clients + + */ + +/** @file event2/event.h + + Core functions for waiting for and receiving events, and using event bases. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +#include + +/* For int types. */ +#include + +/** + * Structure to hold information and state for a Libevent dispatch loop. + * + * The event_base lies at the center of Libevent; every application will + * have one. It keeps track of all pending and active events, and + * notifies your application of the active ones. + * + * This is an opaque structure; you can allocate one using + * event_base_new() or event_base_new_with_config(). + * + * @see event_base_new(), event_base_free(), event_base_loop(), + * event_base_new_with_config() + */ +struct event_base +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * @struct event + * + * Structure to represent a single event. + * + * An event can have some underlying condition it represents: a socket + * becoming readable or writeable (or both), or a signal becoming raised. + * (An event that represents no underlying condition is still useful: you + * can use one to implement a timer, or to communicate between threads.) + * + * Generally, you can create events with event_new(), then make them + * pending with event_add(). As your event_base runs, it will run the + * callbacks of an events whose conditions are triggered. When you + * longer want the event, free it with event_free(). + * + * In more depth: + * + * An event may be "pending" (one whose condition we are watching), + * "active" (one whose condition has triggered and whose callback is about + * to run), neither, or both. Events come into existence via + * event_assign() or event_new(), and are then neither active nor pending. + * + * To make an event pending, pass it to event_add(). When doing so, you + * can also set a timeout for the event. + * + * Events become active during an event_base_loop() call when either their + * condition has triggered, or when their timeout has elapsed. You can + * also activate an event manually using event_active(). The even_base + * loop will run the callbacks of active events; after it has done so, it + * marks them as no longer active. + * + * You can make an event non-pending by passing it to event_del(). This + * also makes the event non-active. + * + * Events can be "persistent" or "non-persistent". A non-persistent event + * becomes non-pending as soon as it is triggered: thus, it only runs at + * most once per call to event_add(). A persistent event remains pending + * even when it becomes active: you'll need to event_del() it manually in + * order to make it non-pending. When a persistent event with a timeout + * becomes active, its timeout is reset: this means you can use persistent + * events to implement periodic timeouts. + * + * This should be treated as an opaque structure; you should never read or + * write any of its fields directly. For backward compatibility with old + * code, it is defined in the event2/event_struct.h header; including this + * header may make your code incompatible with other versions of Libevent. + * + * @see event_new(), event_free(), event_assign(), event_get_assignment(), + * event_add(), event_del(), event_active(), event_pending(), + * event_get_fd(), event_get_base(), event_get_events(), + * event_get_callback(), event_get_callback_arg(), + * event_priority_set() + */ +struct event +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * Configuration for an event_base. + * + * There are many options that can be used to alter the behavior and + * implementation of an event_base. To avoid having to pass them all in a + * complex many-argument constructor, we provide an abstract data type + * wrhere you set up configation information before passing it to + * event_base_new_with_config(). + * + * @see event_config_new(), event_config_free(), event_base_new_with_config(), + * event_config_avoid_method(), event_config_require_features(), + * event_config_set_flag(), event_config_set_num_cpus_hint() + */ +struct event_config +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +/** + * Enable some relatively expensive debugging checks in Libevent that + * would normally be turned off. Generally, these checks cause code that + * would otherwise crash mysteriously to fail earlier with an assertion + * failure. Note that this method MUST be called before any events or + * event_bases have been created. + * + * Debug mode can currently catch the following errors: + * An event is re-assigned while it is added + * Any function is called on a non-assigned event + * + * Note that debugging mode uses memory to track every event that has been + * initialized (via event_assign, event_set, or event_new) but not yet + * released (via event_free or event_debug_unassign). If you want to use + * debug mode, and you find yourself running out of memory, you will need + * to use event_debug_unassign to explicitly stop tracking events that + * are no longer considered set-up. + * + * @see event_debug_unassign() + */ +EVENT2_EXPORT_SYMBOL +void event_enable_debug_mode(void); + +/** + * When debugging mode is enabled, informs Libevent that an event should no + * longer be considered as assigned. When debugging mode is not enabled, does + * nothing. + * + * This function must only be called on a non-added event. + * + * @see event_enable_debug_mode() + */ +EVENT2_EXPORT_SYMBOL +void event_debug_unassign(struct event *); + +/** + * Create and return a new event_base to use with the rest of Libevent. + * + * @return a new event_base on success, or NULL on failure. + * + * @see event_base_free(), event_base_new_with_config() + */ +EVENT2_EXPORT_SYMBOL +struct event_base *event_base_new(void); + +/** + Reinitialize the event base after a fork + + Some event mechanisms do not survive across fork. The event base needs + to be reinitialized with the event_reinit() function. + + @param base the event base that needs to be re-initialized + @return 0 if successful, or -1 if some events could not be re-added. + @see event_base_new() +*/ +EVENT2_EXPORT_SYMBOL +int event_reinit(struct event_base *base); + +/** + Event dispatching loop + + This loop will run the event base until either there are no more pending or + active, or until something calls event_base_loopbreak() or + event_base_loopexit(). + + @param base the event_base structure returned by event_base_new() or + event_base_new_with_config() + @return 0 if successful, -1 if an error occurred, or 1 if we exited because + no events were pending or active. + @see event_base_loop() + */ +EVENT2_EXPORT_SYMBOL +int event_base_dispatch(struct event_base *); + +/** + Get the kernel event notification mechanism used by Libevent. + + @param eb the event_base structure returned by event_base_new() + @return a string identifying the kernel event mechanism (kqueue, epoll, etc.) + */ +EVENT2_EXPORT_SYMBOL +const char *event_base_get_method(const struct event_base *); + +/** + Gets all event notification mechanisms supported by Libevent. + + This functions returns the event mechanism in order preferred by + Libevent. Note that this list will include all backends that + Libevent has compiled-in support for, and will not necessarily check + your OS to see whether it has the required resources. + + @return an array with pointers to the names of support methods. + The end of the array is indicated by a NULL pointer. If an + error is encountered NULL is returned. +*/ +EVENT2_EXPORT_SYMBOL +const char **event_get_supported_methods(void); + +/** Query the current monotonic time from a the timer for a struct + * event_base. + */ +EVENT2_EXPORT_SYMBOL +int event_gettime_monotonic(struct event_base *base, struct timeval *tp); + +/** + @name event type flag + + Flags to pass to event_base_get_num_events() to specify the kinds of events + we want to aggregate counts for +*/ +/**@{*/ +/** count the number of active events, which have been triggered.*/ +#define EVENT_BASE_COUNT_ACTIVE 1U +/** count the number of virtual events, which is used to represent an internal + * condition, other than a pending event, that keeps the loop from exiting. */ +#define EVENT_BASE_COUNT_VIRTUAL 2U +/** count the number of events which have been added to event base, including + * internal events. */ +#define EVENT_BASE_COUNT_ADDED 4U +/**@}*/ + +/** + Gets the number of events in event_base, as specified in the flags. + + Since event base has some internal events added to make some of its + functionalities work, EVENT_BASE_COUNT_ADDED may return more than the + number of events you added using event_add(). + + If you pass EVENT_BASE_COUNT_ACTIVE and EVENT_BASE_COUNT_ADDED together, an + active event will be counted twice. However, this might not be the case in + future libevent versions. The return value is an indication of the work + load, but the user shouldn't rely on the exact value as this may change in + the future. + + @param eb the event_base structure returned by event_base_new() + @param flags a bitwise combination of the kinds of events to aggregate + counts for + @return the number of events specified in the flags +*/ +EVENT2_EXPORT_SYMBOL +int event_base_get_num_events(struct event_base *, unsigned int); + +/** + Get the maximum number of events in a given event_base as specified in the + flags. + + @param eb the event_base structure returned by event_base_new() + @param flags a bitwise combination of the kinds of events to aggregate + counts for + @param clear option used to reset the maximum count. + @return the number of events specified in the flags + */ +EVENT2_EXPORT_SYMBOL +int event_base_get_max_events(struct event_base *, unsigned int, int); + +/** + Allocates a new event configuration object. + + The event configuration object can be used to change the behavior of + an event base. + + @return an event_config object that can be used to store configuration, or + NULL if an error is encountered. + @see event_base_new_with_config(), event_config_free(), event_config +*/ +EVENT2_EXPORT_SYMBOL +struct event_config *event_config_new(void); + +/** + Deallocates all memory associated with an event configuration object + + @param cfg the event configuration object to be freed. +*/ +EVENT2_EXPORT_SYMBOL +void event_config_free(struct event_config *cfg); + +/** + Enters an event method that should be avoided into the configuration. + + This can be used to avoid event mechanisms that do not support certain + file descriptor types, or for debugging to avoid certain event + mechanisms. An application can make use of multiple event bases to + accommodate incompatible file descriptor types. + + @param cfg the event configuration object + @param method the name of the event method to avoid + @return 0 on success, -1 on failure. +*/ +EVENT2_EXPORT_SYMBOL +int event_config_avoid_method(struct event_config *cfg, const char *method); + +/** + A flag used to describe which features an event_base (must) provide. + + Because of OS limitations, not every Libevent backend supports every + possible feature. You can use this type with + event_config_require_features() to tell Libevent to only proceed if your + event_base implements a given feature, and you can receive this type from + event_base_get_features() to see which features are available. +*/ +enum event_method_feature { + /** Require an event method that allows edge-triggered events with EV_ET. */ + EV_FEATURE_ET = 0x01, + /** Require an event method where having one event triggered among + * many is [approximately] an O(1) operation. This excludes (for + * example) select and poll, which are approximately O(N) for N + * equal to the total number of possible events. */ + EV_FEATURE_O1 = 0x02, + /** Require an event method that allows file descriptors as well as + * sockets. */ + EV_FEATURE_FDS = 0x04, + /** Require an event method that allows you to use EV_CLOSED to detect + * connection close without the necessity of reading all the pending data. + * + * Methods that do support EV_CLOSED may not be able to provide support on + * all kernel versions. + **/ + EV_FEATURE_EARLY_CLOSE = 0x08 +}; + +/** + A flag passed to event_config_set_flag(). + + These flags change the behavior of an allocated event_base. + + @see event_config_set_flag(), event_base_new_with_config(), + event_method_feature + */ +enum event_base_config_flag { + /** Do not allocate a lock for the event base, even if we have + locking set up. + + Setting this option will make it unsafe and nonfunctional to call + functions on the base concurrently from multiple threads. + */ + EVENT_BASE_FLAG_NOLOCK = 0x01, + /** Do not check the EVENT_* environment variables when configuring + an event_base */ + EVENT_BASE_FLAG_IGNORE_ENV = 0x02, + /** Windows only: enable the IOCP dispatcher at startup + + If this flag is set then bufferevent_socket_new() and + evconn_listener_new() will use IOCP-backed implementations + instead of the usual select-based one on Windows. + */ + EVENT_BASE_FLAG_STARTUP_IOCP = 0x04, + /** Instead of checking the current time every time the event loop is + ready to run timeout callbacks, check after each timeout callback. + */ + EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08, + + /** If we are using the epoll backend, this flag says that it is + safe to use Libevent's internal change-list code to batch up + adds and deletes in order to try to do as few syscalls as + possible. Setting this flag can make your code run faster, but + it may trigger a Linux bug: it is not safe to use this flag + if you have any fds cloned by dup() or its variants. Doing so + will produce strange and hard-to-diagnose bugs. + + This flag can also be activated by setting the + EVENT_EPOLL_USE_CHANGELIST environment variable. + + This flag has no effect if you wind up using a backend other than + epoll. + */ + EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, + + /** Ordinarily, Libevent implements its time and timeout code using + the fastest monotonic timer that we have. If this flag is set, + however, we use less efficient more precise timer, assuming one is + present. + */ + EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 +}; + +/** + Return a bitmask of the features implemented by an event base. This + will be a bitwise OR of one or more of the values of + event_method_feature + + @see event_method_feature + */ +EVENT2_EXPORT_SYMBOL +int event_base_get_features(const struct event_base *base); + +/** + Enters a required event method feature that the application demands. + + Note that not every feature or combination of features is supported + on every platform. Code that requests features should be prepared + to handle the case where event_base_new_with_config() returns NULL, as in: +
+     event_config_require_features(cfg, EV_FEATURE_ET);
+     base = event_base_new_with_config(cfg);
+     if (base == NULL) {
+       // We can't get edge-triggered behavior here.
+       event_config_require_features(cfg, 0);
+       base = event_base_new_with_config(cfg);
+     }
+   
+ + @param cfg the event configuration object + @param feature a bitfield of one or more event_method_feature values. + Replaces values from previous calls to this function. + @return 0 on success, -1 on failure. + @see event_method_feature, event_base_new_with_config() +*/ +EVENT2_EXPORT_SYMBOL +int event_config_require_features(struct event_config *cfg, int feature); + +/** + * Sets one or more flags to configure what parts of the eventual event_base + * will be initialized, and how they'll work. + * + * @see event_base_config_flags, event_base_new_with_config() + **/ +EVENT2_EXPORT_SYMBOL +int event_config_set_flag(struct event_config *cfg, int flag); + +/** + * Records a hint for the number of CPUs in the system. This is used for + * tuning thread pools, etc, for optimal performance. In Libevent 2.0, + * it is only on Windows, and only when IOCP is in use. + * + * @param cfg the event configuration object + * @param cpus the number of cpus + * @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus); + +/** + * Record an interval and/or a number of callbacks after which the event base + * should check for new events. By default, the event base will run as many + * events are as activated at the higest activated priority before checking + * for new events. If you configure it by setting max_interval, it will check + * the time after each callback, and not allow more than max_interval to + * elapse before checking for new events. If you configure it by setting + * max_callbacks to a value >= 0, it will run no more than max_callbacks + * callbacks before checking for new events. + * + * This option can decrease the latency of high-priority events, and + * avoid priority inversions where multiple low-priority events keep us from + * polling for high-priority events, but at the expense of slightly decreasing + * the throughput. Use it with caution! + * + * @param cfg The event_base configuration object. + * @param max_interval An interval after which Libevent should stop running + * callbacks and check for more events, or NULL if there should be + * no such interval. + * @param max_callbacks A number of callbacks after which Libevent should + * stop running callbacks and check for more events, or -1 if there + * should be no such limit. + * @param min_priority A priority below which max_interval and max_callbacks + * should not be enforced. If this is set to 0, they are enforced + * for events of every priority; if it's set to 1, they're enforced + * for events of priority 1 and above, and so on. + * @return 0 on success, -1 on failure. + **/ +EVENT2_EXPORT_SYMBOL +int event_config_set_max_dispatch_interval(struct event_config *cfg, + const struct timeval *max_interval, int max_callbacks, + int min_priority); + +/** + Initialize the event API. + + Use event_base_new_with_config() to initialize a new event base, taking + the specified configuration under consideration. The configuration object + can currently be used to avoid certain event notification mechanisms. + + @param cfg the event configuration object + @return an initialized event_base that can be used to registering events, + or NULL if no event base can be created with the requested event_config. + @see event_base_new(), event_base_free(), event_init(), event_assign() +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *event_base_new_with_config(const struct event_config *); + +/** + Deallocate all memory associated with an event_base, and free the base. + + Note that this function will not close any fds or free any memory passed + to event_new as the argument to callback. + + If there are any pending finalizer callbacks, this function will invoke + them. + + @param eb an event_base to be freed + */ +EVENT2_EXPORT_SYMBOL +void event_base_free(struct event_base *); + +/** + As event_free, but do not run finalizers. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + */ +EVENT2_EXPORT_SYMBOL +void event_base_free_nofinalize(struct event_base *); + +/** @name Log severities + */ +/**@{*/ +#define EVENT_LOG_DEBUG 0 +#define EVENT_LOG_MSG 1 +#define EVENT_LOG_WARN 2 +#define EVENT_LOG_ERR 3 +/**@}*/ + +/* Obsolete names: these are deprecated, but older programs might use them. + * They violate the reserved-identifier namespace. */ +#define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG +#define _EVENT_LOG_MSG EVENT_LOG_MSG +#define _EVENT_LOG_WARN EVENT_LOG_WARN +#define _EVENT_LOG_ERR EVENT_LOG_ERR + +/** + A callback function used to intercept Libevent's log messages. + + @see event_set_log_callback + */ +typedef void (*event_log_cb)(int severity, const char *msg); +/** + Redirect Libevent's log messages. + + @param cb a function taking two arguments: an integer severity between + EVENT_LOG_DEBUG and EVENT_LOG_ERR, and a string. If cb is NULL, + then the default log is used. + + NOTE: The function you provide *must not* call any other libevent + functionality. Doing so can produce undefined behavior. + */ +EVENT2_EXPORT_SYMBOL +void event_set_log_callback(event_log_cb cb); + +/** + A function to be called if Libevent encounters a fatal internal error. + + @see event_set_fatal_callback + */ +typedef void (*event_fatal_cb)(int err); + +/** + Override Libevent's behavior in the event of a fatal internal error. + + By default, Libevent will call exit(1) if a programming error makes it + impossible to continue correct operation. This function allows you to supply + another callback instead. Note that if the function is ever invoked, + something is wrong with your program, or with Libevent: any subsequent calls + to Libevent may result in undefined behavior. + + Libevent will (almost) always log an EVENT_LOG_ERR message before calling + this function; look at the last log message to see why Libevent has died. + */ +EVENT2_EXPORT_SYMBOL +void event_set_fatal_callback(event_fatal_cb cb); + +#define EVENT_DBG_ALL 0xffffffffu +#define EVENT_DBG_NONE 0 + +/** + Turn on debugging logs and have them sent to the default log handler. + + This is a global setting; if you are going to call it, you must call this + before any calls that create an event-base. You must call it before any + multithreaded use of Libevent. + + Debug logs are verbose. + + @param which Controls which debug messages are turned on. This option is + unused for now; for forward compatibility, you must pass in the constant + "EVENT_DBG_ALL" to turn debugging logs on, or "EVENT_DBG_NONE" to turn + debugging logs off. + */ +EVENT2_EXPORT_SYMBOL +void event_enable_debug_logging(ev_uint32_t which); + +/** + Associate a different event base with an event. + + The event to be associated must not be currently active or pending. + + @param eb the event base + @param ev the event + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_base_set(struct event_base *, struct event *); + +/** @name Loop flags + + These flags control the behavior of event_base_loop(). + */ +/**@{*/ +/** Block until we have an active event, then exit once all active events + * have had their callbacks run. */ +#define EVLOOP_ONCE 0x01 +/** Do not block: see which events are ready now, run the callbacks + * of the highest-priority ones, then exit. */ +#define EVLOOP_NONBLOCK 0x02 +/** Do not exit the loop because we have no pending events. Instead, keep + * running until event_base_loopexit() or event_base_loopbreak() makes us + * stop. + */ +#define EVLOOP_NO_EXIT_ON_EMPTY 0x04 +/**@}*/ + +/** + Wait for events to become active, and run their callbacks. + + This is a more flexible version of event_base_dispatch(). + + By default, this loop will run the event base until either there are no more + pending or active events, or until something calls event_base_loopbreak() or + event_base_loopexit(). You can override this behavior with the 'flags' + argument. + + @param eb the event_base structure returned by event_base_new() or + event_base_new_with_config() + @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK + @return 0 if successful, -1 if an error occurred, or 1 if we exited because + no events were pending or active. + @see event_base_loopexit(), event_base_dispatch(), EVLOOP_ONCE, + EVLOOP_NONBLOCK + */ +EVENT2_EXPORT_SYMBOL +int event_base_loop(struct event_base *, int); + +/** + Exit the event loop after the specified time + + The next event_base_loop() iteration after the given timer expires will + complete normally (handling all queued events) then exit without + blocking for events again. + + Subsequent invocations of event_base_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @param tv the amount of time after which the loop should terminate, + or NULL to exit after running all currently active events. + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopexit(struct event_base *, const struct timeval *); + +/** + Abort the active event_base_loop() immediately. + + event_base_loop() will abort the loop after the next event is completed; + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "break;" statement. + + Subsequent invocations of event_base_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopexit() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopbreak(struct event_base *); + +/** + Tell the active event_base_loop() to scan for new events immediately. + + Calling this function makes the currently active event_base_loop() + start the loop over again (scanning for new events) after the current + event callback finishes. If the event loop is not running, this + function has no effect. + + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "continue;" statement. + + Subsequent invocations of event loop will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak() + */ +EVENT2_EXPORT_SYMBOL +int event_base_loopcontinue(struct event_base *); + +/** + Checks if the event loop was told to exit by event_base_loopexit(). + + This function will return true for an event_base at every point after + event_loopexit() is called, until the event loop is next entered. + + @param eb the event_base structure returned by event_init() + @return true if event_base_loopexit() was called on this event base, + or 0 otherwise + @see event_base_loopexit() + @see event_base_got_break() + */ +EVENT2_EXPORT_SYMBOL +int event_base_got_exit(struct event_base *); + +/** + Checks if the event loop was told to abort immediately by event_base_loopbreak(). + + This function will return true for an event_base at every point after + event_base_loopbreak() is called, until the event loop is next entered. + + @param eb the event_base structure returned by event_init() + @return true if event_base_loopbreak() was called on this event base, + or 0 otherwise + @see event_base_loopbreak() + @see event_base_got_exit() + */ +EVENT2_EXPORT_SYMBOL +int event_base_got_break(struct event_base *); + +/** + * @name event flags + * + * Flags to pass to event_new(), event_assign(), event_pending(), and + * anything else with an argument of the form "short events" + */ +/**@{*/ +/** Indicates that a timeout has occurred. It's not necessary to pass + * this flag to event_for new()/event_assign() to get a timeout. */ +#define EV_TIMEOUT 0x01 +/** Wait for a socket or FD to become readable */ +#define EV_READ 0x02 +/** Wait for a socket or FD to become writeable */ +#define EV_WRITE 0x04 +/** Wait for a POSIX signal to be raised*/ +#define EV_SIGNAL 0x08 +/** + * Persistent event: won't get removed automatically when activated. + * + * When a persistent event with a timeout becomes activated, its timeout + * is reset to 0. + */ +#define EV_PERSIST 0x10 +/** Select edge-triggered behavior, if supported by the backend. */ +#define EV_ET 0x20 +/** + * If this option is provided, then event_del() will not block in one thread + * while waiting for the event callback to complete in another thread. + * + * To use this option safely, you may need to use event_finalize() or + * event_free_finalize() in order to safely tear down an event in a + * multithreaded application. See those functions for more information. + * + * THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + * BECOMES STABLE. + **/ +#define EV_FINALIZE 0x40 +/** + * Detects connection close events. You can use this to detect when a + * connection has been closed, without having to read all the pending data + * from a connection. + * + * Not all backends support EV_CLOSED. To detect or require it, use the + * feature flag EV_FEATURE_EARLY_CLOSE. + **/ +#define EV_CLOSED 0x80 +/**@}*/ + +/** + @name evtimer_* macros + + Aliases for working with one-shot timer events */ +/**@{*/ +#define evtimer_assign(ev, b, cb, arg) \ + event_assign((ev), (b), -1, 0, (cb), (arg)) +#define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg)) +#define evtimer_add(ev, tv) event_add((ev), (tv)) +#define evtimer_del(ev) event_del(ev) +#define evtimer_pending(ev, tv) event_pending((ev), EV_TIMEOUT, (tv)) +#define evtimer_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + @name evsignal_* macros + + Aliases for working with signal events + */ +/**@{*/ +#define evsignal_add(ev, tv) event_add((ev), (tv)) +#define evsignal_assign(ev, b, x, cb, arg) \ + event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg)) +#define evsignal_new(b, x, cb, arg) \ + event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) +#define evsignal_del(ev) event_del(ev) +#define evsignal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv)) +#define evsignal_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + A callback function for an event. + + It receives three arguments: + + @param fd An fd or signal + @param events One or more EV_* flags + @param arg A user-supplied argument. + + @see event_new() + */ +typedef void (*event_callback_fn)(evutil_socket_t, short, void *); + +/** + Return a value used to specify that the event itself must be used as the callback argument. + + The function event_new() takes a callback argument which is passed + to the event's callback function. To specify that the argument to be + passed to the callback function is the event that event_new() returns, + pass in the return value of event_self_cbarg() as the callback argument + for event_new(). + + For example: +
+      struct event *ev = event_new(base, sock, events, callback, %event_self_cbarg());
+  
+ + For consistency with event_new(), it is possible to pass the return value + of this function as the callback argument for event_assign() – this + achieves the same result as passing the event in directly. + + @return a value to be passed as the callback argument to event_new() or + event_assign(). + @see event_new(), event_assign() + */ +EVENT2_EXPORT_SYMBOL +void *event_self_cbarg(void); + +/** + Allocate and asssign a new event structure, ready to be added. + + The function event_new() returns a new event that can be used in + future calls to event_add() and event_del(). The fd and events + arguments determine which conditions will trigger the event; the + callback and callback_arg arguments tell Libevent what to do when the + event becomes active. + + If events contains one of EV_READ, EV_WRITE, or EV_READ|EV_WRITE, then + fd is a file descriptor or socket that should get monitored for + readiness to read, readiness to write, or readiness for either operation + (respectively). If events contains EV_SIGNAL, then fd is a signal + number to wait for. If events contains none of those flags, then the + event can be triggered only by a timeout or by manual activation with + event_active(): In this case, fd must be -1. + + The EV_PERSIST flag can also be passed in the events argument: it makes + event_add() persistent until event_del() is called. + + The EV_ET flag is compatible with EV_READ and EV_WRITE, and supported + only by certain backends. It tells Libevent to use edge-triggered + events. + + The EV_TIMEOUT flag has no effect here. + + It is okay to have multiple events all listening on the same fds; but + they must either all be edge-triggered, or all not be edge triggerd. + + When the event becomes active, the event loop will run the provided + callbuck function, with three arguments. The first will be the provided + fd value. The second will be a bitfield of the events that triggered: + EV_READ, EV_WRITE, or EV_SIGNAL. Here the EV_TIMEOUT flag indicates + that a timeout occurred, and EV_ET indicates that an edge-triggered + event occurred. The third event will be the callback_arg pointer that + you provide. + + @param base the event base to which the event should be attached. + @param fd the file descriptor or signal to be monitored, or -1. + @param events desired events to monitor: bitfield of EV_READ, EV_WRITE, + EV_SIGNAL, EV_PERSIST, EV_ET. + @param callback callback function to be invoked when the event occurs + @param callback_arg an argument to be passed to the callback function + + @return a newly allocated struct event that must later be freed with + event_free(). + @see event_free(), event_add(), event_del(), event_assign() + */ +EVENT2_EXPORT_SYMBOL +struct event *event_new(struct event_base *, evutil_socket_t, short, event_callback_fn, void *); + + +/** + Prepare a new, already-allocated event structure to be added. + + The function event_assign() prepares the event structure ev to be used + in future calls to event_add() and event_del(). Unlike event_new(), it + doesn't allocate memory itself: it requires that you have already + allocated a struct event, probably on the heap. Doing this will + typically make your code depend on the size of the event structure, and + thereby create incompatibility with future versions of Libevent. + + The easiest way to avoid this problem is just to use event_new() and + event_free() instead. + + A slightly harder way to future-proof your code is to use + event_get_struct_event_size() to determine the required size of an event + at runtime. + + Note that it is NOT safe to call this function on an event that is + active or pending. Doing so WILL corrupt internal data structures in + Libevent, and lead to strange, hard-to-diagnose bugs. You _can_ use + event_assign to change an existing event, but only if it is not active + or pending! + + The arguments for this function, and the behavior of the events that it + makes, are as for event_new(). + + @param ev an event struct to be modified + @param base the event base to which ev should be attached. + @param fd the file descriptor to be monitored + @param events desired events to monitor; can be EV_READ and/or EV_WRITE + @param callback callback function to be invoked when the event occurs + @param callback_arg an argument to be passed to the callback function + + @return 0 if success, or -1 on invalid arguments. + + @see event_new(), event_add(), event_del(), event_base_once(), + event_get_struct_event_size() + */ +EVENT2_EXPORT_SYMBOL +int event_assign(struct event *, struct event_base *, evutil_socket_t, short, event_callback_fn, void *); + +/** + Deallocate a struct event * returned by event_new(). + + If the event is pending or active, first make it non-pending and + non-active. + */ +EVENT2_EXPORT_SYMBOL +void event_free(struct event *); + +/** + * Callback type for event_finalize and event_free_finalize(). + * + * THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + * BECOMES STABLE. + * + **/ +typedef void (*event_finalize_callback_fn)(struct event *, void *); +/** + @name Finalization functions + + These functions are used to safely tear down an event in a multithreaded + application. If you construct your events with EV_FINALIZE to avoid + deadlocks, you will need a way to remove an event in the certainty that + it will definitely not be running its callback when you deallocate it + and its callback argument. + + To do this, call one of event_finalize() or event_free_finalize with + 0 for its first argument, the event to tear down as its second argument, + and a callback function as its third argument. The callback will be + invoked as part of the event loop, with the event's priority. + + After you call a finalizer function, event_add() and event_active() will + no longer work on the event, and event_del() will produce a no-op. You + must not try to change the event's fields with event_assign() or + event_set() while the finalize callback is in progress. Once the + callback has been invoked, you should treat the event structure as + containing uninitialized memory. + + The event_free_finalize() function frees the event after it's finalized; + event_finalize() does not. + + A finalizer callback must not make events pending or active. It must not + add events, activate events, or attempt to "resucitate" the event being + finalized in any way. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + + @return 0 on succes, -1 on failure. + */ +/**@{*/ +EVENT2_EXPORT_SYMBOL +int event_finalize(unsigned, struct event *, event_finalize_callback_fn); +EVENT2_EXPORT_SYMBOL +int event_free_finalize(unsigned, struct event *, event_finalize_callback_fn); +/**@}*/ + +/** + Schedule a one-time event + + The function event_base_once() is similar to event_new(). However, it + schedules a callback to be called exactly once, and does not require the + caller to prepare an event structure. + + Note that in Libevent 2.0 and earlier, if the event is never triggered, the + internal memory used to hold it will never be freed. In Libevent 2.1, + the internal memory will get freed by event_base_free() if the event + is never triggered. The 'arg' value, however, will not get freed in either + case--you'll need to free that on your own if you want it to go away. + + @param base an event_base + @param fd a file descriptor to monitor, or -1 for no fd. + @param events event(s) to monitor; can be any of EV_READ | + EV_WRITE, or EV_TIMEOUT + @param callback callback function to be invoked when the event occurs + @param arg an argument to be passed to the callback function + @param timeout the maximum amount of time to wait for the event. NULL + makes an EV_READ/EV_WRITE event make forever; NULL makes an + EV_TIMEOUT event succees immediately. + @return 0 if successful, or -1 if an error occurred + */ +EVENT2_EXPORT_SYMBOL +int event_base_once(struct event_base *, evutil_socket_t, short, event_callback_fn, void *, const struct timeval *); + +/** + Add an event to the set of pending events. + + The function event_add() schedules the execution of the event 'ev' when the + condition specified by event_assign() or event_new() occurs, or when the time + specified in timeout has elapesed. If atimeout is NULL, no timeout + occurs and the function will only be + called if a matching event occurs. The event in the + ev argument must be already initialized by event_assign() or event_new() + and may not be used + in calls to event_assign() until it is no longer pending. + + If the event in the ev argument already has a scheduled timeout, calling + event_add() replaces the old timeout with the new one if tv is non-NULL. + + @param ev an event struct initialized via event_assign() or event_new() + @param timeout the maximum amount of time to wait for the event, or NULL + to wait forever + @return 0 if successful, or -1 if an error occurred + @see event_del(), event_assign(), event_new() + */ +EVENT2_EXPORT_SYMBOL +int event_add(struct event *ev, const struct timeval *timeout); + +/** + Remove a timer from a pending event without removing the event itself. + + If the event has a scheduled timeout, this function unschedules it but + leaves the event otherwise pending. + + @param ev an event struct initialized via event_assign() or event_new() + @return 0 on success, or -1 if an error occurrect. +*/ +EVENT2_EXPORT_SYMBOL +int event_remove_timer(struct event *ev); + +/** + Remove an event from the set of monitored events. + + The function event_del() will cancel the event in the argument ev. If the + event has already executed or has never been added the call will have no + effect. + + @param ev an event struct to be removed from the working set + @return 0 if successful, or -1 if an error occurred + @see event_add() + */ +EVENT2_EXPORT_SYMBOL +int event_del(struct event *); + +/** + As event_del(), but never blocks while the event's callback is running + in another thread, even if the event was constructed without the + EV_FINALIZE flag. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + */ +EVENT2_EXPORT_SYMBOL +int event_del_noblock(struct event *ev); +/** + As event_del(), but always blocks while the event's callback is running + in another thread, even if the event was constructed with the + EV_FINALIZE flag. + + THIS IS AN EXPERIMENTAL API. IT MIGHT CHANGE BEFORE THE LIBEVENT 2.1 SERIES + BECOMES STABLE. + */ +EVENT2_EXPORT_SYMBOL +int event_del_block(struct event *ev); + +/** + Make an event active. + + You can use this function on a pending or a non-pending event to make it + active, so that its callback will be run by event_base_dispatch() or + event_base_loop(). + + One common use in multithreaded programs is to wake the thread running + event_base_loop() from another thread. + + @param ev an event to make active. + @param res a set of flags to pass to the event's callback. + @param ncalls an obsolete argument: this is ignored. + **/ +EVENT2_EXPORT_SYMBOL +void event_active(struct event *ev, int res, short ncalls); + +/** + Checks if a specific event is pending or scheduled. + + @param ev an event struct previously passed to event_add() + @param events the requested event type; any of EV_TIMEOUT|EV_READ| + EV_WRITE|EV_SIGNAL + @param tv if this field is not NULL, and the event has a timeout, + this field is set to hold the time at which the timeout will + expire. + + @return true if the event is pending on any of the events in 'what', (that + is to say, it has been added), or 0 if the event is not added. + */ +EVENT2_EXPORT_SYMBOL +int event_pending(const struct event *ev, short events, struct timeval *tv); + +/** + If called from within the callback for an event, returns that event. + + The behavior of this function is not defined when called from outside the + callback function for an event. + */ +EVENT2_EXPORT_SYMBOL +struct event *event_base_get_running_event(struct event_base *base); + +/** + Test if an event structure might be initialized. + + The event_initialized() function can be used to check if an event has been + initialized. + + Warning: This function is only useful for distinguishing a a zeroed-out + piece of memory from an initialized event, it can easily be confused by + uninitialized memory. Thus, it should ONLY be used to distinguish an + initialized event from zero. + + @param ev an event structure to be tested + @return 1 if the structure might be initialized, or 0 if it has not been + initialized + */ +EVENT2_EXPORT_SYMBOL +int event_initialized(const struct event *ev); + +/** + Get the signal number assigned to a signal event +*/ +#define event_get_signal(ev) ((int)event_get_fd(ev)) + +/** + Get the socket or signal assigned to an event, or -1 if the event has + no socket. +*/ +EVENT2_EXPORT_SYMBOL +evutil_socket_t event_get_fd(const struct event *ev); + +/** + Get the event_base associated with an event. +*/ +EVENT2_EXPORT_SYMBOL +struct event_base *event_get_base(const struct event *ev); + +/** + Return the events (EV_READ, EV_WRITE, etc) assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +short event_get_events(const struct event *ev); + +/** + Return the callback assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +event_callback_fn event_get_callback(const struct event *ev); + +/** + Return the callback argument assigned to an event. +*/ +EVENT2_EXPORT_SYMBOL +void *event_get_callback_arg(const struct event *ev); + +/** + Return the priority of an event. + @see event_priority_init(), event_get_priority() +*/ +EVENT2_EXPORT_SYMBOL +int event_get_priority(const struct event *ev); + +/** + Extract _all_ of arguments given to construct a given event. The + event_base is copied into *base_out, the fd is copied into *fd_out, and so + on. + + If any of the "_out" arguments is NULL, it will be ignored. + */ +EVENT2_EXPORT_SYMBOL +void event_get_assignment(const struct event *event, + struct event_base **base_out, evutil_socket_t *fd_out, short *events_out, + event_callback_fn *callback_out, void **arg_out); + +/** + Return the size of struct event that the Libevent library was compiled + with. + + This will be NO GREATER than sizeof(struct event) if you're running with + the same version of Libevent that your application was built with, but + otherwise might not. + + Note that it might be SMALLER than sizeof(struct event) if some future + version of Libevent adds extra padding to the end of struct event. + We might do this to help ensure ABI-compatibility between different + versions of Libevent. + */ +EVENT2_EXPORT_SYMBOL +size_t event_get_struct_event_size(void); + +/** + Get the Libevent version. + + Note that this will give you the version of the library that you're + currently linked against, not the version of the headers that you've + compiled against. + + @return a string containing the version number of Libevent +*/ +EVENT2_EXPORT_SYMBOL +const char *event_get_version(void); + +/** + Return a numeric representation of Libevent's version. + + Note that this will give you the version of the library that you're + currently linked against, not the version of the headers you've used to + compile. + + The format uses one byte each for the major, minor, and patchlevel parts of + the version number. The low-order byte is unused. For example, version + 2.0.1-alpha has a numeric representation of 0x02000100 +*/ +EVENT2_EXPORT_SYMBOL +ev_uint32_t event_get_version_number(void); + +/** As event_get_version, but gives the version of Libevent's headers. */ +#define LIBEVENT_VERSION EVENT__VERSION +/** As event_get_version_number, but gives the version number of Libevent's + * headers. */ +#define LIBEVENT_VERSION_NUMBER EVENT__NUMERIC_VERSION + +/** Largest number of priorities that Libevent can support. */ +#define EVENT_MAX_PRIORITIES 256 +/** + Set the number of different event priorities + + By default Libevent schedules all active events with the same priority. + However, some time it is desirable to process some events with a higher + priority than others. For that reason, Libevent supports strict priority + queues. Active events with a lower priority are always processed before + events with a higher priority. + + The number of different priorities can be set initially with the + event_base_priority_init() function. This function should be called + before the first call to event_base_dispatch(). The + event_priority_set() function can be used to assign a priority to an + event. By default, Libevent assigns the middle priority to all events + unless their priority is explicitly set. + + Note that urgent-priority events can starve less-urgent events: after + running all urgent-priority callbacks, Libevent checks for more urgent + events again, before running less-urgent events. Less-urgent events + will not have their callbacks run until there are no events more urgent + than them that want to be active. + + @param eb the event_base structure returned by event_base_new() + @param npriorities the maximum number of priorities + @return 0 if successful, or -1 if an error occurred + @see event_priority_set() + */ +EVENT2_EXPORT_SYMBOL +int event_base_priority_init(struct event_base *, int); + +/** + Get the number of different event priorities. + + @param eb the event_base structure returned by event_base_new() + @return Number of different event priorities + @see event_base_priority_init() +*/ +EVENT2_EXPORT_SYMBOL +int event_base_get_npriorities(struct event_base *eb); + +/** + Assign a priority to an event. + + @param ev an event struct + @param priority the new priority to be assigned + @return 0 if successful, or -1 if an error occurred + @see event_priority_init(), event_get_priority() + */ +EVENT2_EXPORT_SYMBOL +int event_priority_set(struct event *, int); + +/** + Prepare an event_base to use a large number of timeouts with the same + duration. + + Libevent's default scheduling algorithm is optimized for having a large + number of timeouts with their durations more or less randomly + distributed. But if you have a large number of timeouts that all have + the same duration (for example, if you have a large number of + connections that all have a 10-second timeout), then you can improve + Libevent's performance by telling Libevent about it. + + To do this, call this function with the common duration. It will return a + pointer to a different, opaque timeout value. (Don't depend on its actual + contents!) When you use this timeout value in event_add(), Libevent will + schedule the event more efficiently. + + (This optimization probably will not be worthwhile until you have thousands + or tens of thousands of events with the same timeout.) + */ +EVENT2_EXPORT_SYMBOL +const struct timeval *event_base_init_common_timeout(struct event_base *base, + const struct timeval *duration); + +#if !defined(EVENT__DISABLE_MM_REPLACEMENT) || defined(EVENT_IN_DOXYGEN_) +/** + Override the functions that Libevent uses for memory management. + + Usually, Libevent uses the standard libc functions malloc, realloc, and + free to allocate memory. Passing replacements for those functions to + event_set_mem_functions() overrides this behavior. + + Note that all memory returned from Libevent will be allocated by the + replacement functions rather than by malloc() and realloc(). Thus, if you + have replaced those functions, it will not be appropriate to free() memory + that you get from Libevent. Instead, you must use the free_fn replacement + that you provided. + + Note also that if you are going to call this function, you should do so + before any call to any Libevent function that does allocation. + Otherwise, those funtions will allocate their memory using malloc(), but + then later free it using your provided free_fn. + + @param malloc_fn A replacement for malloc. + @param realloc_fn A replacement for realloc + @param free_fn A replacement for free. + **/ +EVENT2_EXPORT_SYMBOL +void event_set_mem_functions( + void *(*malloc_fn)(size_t sz), + void *(*realloc_fn)(void *ptr, size_t sz), + void (*free_fn)(void *ptr)); +/** This definition is present if Libevent was built with support for + event_set_mem_functions() */ +#define EVENT_SET_MEM_FUNCTIONS_IMPLEMENTED +#endif + +/** + Writes a human-readable description of all inserted and/or active + events to a provided stdio stream. + + This is intended for debugging; its format is not guaranteed to be the same + between libevent versions. + + @param base An event_base on which to scan the events. + @param output A stdio file to write on. + */ +EVENT2_EXPORT_SYMBOL +void event_base_dump_events(struct event_base *, FILE *); + + +/** + Activates all pending events for the given fd and event mask. + + This function activates pending events only. Events which have not been + added will not become active. + + @param base the event_base on which to activate the events. + @param fd An fd to active events on. + @param events One or more of EV_{READ,WRITE}. + */ +EVENT2_EXPORT_SYMBOL +void event_base_active_by_fd(struct event_base *base, evutil_socket_t fd, short events); + +/** + Activates all pending signals with a given signal number + + This function activates pending events only. Events which have not been + added will not become active. + + @param base the event_base on which to activate the events. + @param fd The signal to active events on. + */ +EVENT2_EXPORT_SYMBOL +void event_base_active_by_signal(struct event_base *base, int sig); + +/** + * Callback for iterating events in an event base via event_base_foreach_event + */ +typedef int (*event_base_foreach_event_cb)(const struct event_base *, const struct event *, void *); + +/** + Iterate over all added or active events events in an event loop, and invoke + a given callback on each one. + + The callback must not call any function that modifies the event base, that + modifies any event in the event base, or that adds or removes any event to + the event base. Doing so is unsupported and will lead to undefined + behavior -- likely, to crashes. + + event_base_foreach_event() holds a lock on the event_base() for the whole + time it's running: slow callbacks are not advisable. + + Note that Libevent adds some events of its own to make pieces of its + functionality work. You must not assume that the only events you'll + encounter will be the ones you added yourself. + + The callback function must return 0 to continue iteration, or some other + integer to stop iterating. + + @param base An event_base on which to scan the events. + @param fn A callback function to receive the events. + @param arg An argument passed to the callback function. + @return 0 if we iterated over every event, or the value returned by the + callback function if the loop exited early. +*/ +EVENT2_EXPORT_SYMBOL +int event_base_foreach_event(struct event_base *base, event_base_foreach_event_cb fn, void *arg); + + +/** Sets 'tv' to the current time (as returned by gettimeofday()), + looking at the cached value in 'base' if possible, and calling + gettimeofday() or clock_gettime() as appropriate if there is no + cached time. + + Generally, this value will only be cached while actually + processing event callbacks, and may be very inaccuate if your + callbacks take a long time to execute. + + Returns 0 on success, negative on failure. + */ +EVENT2_EXPORT_SYMBOL +int event_base_gettimeofday_cached(struct event_base *base, + struct timeval *tv); + +/** Update cached_tv in the 'base' to the current time + * + * You can use this function is useful for selectively increasing + * the accuracy of the cached time value in 'base' during callbacks + * that take a long time to execute. + * + * This function has no effect if the base is currently not in its + * event loop, or if timeval caching is disabled via + * EVENT_BASE_FLAG_NO_CACHE_TIME. + * + * @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int event_base_update_cache_time(struct event_base *base); + +/** Release up all globally-allocated resources allocated by Libevent. + + This function does not free developer-controlled resources like + event_bases, events, bufferevents, listeners, and so on. It only releases + resources like global locks that there is no other way to free. + + It is not actually necessary to call this function before exit: every + resource that it frees would be released anyway on exit. It mainly exists + so that resource-leak debugging tools don't see Libevent as holding + resources at exit. + + You should only call this function when no other Libevent functions will + be invoked -- e.g., when cleanly exiting a program. + */ +EVENT2_EXPORT_SYMBOL +void libevent_global_shutdown(void); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/event_compat.h b/src/inc_for_view/event2/event_compat.h new file mode 100644 index 0000000..5110175 --- /dev/null +++ b/src/inc_for_view/event2/event_compat.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_EVENT_COMPAT_H_INCLUDED_ +#define EVENT2_EVENT_COMPAT_H_INCLUDED_ + +/** @file event2/event_compat.h + + Potentially non-threadsafe versions of the functions in event.h: provided + only for backwards compatibility. + + In the oldest versions of Libevent, event_base was not a first-class + structure. Instead, there was a single event base that every function + manipulated. Later, when separate event bases were added, the old functions + that didn't take an event_base argument needed to work by manipulating the + "current" event base. This could lead to thread-safety issues, and obscure, + hard-to-diagnose bugs. + + @deprecated All functions in this file are by definition deprecated. + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + Initialize the event API. + + The event API needs to be initialized with event_init() before it can be + used. Sets the global current base that gets used for events that have no + base associated with them. + + @deprecated This function is deprecated because it replaces the "current" + event_base, and is totally unsafe for multithreaded use. The replacement + is event_base_new(). + + @see event_base_set(), event_base_new() + */ +EVENT2_EXPORT_SYMBOL +struct event_base *event_init(void); + +/** + Loop to process events. + + Like event_base_dispatch(), but uses the "current" base. + + @deprecated This function is deprecated because it is easily confused by + multiple calls to event_init(), and because it is not safe for + multithreaded use. The replacement is event_base_dispatch(). + + @see event_base_dispatch(), event_init() + */ +EVENT2_EXPORT_SYMBOL +int event_dispatch(void); + +/** + Handle events. + + This function behaves like event_base_loop(), but uses the "current" base + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loop(). + + @see event_base_loop(), event_init() +*/ +EVENT2_EXPORT_SYMBOL +int event_loop(int); + + +/** + Exit the event loop after the specified time. + + This function behaves like event_base_loopexit(), except that it uses the + "current" base. + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loopexit(). + + @see event_init, event_base_loopexit() + */ +EVENT2_EXPORT_SYMBOL +int event_loopexit(const struct timeval *); + + +/** + Abort the active event_loop() immediately. + + This function behaves like event_base_loopbreakt(), except that it uses the + "current" base. + + @deprecated This function is deprecated because it uses the event base from + the last call to event_init, and is therefore not safe for multithreaded + use. The replacement is event_base_loopbreak(). + + @see event_base_loopbreak(), event_init() + */ +EVENT2_EXPORT_SYMBOL +int event_loopbreak(void); + +/** + Schedule a one-time event to occur. + + @deprecated This function is obsolete, and has been replaced by + event_base_once(). Its use is deprecated because it relies on the + "current" base configured by event_init(). + + @see event_base_once() + */ +EVENT2_EXPORT_SYMBOL +int event_once(evutil_socket_t , short, + void (*)(evutil_socket_t, short, void *), void *, const struct timeval *); + + +/** + Get the kernel event notification mechanism used by Libevent. + + @deprecated This function is obsolete, and has been replaced by + event_base_get_method(). Its use is deprecated because it relies on the + "current" base configured by event_init(). + + @see event_base_get_method() + */ +EVENT2_EXPORT_SYMBOL +const char *event_get_method(void); + + +/** + Set the number of different event priorities. + + @deprecated This function is deprecated because it is easily confused by + multiple calls to event_init(), and because it is not safe for + multithreaded use. The replacement is event_base_priority_init(). + + @see event_base_priority_init() + */ +EVENT2_EXPORT_SYMBOL +int event_priority_init(int); + +/** + Prepare an event structure to be added. + + @deprecated event_set() is not recommended for new code, because it requires + a subsequent call to event_base_set() to be safe under most circumstances. + Use event_assign() or event_new() instead. + */ +EVENT2_EXPORT_SYMBOL +void event_set(struct event *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *); + +#define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) +#define evsignal_set(ev, x, cb, arg) \ + event_set((ev), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) + + +/** + @name timeout_* macros + + @deprecated These macros are deprecated because their naming is inconsistent + with the rest of Libevent. Use the evtimer_* macros instead. + @{ + */ +#define timeout_add(ev, tv) event_add((ev), (tv)) +#define timeout_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) +#define timeout_del(ev) event_del(ev) +#define timeout_pending(ev, tv) event_pending((ev), EV_TIMEOUT, (tv)) +#define timeout_initialized(ev) event_initialized(ev) +/**@}*/ + +/** + @name signal_* macros + + @deprecated These macros are deprecated because their naming is inconsistent + with the rest of Libevent. Use the evsignal_* macros instead. + @{ + */ +#define signal_add(ev, tv) event_add((ev), (tv)) +#define signal_set(ev, x, cb, arg) \ + event_set((ev), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) +#define signal_del(ev) event_del(ev) +#define signal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv)) +#define signal_initialized(ev) event_initialized(ev) +/**@}*/ + +#ifndef EVENT_FD +/* These macros are obsolete; use event_get_fd and event_get_signal instead. */ +#define EVENT_FD(ev) ((int)event_get_fd(ev)) +#define EVENT_SIGNAL(ev) event_get_signal(ev) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/event_struct.h b/src/inc_for_view/event2/event_struct.h new file mode 100644 index 0000000..1c8b71b --- /dev/null +++ b/src/inc_for_view/event2/event_struct.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_EVENT_STRUCT_H_INCLUDED_ +#define EVENT2_EVENT_STRUCT_H_INCLUDED_ + +/** @file event2/event_struct.h + + Structures used by event.h. Using these structures directly WILL harm + forward compatibility: be careful. + + No field declared in this file should be used directly in user code. Except + for historical reasons, these fields would not be exposed at all. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/* For evkeyvalq */ +#include + +#define EVLIST_TIMEOUT 0x01 +#define EVLIST_INSERTED 0x02 +#define EVLIST_SIGNAL 0x04 +#define EVLIST_ACTIVE 0x08 +#define EVLIST_INTERNAL 0x10 +#define EVLIST_ACTIVE_LATER 0x20 +#define EVLIST_FINALIZING 0x40 +#define EVLIST_INIT 0x80 + +#define EVLIST_ALL 0xff + +/* Fix so that people don't have to run with */ +#ifndef TAILQ_ENTRY +#define EVENT_DEFINED_TQENTRY_ +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +#ifndef TAILQ_HEAD +#define EVENT_DEFINED_TQHEAD_ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; \ + struct type **tqh_last; \ +} +#endif + +/* Fix so that people don't have to run with */ +#ifndef LIST_ENTRY +#define EVENT_DEFINED_LISTENTRY_ +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} +#endif /* !LIST_ENTRY */ + +#ifndef LIST_HEAD +#define EVENT_DEFINED_LISTHEAD_ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ + } +#endif /* !LIST_HEAD */ + +struct event; + +struct event_callback { + TAILQ_ENTRY(event_callback) evcb_active_next; + short evcb_flags; + ev_uint8_t evcb_pri; /* smaller numbers are higher priority */ + ev_uint8_t evcb_closure; + /* allows us to adopt for different types of events */ + union { + void (*evcb_callback)(evutil_socket_t, short, void *); + void (*evcb_selfcb)(struct event_callback *, void *); + void (*evcb_evfinalize)(struct event *, void *); + void (*evcb_cbfinalize)(struct event_callback *, void *); + } evcb_cb_union; + void *evcb_arg; +}; + +struct event_base; +struct event { + struct event_callback ev_evcallback; + + /* for managing timeouts */ + union { + TAILQ_ENTRY(event) ev_next_with_common_timeout; + int min_heap_idx; + } ev_timeout_pos; + evutil_socket_t ev_fd; + + struct event_base *ev_base; + + union { + /* used for io events */ + struct { + LIST_ENTRY (event) ev_io_next; + struct timeval ev_timeout; + } ev_io; + + /* used by signal events */ + struct { + LIST_ENTRY (event) ev_signal_next; + short ev_ncalls; + /* Allows deletes in callback */ + short *ev_pncalls; + } ev_signal; + } ev_; + + short ev_events; + short ev_res; /* result passed to event callback */ + struct timeval ev_timeout; +}; + +TAILQ_HEAD (event_list, event); + +#ifdef EVENT_DEFINED_TQENTRY_ +#undef TAILQ_ENTRY +#endif + +#ifdef EVENT_DEFINED_TQHEAD_ +#undef TAILQ_HEAD +#endif + +LIST_HEAD (event_dlist, event); + +#ifdef EVENT_DEFINED_LISTENTRY_ +#undef LIST_ENTRY +#endif + +#ifdef EVENT_DEFINED_LISTHEAD_ +#undef LIST_HEAD +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_STRUCT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/http.h b/src/inc_for_view/event2/http.h new file mode 100644 index 0000000..8fb5642 --- /dev/null +++ b/src/inc_for_view/event2/http.h @@ -0,0 +1,1189 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_HTTP_H_INCLUDED_ +#define EVENT2_HTTP_H_INCLUDED_ + +/* For int types. */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* In case we haven't included the right headers yet. */ +struct evbuffer; +struct event_base; +struct bufferevent; +struct evhttp_connection; + +/** @file event2/http.h + * + * Basic support for HTTP serving. + * + * As Libevent is a library for dealing with event notification and most + * interesting applications are networked today, I have often found the + * need to write HTTP code. The following prototypes and definitions provide + * an application with a minimal interface for making HTTP requests and for + * creating a very simple HTTP server. + */ + +/* Response codes */ +#define HTTP_OK 200 /**< request completed ok */ +#define HTTP_NOCONTENT 204 /**< request does not have content */ +#define HTTP_MOVEPERM 301 /**< the uri moved permanently */ +#define HTTP_MOVETEMP 302 /**< the uri moved temporarily */ +#define HTTP_NOTMODIFIED 304 /**< page was not modified from last */ +#define HTTP_BADREQUEST 400 /**< invalid http request was made */ +#define HTTP_NOTFOUND 404 /**< could not find content for uri */ +#define HTTP_BADMETHOD 405 /**< method not allowed for this uri */ +#define HTTP_ENTITYTOOLARGE 413 /**< */ +#define HTTP_EXPECTATIONFAILED 417 /**< we can't handle this expectation */ +#define HTTP_INTERNAL 500 /**< internal error */ +#define HTTP_NOTIMPLEMENTED 501 /**< not implemented */ +#define HTTP_SERVUNAVAIL 503 /**< the server is not available */ + +struct evhttp; +struct evhttp_request; +struct evkeyvalq; +struct evhttp_bound_socket; +struct evconnlistener; +struct evdns_base; + +/** + * Create a new HTTP server. + * + * @param base (optional) the event base to receive the HTTP events + * @return a pointer to a newly initialized evhttp server structure + * @see evhttp_free() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp *evhttp_new(struct event_base *base); + +/** + * Binds an HTTP server on the specified address and port. + * + * Can be called multiple times to bind the same http server + * to multiple different ports. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @return 0 on success, -1 on failure. + * @see evhttp_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +int evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port); + +/** + * Like evhttp_bind_socket(), but returns a handle for referencing the socket. + * + * The returned pointer is not valid after \a http is freed. + * + * @param http a pointer to an evhttp object + * @param address a string containing the IP address to listen(2) on + * @param port the port number to listen on + * @return Handle for the socket on success, NULL on failure. + * @see evhttp_bind_socket(), evhttp_del_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port); + +/** + * Makes an HTTP server accept connections on the specified socket. + * + * This may be useful to create a socket and then fork multiple instances + * of an http server, or when a socket has been communicated via file + * descriptor passing in situations where an http servers does not have + * permissions to bind to a low-numbered port. + * + * Can be called multiple times to have the http server listen to + * multiple different sockets. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is ready for accepting connections + * @return 0 on success, -1 on failure. + * @see evhttp_bind_socket() + */ +EVENT2_EXPORT_SYMBOL +int evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd); + +/** + * Like evhttp_accept_socket(), but returns a handle for referencing the socket. + * + * The returned pointer is not valid after \a http is freed. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is ready for accepting connections + * @return Handle for the socket on success, NULL on failure. + * @see evhttp_accept_socket(), evhttp_del_accept_socket() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd); + +/** + * The most low-level evhttp_bind/accept method: takes an evconnlistener, and + * returns an evhttp_bound_socket. The listener will be freed when the bound + * socket is freed. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_bound_socket *evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener); + +/** + * Return the listener used to implement a bound socket. + */ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound); + +typedef void evhttp_bound_socket_foreach_fn(struct evhttp_bound_socket *, void *); +/** + * Applies the function specified in the first argument to all + * evhttp_bound_sockets associated with "http". The user must not + * attempt to free or remove any connections, sockets or listeners + * in the callback "function". + * + * @param http pointer to an evhttp object + * @param function function to apply to every bound socket + * @param argument pointer value passed to function for every socket iterated + */ +EVENT2_EXPORT_SYMBOL +void evhttp_foreach_bound_socket(struct evhttp *http, evhttp_bound_socket_foreach_fn *function, void *argument); + +/** + * Makes an HTTP server stop accepting connections on the specified socket + * + * This may be useful when a socket has been sent via file descriptor passing + * and is no longer needed by the current process. + * + * If you created this bound socket with evhttp_bind_socket_with_handle or + * evhttp_accept_socket_with_handle, this function closes the fd you provided. + * If you created this bound socket with evhttp_bind_listener, this function + * frees the listener you provided. + * + * \a bound_socket is an invalid pointer after this call returns. + * + * @param http a pointer to an evhttp object + * @param bound_socket a handle returned by evhttp_{bind,accept}_socket_with_handle + * @see evhttp_bind_socket_with_handle(), evhttp_accept_socket_with_handle() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound_socket); + +/** + * Get the raw file descriptor referenced by an evhttp_bound_socket. + * + * @param bound_socket a handle returned by evhttp_{bind,accept}_socket_with_handle + * @return the file descriptor used by the bound socket + * @see evhttp_bind_socket_with_handle(), evhttp_accept_socket_with_handle() + */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t evhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound_socket); + +/** + * Free the previously created HTTP server. + * + * Works only if no requests are currently being served. + * + * @param http the evhttp server object to be freed + * @see evhttp_start() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_free(struct evhttp* http); + +/** XXX Document. */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_size); +/** XXX Document. */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size); + +/** + Set the value to use for the Content-Type header when none was provided. If + the content type string is NULL, the Content-Type header will not be + automatically added. + + @param http the http server on which to set the default content type + @param content_type the value for the Content-Type header +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_default_content_type(struct evhttp *http, + const char *content_type); + +/** + Sets the what HTTP methods are supported in requests accepted by this + server, and passed to user callbacks. + + If not supported they will generate a "405 Method not allowed" response. + + By default this includes the following methods: GET, POST, HEAD, PUT, DELETE + + @param http the http server on which to set the methods + @param methods bit mask constructed from evhttp_cmd_type values +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_allowed_methods(struct evhttp* http, ev_uint16_t methods); + +/** + Set a callback for a specified URI + + @param http the http sever on which to set the callback + @param path the path for which to invoke the callback + @param cb the callback function that gets invoked on requesting path + @param cb_arg an additional context argument for the callback + @return 0 on success, -1 if the callback existed already, -2 on failure +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_set_cb(struct evhttp *http, const char *path, + void (*cb)(struct evhttp_request *, void *), void *cb_arg); + +/** Removes the callback for a specified URI */ +EVENT2_EXPORT_SYMBOL +int evhttp_del_cb(struct evhttp *, const char *); + +/** + Set a callback for all requests that are not caught by specific callbacks + + Invokes the specified callback for all requests that do not match any of + the previously specified request paths. This is catchall for requests not + specifically configured with evhttp_set_cb(). + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke for any unmatched requests + @param arg an context argument for the callback +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_set_gencb(struct evhttp *http, + void (*cb)(struct evhttp_request *, void *), void *arg); + +/** + Set a callback used to create new bufferevents for connections + to a given evhttp object. + + You can use this to override the default bufferevent type -- for example, + to make this evhttp object use SSL bufferevents rather than unencrypted + ones. + + New bufferevents must be allocated with no fd set on them. + + @param http the evhttp server object for which to set the callback + @param cb the callback to invoke for incoming connections + @param arg an context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_bevcb(struct evhttp *http, + struct bufferevent *(*cb)(struct event_base *, void *), void *arg); + +/** + Adds a virtual host to the http server. + + A virtual host is a newly initialized evhttp object that has request + callbacks set on it via evhttp_set_cb() or evhttp_set_gencb(). It + most not have any listing sockets associated with it. + + If the virtual host has not been removed by the time that evhttp_free() + is called on the main http server, it will be automatically freed, too. + + It is possible to have hierarchical vhosts. For example: A vhost + with the pattern *.example.com may have other vhosts with patterns + foo.example.com and bar.example.com associated with it. + + @param http the evhttp object to which to add a virtual host + @param pattern the glob pattern against which the hostname is matched. + The match is case insensitive and follows otherwise regular shell + matching. + @param vhost the virtual host to add the regular http server. + @return 0 on success, -1 on failure + @see evhttp_remove_virtual_host() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_virtual_host(struct evhttp* http, const char *pattern, + struct evhttp* vhost); + +/** + Removes a virtual host from the http server. + + @param http the evhttp object from which to remove the virtual host + @param vhost the virtual host to remove from the regular http server. + @return 0 on success, -1 on failure + @see evhttp_add_virtual_host() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost); + +/** + Add a server alias to an http object. The http object can be a virtual + host or the main server. + + @param http the evhttp object + @param alias the alias to add + @see evhttp_add_remove_alias() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_server_alias(struct evhttp *http, const char *alias); + +/** + Remove a server alias from an http object. + + @param http the evhttp object + @param alias the alias to remove + @see evhttp_add_server_alias() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_server_alias(struct evhttp *http, const char *alias); + +/** + * Set the timeout for an HTTP request. + * + * @param http an evhttp object + * @param timeout_in_secs the timeout, in seconds + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_timeout(struct evhttp *http, int timeout_in_secs); + +/** + * Set the timeout for an HTTP request. + * + * @param http an evhttp object + * @param tv the timeout, or NULL + */ +EVENT2_EXPORT_SYMBOL +void evhttp_set_timeout_tv(struct evhttp *http, const struct timeval* tv); + +/* Read all the clients body, and only after this respond with an error if the + * clients body exceed max_body_size */ +#define EVHTTP_SERVER_LINGERING_CLOSE 0x0001 +/** + * Set connection flags for HTTP server. + * + * @see EVHTTP_SERVER_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_set_flags(struct evhttp *http, int flags); + +/* Request/Response functionality */ + +/** + * Send an HTML error message to the client. + * + * @param req a request object + * @param error the HTTP error code + * @param reason a brief explanation of the error. If this is NULL, we'll + * just use the standard meaning of the error code. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_send_error(struct evhttp_request *req, int error, + const char *reason); + +/** + * Send an HTML reply to the client. + * + * The body of the reply consists of the data in databuf. After calling + * evhttp_send_reply() databuf will be empty, but the buffer is still + * owned by the caller and needs to be deallocated by the caller if + * necessary. + * + * @param req a request object + * @param code the HTTP response code to send + * @param reason a brief message to send with the response code + * @param databuf the body of the response + */ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply(struct evhttp_request *req, int code, + const char *reason, struct evbuffer *databuf); + +/* Low-level response interface, for streaming/chunked replies */ + +/** + Initiate a reply that uses Transfer-Encoding chunked. + + This allows the caller to stream the reply back to the client and is + useful when either not all of the reply data is immediately available + or when sending very large replies. + + The caller needs to supply data chunks with evhttp_send_reply_chunk() + and complete the reply by calling evhttp_send_reply_end(). + + @param req a request object + @param code the HTTP response code to send + @param reason a brief message to send with the response code +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_start(struct evhttp_request *req, int code, + const char *reason); + +/** + Send another data chunk as part of an ongoing chunked reply. + + The reply chunk consists of the data in databuf. After calling + evhttp_send_reply_chunk() databuf will be empty, but the buffer is + still owned by the caller and needs to be deallocated by the caller + if necessary. + + @param req a request object + @param databuf the data chunk to send as part of the reply. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_chunk(struct evhttp_request *req, + struct evbuffer *databuf); + +/** + Send another data chunk as part of an ongoing chunked reply. + + The reply chunk consists of the data in databuf. After calling + evhttp_send_reply_chunk() databuf will be empty, but the buffer is + still owned by the caller and needs to be deallocated by the caller + if necessary. + + @param req a request object + @param databuf the data chunk to send as part of the reply. + @param cb callback funcion + @param call back's argument. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_chunk_with_cb(struct evhttp_request *, struct evbuffer *, + void (*cb)(struct evhttp_connection *, void *), void *arg); + +/** + Complete a chunked reply, freeing the request as appropriate. + + @param req a request object +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_send_reply_end(struct evhttp_request *req); + +/* + * Interfaces for making requests + */ + +/** The different request types supported by evhttp. These are as specified + * in RFC2616, except for PATCH which is specified by RFC5789. + * + * By default, only some of these methods are accepted and passed to user + * callbacks; use evhttp_set_allowed_methods() to change which methods + * are allowed. + */ +enum evhttp_cmd_type { + EVHTTP_REQ_GET = 1 << 0, + EVHTTP_REQ_POST = 1 << 1, + EVHTTP_REQ_HEAD = 1 << 2, + EVHTTP_REQ_PUT = 1 << 3, + EVHTTP_REQ_DELETE = 1 << 4, + EVHTTP_REQ_OPTIONS = 1 << 5, + EVHTTP_REQ_TRACE = 1 << 6, + EVHTTP_REQ_CONNECT = 1 << 7, + EVHTTP_REQ_PATCH = 1 << 8 +}; + +/** a request object can represent either a request or a reply */ +enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE }; + +/** + * Create and return a connection object that can be used to for making HTTP + * requests. The connection object tries to resolve address and establish the + * connection when it is given an http request object. + * + * @param base the event_base to use for handling the connection + * @param dnsbase the dns_base to use for resolving host names; if not + * specified host name resolution will block. + * @param bev a bufferevent to use for connecting to the server; if NULL, a + * socket-based bufferevent will be created. This buffrevent will be freed + * when the connection closes. It must have no fd set on it. + * @param address the address to which to connect + * @param port the port to connect to + * @return an evhttp_connection object that can be used for making requests + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_base_bufferevent_new( + struct event_base *base, struct evdns_base *dnsbase, struct bufferevent* bev, const char *address, ev_uint16_t port); + +/** + * Return the bufferevent that an evhttp_connection is using. + */ +EVENT2_EXPORT_SYMBOL +struct bufferevent* evhttp_connection_get_bufferevent(struct evhttp_connection *evcon); + +/** + * Return the HTTP server associated with this connection, or NULL. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp *evhttp_connection_get_server(struct evhttp_connection *evcon); + +/** + * Creates a new request object that needs to be filled in with the request + * parameters. The callback is executed when the request completed or an + * error occurred. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_request *evhttp_request_new( + void (*cb)(struct evhttp_request *, void *), void *arg); + +/** + * Enable delivery of chunks to requestor. + * @param cb will be called after every read of data with the same argument + * as the completion callback. Will never be called on an empty + * response. May drain the input buffer; it will be drained + * automatically on return. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_chunked_cb(struct evhttp_request *, + void (*cb)(struct evhttp_request *, void *)); + +/** + * Register callback for additional parsing of request headers. + * @param cb will be called after receiving and parsing the full header. + * It allows analyzing the header and possibly closing the connection + * by returning a value < 0. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_header_cb(struct evhttp_request *, + int (*cb)(struct evhttp_request *, void *)); + +/** + * The different error types supported by evhttp + * + * @see evhttp_request_set_error_cb() + */ +enum evhttp_request_error { + /** + * Timeout reached, also @see evhttp_connection_set_timeout() + */ + EVREQ_HTTP_TIMEOUT, + /** + * EOF reached + */ + EVREQ_HTTP_EOF, + /** + * Error while reading header, or invalid header + */ + EVREQ_HTTP_INVALID_HEADER, + /** + * Error encountered while reading or writing + */ + EVREQ_HTTP_BUFFER_ERROR, + /** + * The evhttp_cancel_request() called on this request. + */ + EVREQ_HTTP_REQUEST_CANCEL, + /** + * Body is greater then evhttp_connection_set_max_body_size() + */ + EVREQ_HTTP_DATA_TOO_LONG +}; +/** + * Set a callback for errors + * @see evhttp_request_error for error types. + * + * On error, both the error callback and the regular callback will be called, + * error callback is called before the regular callback. + **/ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_error_cb(struct evhttp_request *, + void (*)(enum evhttp_request_error, void *)); + +/** + * Set a callback to be called on request completion of evhttp_send_* function. + * + * The callback function will be called on the completion of the request after + * the output data has been written and before the evhttp_request object + * is destroyed. This can be useful for tracking resources associated with a + * request (ex: timing metrics). + * + * @param req a request object + * @param cb callback function that will be called on request completion + * @param cb_arg an additional context argument for the callback + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_set_on_complete_cb(struct evhttp_request *req, + void (*cb)(struct evhttp_request *, void *), void *cb_arg); + +/** Frees the request object and removes associated events. */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_free(struct evhttp_request *req); + +/** + * Create and return a connection object that can be used to for making HTTP + * requests. The connection object tries to resolve address and establish the + * connection when it is given an http request object. + * + * @param base the event_base to use for handling the connection + * @param dnsbase the dns_base to use for resolving host names; if not + * specified host name resolution will block. + * @param address the address to which to connect + * @param port the port to connect to + * @return an evhttp_connection object that can be used for making requests + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_connection_base_new( + struct event_base *base, struct evdns_base *dnsbase, + const char *address, ev_uint16_t port); + +/** + * Set family hint for DNS requests. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_family(struct evhttp_connection *evcon, + int family); + +/* reuse connection address on retry */ +#define EVHTTP_CON_REUSE_CONNECTED_ADDR 0x0008 +/* Try to read error, since server may already send and close + * connection, but if at that time we have some data to send then we + * can send get EPIPE and fail, while we can read that HTTP error. */ +#define EVHTTP_CON_READ_ON_WRITE_ERROR 0x0010 +/* @see EVHTTP_SERVER_LINGERING_CLOSE */ +#define EVHTTP_CON_LINGERING_CLOSE 0x0020 +/* Padding for public flags, @see EVHTTP_CON_* in http-internal.h */ +#define EVHTTP_CON_PUBLIC_FLAGS_END 0x100000 +/** + * Set connection flags. + * + * @see EVHTTP_CON_* + * @return 0 on success, otherwise non zero (for example if flag doesn't + * supported). + */ +EVENT2_EXPORT_SYMBOL +int evhttp_connection_set_flags(struct evhttp_connection *evcon, + int flags); + +/** Takes ownership of the request object + * + * Can be used in a request callback to keep onto the request until + * evhttp_request_free() is explicitly called by the user. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_request_own(struct evhttp_request *req); + +/** Returns 1 if the request is owned by the user */ +EVENT2_EXPORT_SYMBOL +int evhttp_request_is_owned(struct evhttp_request *req); + +/** + * Returns the connection object associated with the request or NULL + * + * The user needs to either free the request explicitly or call + * evhttp_send_reply_end(). + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_connection *evhttp_request_get_connection(struct evhttp_request *req); + +/** + * Returns the underlying event_base for this connection + */ +EVENT2_EXPORT_SYMBOL +struct event_base *evhttp_connection_get_base(struct evhttp_connection *req); + +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_max_headers_size(struct evhttp_connection *evcon, + ev_ssize_t new_max_headers_size); + +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_max_body_size(struct evhttp_connection* evcon, + ev_ssize_t new_max_body_size); + +/** Frees an http connection */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_free(struct evhttp_connection *evcon); + +/** Disowns a given connection object + * + * Can be used to tell libevent to free the connection object after + * the last request has completed or failed. + */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_free_on_completion(struct evhttp_connection *evcon); + +/** sets the ip address from which http connections are made */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_local_address(struct evhttp_connection *evcon, + const char *address); + +/** sets the local port from which http connections are made */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_local_port(struct evhttp_connection *evcon, + ev_uint16_t port); + +/** Sets the timeout in seconds for events related to this connection */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_timeout(struct evhttp_connection *evcon, + int timeout_in_secs); + +/** Sets the timeout for events related to this connection. Takes a struct + * timeval. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_timeout_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** Sets the delay before retrying requests on this connection. This is only + * used if evhttp_connection_set_retries is used to make the number of retries + * at least one. Each retry after the first is twice as long as the one before + * it. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_initial_retry_tv(struct evhttp_connection *evcon, + const struct timeval *tv); + +/** Sets the retry limit for this connection - -1 repeats indefinitely */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_retries(struct evhttp_connection *evcon, + int retry_max); + +/** Set a callback for connection close. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_set_closecb(struct evhttp_connection *evcon, + void (*)(struct evhttp_connection *, void *), void *); + +/** Get the remote address and port associated with this connection. */ +EVENT2_EXPORT_SYMBOL +void evhttp_connection_get_peer(struct evhttp_connection *evcon, + char **address, ev_uint16_t *port); + +/** Get the remote address associated with this connection. + * extracted from getpeername() OR from nameserver. + * + * @return NULL if getpeername() return non success, + * or connection is not connected, + * otherwise it return pointer to struct sockaddr_storage */ +EVENT2_EXPORT_SYMBOL +const struct sockaddr* +evhttp_connection_get_addr(struct evhttp_connection *evcon); + +/** + Make an HTTP request over the specified connection. + + The connection gets ownership of the request. On failure, the + request object is no longer valid as it has been freed. + + @param evcon the evhttp_connection object over which to send the request + @param req the previously created and configured request object + @param type the request type EVHTTP_REQ_GET, EVHTTP_REQ_POST, etc. + @param uri the URI associated with the request + @return 0 on success, -1 on failure + @see evhttp_cancel_request() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_make_request(struct evhttp_connection *evcon, + struct evhttp_request *req, + enum evhttp_cmd_type type, const char *uri); + +/** + Cancels a pending HTTP request. + + Cancels an ongoing HTTP request. The callback associated with this request + is not executed and the request object is freed. If the request is + currently being processed, e.g. it is ongoing, the corresponding + evhttp_connection object is going to get reset. + + A request cannot be canceled if its callback has executed already. A request + may be canceled reentrantly from its chunked callback. + + @param req the evhttp_request to cancel; req becomes invalid after this call. +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_cancel_request(struct evhttp_request *req); + +/** + * A structure to hold a parsed URI or Relative-Ref conforming to RFC3986. + */ +struct evhttp_uri; + +/** Returns the request URI */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_request_get_uri(const struct evhttp_request *req); +/** Returns the request URI (parsed) */ +EVENT2_EXPORT_SYMBOL +const struct evhttp_uri *evhttp_request_get_evhttp_uri(const struct evhttp_request *req); +/** Returns the request command */ +EVENT2_EXPORT_SYMBOL +enum evhttp_cmd_type evhttp_request_get_command(const struct evhttp_request *req); + +EVENT2_EXPORT_SYMBOL +int evhttp_request_get_response_code(const struct evhttp_request *req); +EVENT2_EXPORT_SYMBOL +const char * evhttp_request_get_response_code_line(const struct evhttp_request *req); + +/** Returns the input headers */ +EVENT2_EXPORT_SYMBOL +struct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req); +/** Returns the output headers */ +EVENT2_EXPORT_SYMBOL +struct evkeyvalq *evhttp_request_get_output_headers(struct evhttp_request *req); +/** Returns the input buffer */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evhttp_request_get_input_buffer(struct evhttp_request *req); +/** Returns the output buffer */ +EVENT2_EXPORT_SYMBOL +struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req); +/** Returns the host associated with the request. If a client sends an absolute + URI, the host part of that is preferred. Otherwise, the input headers are + searched for a Host: header. NULL is returned if no absolute URI or Host: + header is provided. */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_request_get_host(struct evhttp_request *req); + +/* Interfaces for dealing with HTTP headers */ + +/** + Finds the value belonging to a header. + + @param headers the evkeyvalq object in which to find the header + @param key the name of the header to find + @returns a pointer to the value for the header or NULL if the header + could not be found. + @see evhttp_add_header(), evhttp_remove_header() +*/ +EVENT2_EXPORT_SYMBOL +const char *evhttp_find_header(const struct evkeyvalq *headers, + const char *key); + +/** + Removes a header from a list of existing headers. + + @param headers the evkeyvalq object from which to remove a header + @param key the name of the header to remove + @returns 0 if the header was removed, -1 otherwise. + @see evhttp_find_header(), evhttp_add_header() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_remove_header(struct evkeyvalq *headers, const char *key); + +/** + Adds a header to a list of existing headers. + + @param headers the evkeyvalq object to which to add a header + @param key the name of the header + @param value the value belonging to the header + @returns 0 on success, -1 otherwise. + @see evhttp_find_header(), evhttp_clear_headers() +*/ +EVENT2_EXPORT_SYMBOL +int evhttp_add_header(struct evkeyvalq *headers, const char *key, const char *value); + +/** + Removes all headers from the header list. + + @param headers the evkeyvalq object from which to remove all headers +*/ +EVENT2_EXPORT_SYMBOL +void evhttp_clear_headers(struct evkeyvalq *headers); + +/* Miscellaneous utility functions */ + + +/** + Helper function to encode a string for inclusion in a URI. All + characters are replaced by their hex-escaped (%22) equivalents, + except for characters explicitly unreserved by RFC3986 -- that is, + ASCII alphanumeric characters, hyphen, dot, underscore, and tilde. + + The returned string must be freed by the caller. + + @param str an unencoded string + @return a newly allocated URI-encoded string or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_encode_uri(const char *str); + +/** + As evhttp_encode_uri, but if 'size' is nonnegative, treat the string + as being 'size' bytes long. This allows you to encode strings that + may contain 0-valued bytes. + + The returned string must be freed by the caller. + + @param str an unencoded string + @param size the length of the string to encode, or -1 if the string + is NUL-terminated + @param space_to_plus if true, space characters in 'str' are encoded + as +, not %20. + @return a newly allocate URI-encoded string, or NULL on failure. + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uriencode(const char *str, ev_ssize_t size, int space_to_plus); + +/** + Helper function to sort of decode a URI-encoded string. Unlike + evhttp_get_decoded_uri, it decodes all plus characters that appear + _after_ the first question mark character, but no plusses that occur + before. This is not a good way to decode URIs in whole or in part. + + The returned string must be freed by the caller + + @deprecated This function is deprecated; you probably want to use + evhttp_get_decoded_uri instead. + + @param uri an encoded URI + @return a newly allocated unencoded URI or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_decode_uri(const char *uri); + +/** + Helper function to decode a URI-escaped string or HTTP parameter. + + If 'decode_plus' is 1, then we decode the string as an HTTP parameter + value, and convert all plus ('+') characters to spaces. If + 'decode_plus' is 0, we leave all plus characters unchanged. + + The returned string must be freed by the caller. + + @param uri a URI-encode encoded URI + @param decode_plus determines whether we convert '+' to space. + @param size_out if size_out is not NULL, *size_out is set to the size of the + returned string + @return a newly allocated unencoded URI or NULL on failure + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uridecode(const char *uri, int decode_plus, + size_t *size_out); + +/** + Helper function to parse out arguments in a query. + + Parsing a URI like + + http://foo.com/?q=test&s=some+thing + + will result in two entries in the key value queue. + + The first entry is: key="q", value="test" + The second entry is: key="s", value="some thing" + + @deprecated This function is deprecated as of Libevent 2.0.9. Use + evhttp_uri_parse and evhttp_parse_query_str instead. + + @param uri the request URI + @param headers the head of the evkeyval queue + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evhttp_parse_query(const char *uri, struct evkeyvalq *headers); + +/** + Helper function to parse out arguments from the query portion of an + HTTP URI. + + Parsing a query string like + + q=test&s=some+thing + + will result in two entries in the key value queue. + + The first entry is: key="q", value="test" + The second entry is: key="s", value="some thing" + + @param query_parse the query portion of the URI + @param headers the head of the evkeyval queue + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers); + +/** + * Escape HTML character entities in a string. + * + * Replaces <, >, ", ' and & with <, >, ", + * ' and & correspondingly. + * + * The returned string needs to be freed by the caller. + * + * @param html an unescaped HTML string + * @return an escaped HTML string or NULL on error + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_htmlescape(const char *html); + +/** + * Return a new empty evhttp_uri with no fields set. + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_new(void); + +/** + * Changes the flags set on a given URI. See EVHTTP_URI_* for + * a list of flags. + **/ +EVENT2_EXPORT_SYMBOL +void evhttp_uri_set_flags(struct evhttp_uri *uri, unsigned flags); + +/** Return the scheme of an evhttp_uri, or NULL if there is no scheme has + * been set and the evhttp_uri contains a Relative-Ref. */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_scheme(const struct evhttp_uri *uri); +/** + * Return the userinfo part of an evhttp_uri, or NULL if it has no userinfo + * set. + */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_userinfo(const struct evhttp_uri *uri); +/** + * Return the host part of an evhttp_uri, or NULL if it has no host set. + * The host may either be a regular hostname (conforming to the RFC 3986 + * "regname" production), or an IPv4 address, or the empty string, or a + * bracketed IPv6 address, or a bracketed 'IP-Future' address. + * + * Note that having a NULL host means that the URI has no authority + * section, but having an empty-string host means that the URI has an + * authority section with no host part. For example, + * "mailto:user@example.com" has a host of NULL, but "file:///etc/motd" + * has a host of "". + */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_host(const struct evhttp_uri *uri); +/** Return the port part of an evhttp_uri, or -1 if there is no port set. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_get_port(const struct evhttp_uri *uri); +/** Return the path part of an evhttp_uri, or NULL if it has no path set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_path(const struct evhttp_uri *uri); +/** Return the query part of an evhttp_uri (excluding the leading "?"), or + * NULL if it has no query set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_query(const struct evhttp_uri *uri); +/** Return the fragment part of an evhttp_uri (excluding the leading "#"), + * or NULL if it has no fragment set */ +EVENT2_EXPORT_SYMBOL +const char *evhttp_uri_get_fragment(const struct evhttp_uri *uri); + +/** Set the scheme of an evhttp_uri, or clear the scheme if scheme==NULL. + * Returns 0 on success, -1 if scheme is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme); +/** Set the userinfo of an evhttp_uri, or clear the userinfo if userinfo==NULL. + * Returns 0 on success, -1 if userinfo is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo); +/** Set the host of an evhttp_uri, or clear the host if host==NULL. + * Returns 0 on success, -1 if host is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_host(struct evhttp_uri *uri, const char *host); +/** Set the port of an evhttp_uri, or clear the port if port==-1. + * Returns 0 on success, -1 if port is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_port(struct evhttp_uri *uri, int port); +/** Set the path of an evhttp_uri, or clear the path if path==NULL. + * Returns 0 on success, -1 if path is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_path(struct evhttp_uri *uri, const char *path); +/** Set the query of an evhttp_uri, or clear the query if query==NULL. + * The query should not include a leading "?". + * Returns 0 on success, -1 if query is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_query(struct evhttp_uri *uri, const char *query); +/** Set the fragment of an evhttp_uri, or clear the fragment if fragment==NULL. + * The fragment should not include a leading "#". + * Returns 0 on success, -1 if fragment is not well-formed. */ +EVENT2_EXPORT_SYMBOL +int evhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment); + +/** + * Helper function to parse a URI-Reference as specified by RFC3986. + * + * This function matches the URI-Reference production from RFC3986, + * which includes both URIs like + * + * scheme://[[userinfo]@]foo.com[:port]]/[path][?query][#fragment] + * + * and relative-refs like + * + * [path][?query][#fragment] + * + * Any optional elements portions not present in the original URI are + * left set to NULL in the resulting evhttp_uri. If no port is + * specified, the port is set to -1. + * + * Note that no decoding is performed on percent-escaped characters in + * the string; if you want to parse them, use evhttp_uridecode or + * evhttp_parse_query_str as appropriate. + * + * Note also that most URI schemes will have additional constraints that + * this function does not know about, and cannot check. For example, + * mailto://www.example.com/cgi-bin/fortune.pl is not a reasonable + * mailto url, http://www.example.com:99999/ is not a reasonable HTTP + * URL, and ftp:username@example.com is not a reasonable FTP URL. + * Nevertheless, all of these URLs conform to RFC3986, and this function + * accepts all of them as valid. + * + * @param source_uri the request URI + * @param flags Zero or more EVHTTP_URI_* flags to affect the behavior + * of the parser. + * @return uri container to hold parsed data, or NULL if there is error + * @see evhttp_uri_free() + */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_parse_with_flags(const char *source_uri, + unsigned flags); + +/** Tolerate URIs that do not conform to RFC3986. + * + * Unfortunately, some HTTP clients generate URIs that, according to RFC3986, + * are not conformant URIs. If you need to support these URIs, you can + * do so by passing this flag to evhttp_uri_parse_with_flags. + * + * Currently, these changes are: + *
    + *
  • Nonconformant URIs are allowed to contain otherwise unreasonable + * characters in their path, query, and fragment components. + *
+ */ +#define EVHTTP_URI_NONCONFORMANT 0x01 + +/** Alias for evhttp_uri_parse_with_flags(source_uri, 0) */ +EVENT2_EXPORT_SYMBOL +struct evhttp_uri *evhttp_uri_parse(const char *source_uri); + +/** + * Free all memory allocated for a parsed uri. Only use this for URIs + * generated by evhttp_uri_parse. + * + * @param uri container with parsed data + * @see evhttp_uri_parse() + */ +EVENT2_EXPORT_SYMBOL +void evhttp_uri_free(struct evhttp_uri *uri); + +/** + * Join together the uri parts from parsed data to form a URI-Reference. + * + * Note that no escaping of reserved characters is done on the members + * of the evhttp_uri, so the generated string might not be a valid URI + * unless the members of evhttp_uri are themselves valid. + * + * @param uri container with parsed data + * @param buf destination buffer + * @param limit destination buffer size + * @return an joined uri as string or NULL on error + * @see evhttp_uri_parse() + */ +EVENT2_EXPORT_SYMBOL +char *evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_HTTP_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/http_compat.h b/src/inc_for_view/event2/http_compat.h new file mode 100644 index 0000000..43c2c43 --- /dev/null +++ b/src/inc_for_view/event2/http_compat.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_HTTP_COMPAT_H_INCLUDED_ +#define EVENT2_HTTP_COMPAT_H_INCLUDED_ + +/** @file event2/http_compat.h + + Potentially non-threadsafe versions of the functions in http.h: provided + only for backwards compatibility. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + * Start an HTTP server on the specified address and port + * + * @deprecated It does not allow an event base to be specified + * + * @param address the address to which the HTTP server should be bound + * @param port the port number on which the HTTP server should listen + * @return an struct evhttp object + */ +struct evhttp *evhttp_start(const char *address, ev_uint16_t port); + +/** + * A connection object that can be used to for making HTTP requests. The + * connection object tries to establish the connection when it is given an + * http request object. + * + * @deprecated It does not allow an event base to be specified + */ +struct evhttp_connection *evhttp_connection_new( + const char *address, ev_uint16_t port); + +/** + * Associates an event base with the connection - can only be called + * on a freshly created connection object that has not been used yet. + * + * @deprecated XXXX Why? + */ +void evhttp_connection_set_base(struct evhttp_connection *evcon, + struct event_base *base); + + +/** Returns the request URI */ +#define evhttp_request_uri evhttp_request_get_uri + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/http_struct.h b/src/inc_for_view/event2/http_struct.h new file mode 100644 index 0000000..4bf5b1f --- /dev/null +++ b/src/inc_for_view/event2/http_struct.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_HTTP_STRUCT_H_INCLUDED_ +#define EVENT2_HTTP_STRUCT_H_INCLUDED_ + +/** @file event2/http_struct.h + + Data structures for http. Using these structures may hurt forward + compatibility with later versions of Libevent: be careful! + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +/** + * the request structure that a server receives. + * WARNING: expect this structure to change. I will try to provide + * reasonable accessors. + */ +struct evhttp_request { +#if defined(TAILQ_ENTRY) + TAILQ_ENTRY(evhttp_request) next; +#else +struct { + struct evhttp_request *tqe_next; + struct evhttp_request **tqe_prev; +} next; +#endif + + /* the connection object that this request belongs to */ + struct evhttp_connection *evcon; + int flags; +/** The request obj owns the evhttp connection and needs to free it */ +#define EVHTTP_REQ_OWN_CONNECTION 0x0001 +/** Request was made via a proxy */ +#define EVHTTP_PROXY_REQUEST 0x0002 +/** The request object is owned by the user; the user must free it */ +#define EVHTTP_USER_OWNED 0x0004 +/** The request will be used again upstack; freeing must be deferred */ +#define EVHTTP_REQ_DEFER_FREE 0x0008 +/** The request should be freed upstack */ +#define EVHTTP_REQ_NEEDS_FREE 0x0010 + + struct evkeyvalq *input_headers; + struct evkeyvalq *output_headers; + + /* address of the remote host and the port connection came from */ + char *remote_host; + ev_uint16_t remote_port; + + /* cache of the hostname for evhttp_request_get_host */ + char *host_cache; + + enum evhttp_request_kind kind; + enum evhttp_cmd_type type; + + size_t headers_size; + size_t body_size; + + char *uri; /* uri after HTTP request was parsed */ + struct evhttp_uri *uri_elems; /* uri elements */ + + char major; /* HTTP Major number */ + char minor; /* HTTP Minor number */ + + int response_code; /* HTTP Response code */ + char *response_code_line; /* Readable response */ + + struct evbuffer *input_buffer; /* read data */ + ev_int64_t ntoread; + unsigned chunked:1, /* a chunked request */ + userdone:1; /* the user has sent all data */ + + struct evbuffer *output_buffer; /* outgoing post or data */ + + /* Callback */ + void (*cb)(struct evhttp_request *, void *); + void *cb_arg; + + /* + * Chunked data callback - call for each completed chunk if + * specified. If not specified, all the data is delivered via + * the regular callback. + */ + void (*chunk_cb)(struct evhttp_request *, void *); + + /* + * Callback added for forked-daapd so they can collect ICY + * (shoutcast) metadata from the http header. If return + * int is negative the connection will be closed. + */ + int (*header_cb)(struct evhttp_request *, void *); + + /* + * Error callback - called when error is occured. + * @see evhttp_request_error for error types. + * + * @see evhttp_request_set_error_cb() + */ + void (*error_cb)(enum evhttp_request_error, void *); + + /* + * Send complete callback - called when the request is actually + * sent and completed. + */ + void (*on_complete_cb)(struct evhttp_request *, void *); + void *on_complete_cb_arg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_HTTP_STRUCT_H_INCLUDED_ */ + diff --git a/src/inc_for_view/event2/keyvalq_struct.h b/src/inc_for_view/event2/keyvalq_struct.h new file mode 100644 index 0000000..bffa54b --- /dev/null +++ b/src/inc_for_view/event2/keyvalq_struct.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_KEYVALQ_STRUCT_H_INCLUDED_ +#define EVENT2_KEYVALQ_STRUCT_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Fix so that people don't have to run with */ +/* XXXX This code is duplicated with event_struct.h */ +#ifndef TAILQ_ENTRY +#define EVENT_DEFINED_TQENTRY_ +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +#ifndef TAILQ_HEAD +#define EVENT_DEFINED_TQHEAD_ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; \ + struct type **tqh_last; \ +} +#endif + +/* + * Key-Value pairs. Can be used for HTTP headers but also for + * query argument parsing. + */ +struct evkeyval { + TAILQ_ENTRY(evkeyval) next; + + char *key; + char *value; +}; + +TAILQ_HEAD (evkeyvalq, evkeyval); + +/* XXXX This code is duplicated with event_struct.h */ +#ifdef EVENT_DEFINED_TQENTRY_ +#undef TAILQ_ENTRY +#endif + +#ifdef EVENT_DEFINED_TQHEAD_ +#undef TAILQ_HEAD +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/inc_for_view/event2/listener.h b/src/inc_for_view/event2/listener.h new file mode 100644 index 0000000..84b4da0 --- /dev/null +++ b/src/inc_for_view/event2/listener.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_LISTENER_H_INCLUDED_ +#define EVENT2_LISTENER_H_INCLUDED_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct sockaddr; +struct evconnlistener; + +/** + A callback that we invoke when a listener has a new connection. + + @param listener The evconnlistener + @param fd The new file descriptor + @param addr The source address of the connection + @param socklen The length of addr + @param user_arg the pointer passed to evconnlistener_new() + */ +typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *); + +/** + A callback that we invoke when a listener encounters a non-retriable error. + + @param listener The evconnlistener + @param user_arg the pointer passed to evconnlistener_new() + */ +typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *); + +/** Flag: Indicates that we should not make incoming sockets nonblocking + * before passing them to the callback. */ +#define LEV_OPT_LEAVE_SOCKETS_BLOCKING (1u<<0) +/** Flag: Indicates that freeing the listener should close the underlying + * socket. */ +#define LEV_OPT_CLOSE_ON_FREE (1u<<1) +/** Flag: Indicates that we should set the close-on-exec flag, if possible */ +#define LEV_OPT_CLOSE_ON_EXEC (1u<<2) +/** Flag: Indicates that we should disable the timeout (if any) between when + * this socket is closed and when we can listen again on the same port. */ +#define LEV_OPT_REUSEABLE (1u<<3) +/** Flag: Indicates that the listener should be locked so it's safe to use + * from multiple threadcs at once. */ +#define LEV_OPT_THREADSAFE (1u<<4) +/** Flag: Indicates that the listener should be created in disabled + * state. Use evconnlistener_enable() to enable it later. */ +#define LEV_OPT_DISABLED (1u<<5) +/** Flag: Indicates that the listener should defer accept() until data is + * available, if possible. Ignored on platforms that do not support this. + * + * This option can help performance for protocols where the client transmits + * immediately after connecting. Do not use this option if your protocol + * _doesn't_ start out with the client transmitting data, since in that case + * this option will sometimes cause the kernel to never tell you about the + * connection. + * + * This option is only supported by evconnlistener_new_bind(): it can't + * work with evconnlistener_new_fd(), since the listener needs to be told + * to use the option before it is actually bound. + */ +#define LEV_OPT_DEFERRED_ACCEPT (1u<<6) +/** Flag: Indicates that we ask to allow multiple servers (processes or + * threads) to bind to the same port if they each set the option. + * + * SO_REUSEPORT is what most people would expect SO_REUSEADDR to be, however + * SO_REUSEPORT does not imply SO_REUSEADDR. + * + * This is only available on Linux and kernel 3.9+ + */ +#define LEV_OPT_REUSEABLE_PORT (1u<<7) + +/** + Allocate a new evconnlistener object to listen for incoming TCP connections + on a given file descriptor. + + @param base The event base to associate the listener with. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. + @param ptr A user-supplied pointer to give to the callback. + @param flags Any number of LEV_OPT_* flags + @param backlog Passed to the listen() call to determine the length of the + acceptable connection backlog. Set to -1 for a reasonable default. + Set to 0 if the socket is already listening. + @param fd The file descriptor to listen on. It must be a nonblocking + file descriptor, and it should already be bound to an appropriate + port and address. +*/ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evconnlistener_new(struct event_base *base, + evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, + evutil_socket_t fd); +/** + Allocate a new evconnlistener object to listen for incoming TCP connections + on a given address. + + @param base The event base to associate the listener with. + @param cb A callback to be invoked when a new connection arrives. If the + callback is NULL, the listener will be treated as disabled until the + callback is set. + @param ptr A user-supplied pointer to give to the callback. + @param flags Any number of LEV_OPT_* flags + @param backlog Passed to the listen() call to determine the length of the + acceptable connection backlog. Set to -1 for a reasonable default. + @param addr The address to listen for connections on. + @param socklen The length of the address. + */ +EVENT2_EXPORT_SYMBOL +struct evconnlistener *evconnlistener_new_bind(struct event_base *base, + evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, + const struct sockaddr *sa, int socklen); +/** + Disable and deallocate an evconnlistener. + */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_free(struct evconnlistener *lev); +/** + Re-enable an evconnlistener that has been disabled. + */ +EVENT2_EXPORT_SYMBOL +int evconnlistener_enable(struct evconnlistener *lev); +/** + Stop listening for connections on an evconnlistener. + */ +EVENT2_EXPORT_SYMBOL +int evconnlistener_disable(struct evconnlistener *lev); + +/** Return an evconnlistener's associated event_base. */ +EVENT2_EXPORT_SYMBOL +struct event_base *evconnlistener_get_base(struct evconnlistener *lev); + +/** Return the socket that an evconnlistner is listening on. */ +EVENT2_EXPORT_SYMBOL +evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev); + +/** Change the callback on the listener to cb and its user_data to arg. + */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_set_cb(struct evconnlistener *lev, + evconnlistener_cb cb, void *arg); + +/** Set an evconnlistener's error callback. */ +EVENT2_EXPORT_SYMBOL +void evconnlistener_set_error_cb(struct evconnlistener *lev, + evconnlistener_errorcb errorcb); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/inc_for_view/event2/rpc.h b/src/inc_for_view/event2/rpc.h new file mode 100644 index 0000000..dd43df2 --- /dev/null +++ b/src/inc_for_view/event2/rpc.h @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_RPC_H_INCLUDED_ +#define EVENT2_RPC_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file rpc.h + * + * This header files provides basic support for an RPC server and client. + * + * To support RPCs in a server, every supported RPC command needs to be + * defined and registered. + * + * EVRPC_HEADER(SendCommand, Request, Reply); + * + * SendCommand is the name of the RPC command. + * Request is the name of a structure generated by event_rpcgen.py. + * It contains all parameters relating to the SendCommand RPC. The + * server needs to fill in the Reply structure. + * Reply is the name of a structure generated by event_rpcgen.py. It + * contains the answer to the RPC. + * + * To register an RPC with an HTTP server, you need to first create an RPC + * base with: + * + * struct evrpc_base *base = evrpc_init(http); + * + * A specific RPC can then be registered with + * + * EVRPC_REGISTER(base, SendCommand, Request, Reply, FunctionCB, arg); + * + * when the server receives an appropriately formatted RPC, the user callback + * is invoked. The callback needs to fill in the reply structure. + * + * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg); + * + * To send the reply, call EVRPC_REQUEST_DONE(rpc); + * + * See the regression test for an example. + */ + +/** + Determines if the member has been set in the message + + @param msg the message to inspect + @param member the member variable to test for presences + @return 1 if it's present or 0 otherwise. +*/ +#define EVTAG_HAS(msg, member) \ + ((msg)->member##_set == 1) + +#ifndef EVENT2_RPC_COMPAT_H_INCLUDED_ + +/** + Assigns a value to the member in the message. + + @param msg the message to which to assign a value + @param member the name of the member variable + @param value the value to assign +*/ +#define EVTAG_ASSIGN(msg, member, value) \ + (*(msg)->base->member##_assign)((msg), (value)) +/** + Assigns a value to the member in the message. + + @param msg the message to which to assign a value + @param member the name of the member variable + @param value the value to assign + @param len the length of the value +*/ +#define EVTAG_ASSIGN_WITH_LEN(msg, member, value, len) \ + (*(msg)->base->member##_assign)((msg), (value), (len)) +/** + Returns the value for a member. + + @param msg the message from which to get the value + @param member the name of the member variable + @param pvalue a pointer to the variable to hold the value + @return 0 on success, -1 otherwise. +*/ +#define EVTAG_GET(msg, member, pvalue) \ + (*(msg)->base->member##_get)((msg), (pvalue)) +/** + Returns the value for a member. + + @param msg the message from which to get the value + @param member the name of the member variable + @param pvalue a pointer to the variable to hold the value + @param plen a pointer to the length of the value + @return 0 on success, -1 otherwise. +*/ +#define EVTAG_GET_WITH_LEN(msg, member, pvalue, plen) \ + (*(msg)->base->member##_get)((msg), (pvalue), (plen)) + +#endif /* EVENT2_RPC_COMPAT_H_INCLUDED_ */ + +/** + Adds a value to an array. +*/ +#define EVTAG_ARRAY_ADD_VALUE(msg, member, value) \ + (*(msg)->base->member##_add)((msg), (value)) +/** + Allocates a new entry in the array and returns it. +*/ +#define EVTAG_ARRAY_ADD(msg, member) \ + (*(msg)->base->member##_add)(msg) +/** + Gets a variable at the specified offset from the array. +*/ +#define EVTAG_ARRAY_GET(msg, member, offset, pvalue) \ + (*(msg)->base->member##_get)((msg), (offset), (pvalue)) +/** + Returns the number of entries in the array. +*/ +#define EVTAG_ARRAY_LEN(msg, member) ((msg)->member##_length) + + +struct evbuffer; +struct event_base; +struct evrpc_req_generic; +struct evrpc_request_wrapper; +struct evrpc; + +/** The type of a specific RPC Message + * + * @param rpcname the name of the RPC message + */ +#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname + +struct evhttp_request; +struct evrpc_status; +struct evrpc_hook_meta; + +/** Creates the definitions and prototypes for an RPC + * + * You need to use EVRPC_HEADER to create structures and function prototypes + * needed by the server and client implementation. The structures have to be + * defined in an .rpc file and converted to source code via event_rpcgen.py + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param replystruct the name of the RPC reply structure + * @see EVRPC_GENERATE() + */ +#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \ +EVRPC_STRUCT(rpcname) { \ + struct evrpc_hook_meta *hook_meta; \ + struct reqstruct* request; \ + struct rplystruct* reply; \ + struct evrpc* rpc; \ + struct evhttp_request* http_req; \ + struct evbuffer* rpc_data; \ +}; \ +int evrpc_send_request_##rpcname(struct evrpc_pool *, \ + struct reqstruct *, struct rplystruct *, \ + void (*)(struct evrpc_status *, \ + struct reqstruct *, struct rplystruct *, void *cbarg), \ + void *); + +struct evrpc_pool; + +/** use EVRPC_GENERATE instead */ +struct evrpc_request_wrapper *evrpc_make_request_ctx( + struct evrpc_pool *pool, void *request, void *reply, + const char *rpcname, + void (*req_marshal)(struct evbuffer*, void *), + void (*rpl_clear)(void *), + int (*rpl_unmarshal)(void *, struct evbuffer *), + void (*cb)(struct evrpc_status *, void *, void *, void *), + void *cbarg); + +/** Creates a context structure that contains rpc specific information. + * + * EVRPC_MAKE_CTX is used to populate a RPC specific context that + * contains information about marshaling the RPC data types. + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param replystruct the name of the RPC reply structure + * @param pool the evrpc_pool over which to make the request + * @param request a pointer to the RPC request structure object + * @param reply a pointer to the RPC reply structure object + * @param cb the callback function to call when the RPC has completed + * @param cbarg the argument to supply to the callback + */ +#define EVRPC_MAKE_CTX(rpcname, reqstruct, rplystruct, \ + pool, request, reply, cb, cbarg) \ + evrpc_make_request_ctx(pool, request, reply, \ + #rpcname, \ + (void (*)(struct evbuffer *, void *))reqstruct##_marshal, \ + (void (*)(void *))rplystruct##_clear, \ + (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal, \ + (void (*)(struct evrpc_status *, void *, void *, void *))cb, \ + cbarg) + +/** Generates the code for receiving and sending an RPC message + * + * EVRPC_GENERATE is used to create the code corresponding to sending + * and receiving a particular RPC message + * + * @param rpcname the name of the RPC + * @param reqstruct the name of the RPC request structure + * @param replystruct the name of the RPC reply structure + * @see EVRPC_HEADER() + */ +#define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \ + int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \ + struct reqstruct *request, struct rplystruct *reply, \ + void (*cb)(struct evrpc_status *, \ + struct reqstruct *, struct rplystruct *, void *cbarg), \ + void *cbarg) { \ + return evrpc_send_request_generic(pool, request, reply, \ + (void (*)(struct evrpc_status *, void *, void *, void *))cb, \ + cbarg, \ + #rpcname, \ + (void (*)(struct evbuffer *, void *))reqstruct##_marshal, \ + (void (*)(void *))rplystruct##_clear, \ + (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal); \ +} + +/** Provides access to the HTTP request object underlying an RPC + * + * Access to the underlying http object; can be used to look at headers or + * for getting the remote ip address + * + * @param rpc_req the rpc request structure provided to the server callback + * @return an struct evhttp_request object that can be inspected for + * HTTP headers or sender information. + */ +#define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req + +/** completes the server response to an rpc request */ +void evrpc_request_done(struct evrpc_req_generic *req); + +/** accessors for request and reply */ +void *evrpc_get_request(struct evrpc_req_generic *req); +void *evrpc_get_reply(struct evrpc_req_generic *req); + +/** Creates the reply to an RPC request + * + * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected + * to have been filled in. The request and reply pointers become invalid + * after this call has finished. + * + * @param rpc_req the rpc request structure provided to the server callback + */ +#define EVRPC_REQUEST_DONE(rpc_req) do { \ + struct evrpc_req_generic *req_ = (struct evrpc_req_generic *)(rpc_req); \ + evrpc_request_done(req_); \ +} while (0) + + +struct evrpc_base; +struct evhttp; + +/* functions to start up the rpc system */ + +/** Creates a new rpc base from which RPC requests can be received + * + * @param server a pointer to an existing HTTP server + * @return a newly allocated evrpc_base struct + * @see evrpc_free() + */ +struct evrpc_base *evrpc_init(struct evhttp *server); + +/** + * Frees the evrpc base + * + * For now, you are responsible for making sure that no rpcs are ongoing. + * + * @param base the evrpc_base object to be freed + * @see evrpc_init + */ +void evrpc_free(struct evrpc_base *base); + +/** register RPCs with the HTTP Server + * + * registers a new RPC with the HTTP server, each RPC needs to have + * a unique name under which it can be identified. + * + * @param base the evrpc_base structure in which the RPC should be + * registered. + * @param name the name of the RPC + * @param request the name of the RPC request structure + * @param reply the name of the RPC reply structure + * @param callback the callback that should be invoked when the RPC + * is received. The callback has the following prototype + * void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg) + * @param cbarg an additional parameter that can be passed to the callback. + * The parameter can be used to carry around state. + */ +#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \ + evrpc_register_generic(base, #name, \ + (void (*)(struct evrpc_req_generic *, void *))callback, cbarg, \ + (void *(*)(void *))request##_new, NULL, \ + (void (*)(void *))request##_free, \ + (int (*)(void *, struct evbuffer *))request##_unmarshal, \ + (void *(*)(void *))reply##_new, NULL, \ + (void (*)(void *))reply##_free, \ + (int (*)(void *))reply##_complete, \ + (void (*)(struct evbuffer *, void *))reply##_marshal) + +/** + Low level function for registering an RPC with a server. + + Use EVRPC_REGISTER() instead. + + @see EVRPC_REGISTER() +*/ +int evrpc_register_rpc(struct evrpc_base *, struct evrpc *, + void (*)(struct evrpc_req_generic*, void *), void *); + +/** + * Unregisters an already registered RPC + * + * @param base the evrpc_base object from which to unregister an RPC + * @param name the name of the rpc to unregister + * @return -1 on error or 0 when successful. + * @see EVRPC_REGISTER() + */ +#define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc((base), #name) + +int evrpc_unregister_rpc(struct evrpc_base *base, const char *name); + +/* + * Client-side RPC support + */ + +struct evhttp_connection; +struct evrpc_status; + +/** launches an RPC and sends it to the server + * + * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server. + * + * @param name the name of the RPC + * @param pool the evrpc_pool that contains the connection objects over which + * the request should be sent. + * @param request a pointer to the RPC request structure - it contains the + * data to be sent to the server. + * @param reply a pointer to the RPC reply structure. It is going to be filled + * if the request was answered successfully + * @param cb the callback to invoke when the RPC request has been answered + * @param cbarg an additional argument to be passed to the client + * @return 0 on success, -1 on failure + */ +#define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg) \ + evrpc_send_request_##name((pool), (request), (reply), (cb), (cbarg)) + +/** + Makes an RPC request based on the provided context. + + This is a low-level function and should not be used directly + unless a custom context object is provided. Use EVRPC_MAKE_REQUEST() + instead. + + @param ctx a context from EVRPC_MAKE_CTX() + @returns 0 on success, -1 otherwise. + @see EVRPC_MAKE_REQUEST(), EVRPC_MAKE_CTX() +*/ +int evrpc_make_request(struct evrpc_request_wrapper *ctx); + +/** creates an rpc connection pool + * + * a pool has a number of connections associated with it. + * rpc requests are always made via a pool. + * + * @param base a pointer to an struct event_based object; can be left NULL + * in singled-threaded applications + * @return a newly allocated struct evrpc_pool object + * @see evrpc_pool_free() + */ +struct evrpc_pool *evrpc_pool_new(struct event_base *base); +/** frees an rpc connection pool + * + * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new() + * @see evrpc_pool_new() + */ +void evrpc_pool_free(struct evrpc_pool *pool); + +/** + * Adds a connection over which rpc can be dispatched to the pool. + * + * The connection object must have been newly created. + * + * @param pool the pool to which to add the connection + * @param evcon the connection to add to the pool. + */ +void evrpc_pool_add_connection(struct evrpc_pool *pool, + struct evhttp_connection *evcon); + +/** + * Removes a connection from the pool. + * + * The connection object must have been newly created. + * + * @param pool the pool from which to remove the connection + * @param evcon the connection to remove from the pool. + */ +void evrpc_pool_remove_connection(struct evrpc_pool *pool, + struct evhttp_connection *evcon); + +/** + * Sets the timeout in secs after which a request has to complete. The + * RPC is completely aborted if it does not complete by then. Setting + * the timeout to 0 means that it never timeouts and can be used to + * implement callback type RPCs. + * + * Any connection already in the pool will be updated with the new + * timeout. Connections added to the pool after set_timeout has be + * called receive the pool timeout only if no timeout has been set + * for the connection itself. + * + * @param pool a pointer to a struct evrpc_pool object + * @param timeout_in_secs the number of seconds after which a request should + * timeout and a failure be returned to the callback. + */ +void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs); + +/** + * Hooks for changing the input and output of RPCs; this can be used to + * implement compression, authentication, encryption, ... + */ + +enum EVRPC_HOOK_TYPE { + EVRPC_INPUT, /**< apply the function to an input hook */ + EVRPC_OUTPUT /**< apply the function to an output hook */ +}; + +#ifndef _WIN32 +/** Deprecated alias for EVRPC_INPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define INPUT EVRPC_INPUT +/** Deprecated alias for EVRPC_OUTPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define OUTPUT EVRPC_OUTPUT +#endif + +/** + * Return value from hook processing functions + */ + +enum EVRPC_HOOK_RESULT { + EVRPC_TERMINATE = -1, /**< indicates the rpc should be terminated */ + EVRPC_CONTINUE = 0, /**< continue processing the rpc */ + EVRPC_PAUSE = 1 /**< pause processing request until resumed */ +}; + +/** adds a processing hook to either an rpc base or rpc pool + * + * If a hook returns TERMINATE, the processing is aborted. On CONTINUE, + * the request is immediately processed after the hook returns. If the + * hook returns PAUSE, request processing stops until evrpc_resume_request() + * has been called. + * + * The add functions return handles that can be used for removing hooks. + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param hook_type either INPUT or OUTPUT + * @param cb the callback to call when the hook is activated + * @param cb_arg an additional argument for the callback + * @return a handle to the hook so it can be removed later + * @see evrpc_remove_hook() + */ +void *evrpc_add_hook(void *vbase, + enum EVRPC_HOOK_TYPE hook_type, + int (*cb)(void *, struct evhttp_request *, struct evbuffer *, void *), + void *cb_arg); + +/** removes a previously added hook + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param hook_type either INPUT or OUTPUT + * @param handle a handle returned by evrpc_add_hook() + * @return 1 on success or 0 on failure + * @see evrpc_add_hook() + */ +int evrpc_remove_hook(void *vbase, + enum EVRPC_HOOK_TYPE hook_type, + void *handle); + +/** resume a paused request + * + * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool + * @param ctx the context pointer provided to the original hook call + */ +int +evrpc_resume_request(void *vbase, void *ctx, enum EVRPC_HOOK_RESULT res); + +/** adds meta data to request + * + * evrpc_hook_add_meta() allows hooks to add meta data to a request. for + * a client request, the meta data can be inserted by an outgoing request hook + * and retrieved by the incoming request hook. + * + * @param ctx the context provided to the hook call + * @param key a NUL-terminated c-string + * @param data the data to be associated with the key + * @param data_size the size of the data + */ +void evrpc_hook_add_meta(void *ctx, const char *key, + const void *data, size_t data_size); + +/** retrieves meta data previously associated + * + * evrpc_hook_find_meta() can be used to retrieve meta data associated to a + * request by a previous hook. + * @param ctx the context provided to the hook call + * @param key a NUL-terminated c-string + * @param data pointer to a data pointer that will contain the retrieved data + * @param data_size pointer to the size of the data + * @return 0 on success or -1 on failure + */ +int evrpc_hook_find_meta(void *ctx, const char *key, + void **data, size_t *data_size); + +/** + * returns the connection object associated with the request + * + * @param ctx the context provided to the hook call + * @return a pointer to the evhttp_connection object + */ +struct evhttp_connection *evrpc_hook_get_connection(void *ctx); + +/** + Function for sending a generic RPC request. + + Do not call this function directly, use EVRPC_MAKE_REQUEST() instead. + + @see EVRPC_MAKE_REQUEST() + */ +int evrpc_send_request_generic(struct evrpc_pool *pool, + void *request, void *reply, + void (*cb)(struct evrpc_status *, void *, void *, void *), + void *cb_arg, + const char *rpcname, + void (*req_marshal)(struct evbuffer *, void *), + void (*rpl_clear)(void *), + int (*rpl_unmarshal)(void *, struct evbuffer *)); + +/** + Function for registering a generic RPC with the RPC base. + + Do not call this function directly, use EVRPC_REGISTER() instead. + + @see EVRPC_REGISTER() + */ +int +evrpc_register_generic(struct evrpc_base *base, const char *name, + void (*callback)(struct evrpc_req_generic *, void *), void *cbarg, + void *(*req_new)(void *), void *req_new_arg, void (*req_free)(void *), + int (*req_unmarshal)(void *, struct evbuffer *), + void *(*rpl_new)(void *), void *rpl_new_arg, void (*rpl_free)(void *), + int (*rpl_complete)(void *), + void (*rpl_marshal)(struct evbuffer *, void *)); + +/** accessors for obscure and undocumented functionality */ +struct evrpc_pool* evrpc_request_get_pool(struct evrpc_request_wrapper *ctx); +void evrpc_request_set_pool(struct evrpc_request_wrapper *ctx, + struct evrpc_pool *pool); +void evrpc_request_set_cb(struct evrpc_request_wrapper *ctx, + void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg), + void *cb_arg); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_RPC_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/rpc_compat.h b/src/inc_for_view/event2/rpc_compat.h new file mode 100644 index 0000000..8d8334d --- /dev/null +++ b/src/inc_for_view/event2/rpc_compat.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_RPC_COMPAT_H_INCLUDED_ +#define EVENT2_RPC_COMPAT_H_INCLUDED_ + +/** @file event2/rpc_compat.h + + Deprecated versions of the functions in rpc.h: provided only for + backwards compatibility. + + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** backwards compatible accessors that work only with gcc */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) + +#undef EVTAG_ASSIGN +#undef EVTAG_GET +#undef EVTAG_ADD + +#define EVTAG_ASSIGN(msg, member, args...) \ + (*(msg)->base->member##_assign)(msg, ## args) +#define EVTAG_GET(msg, member, args...) \ + (*(msg)->base->member##_get)(msg, ## args) +#define EVTAG_ADD(msg, member, args...) \ + (*(msg)->base->member##_add)(msg, ## args) +#endif +#define EVTAG_LEN(msg, member) ((msg)->member##_length) + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_EVENT_COMPAT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/rpc_struct.h b/src/inc_for_view/event2/rpc_struct.h new file mode 100644 index 0000000..8f691f4 --- /dev/null +++ b/src/inc_for_view/event2/rpc_struct.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_RPC_STRUCT_H_INCLUDED_ +#define EVENT2_RPC_STRUCT_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file event2/rpc_struct.h + + Structures used by rpc.h. Using these structures directly may harm + forward compatibility: be careful! + + */ + +/** + * provides information about the completed RPC request. + */ +struct evrpc_status { +#define EVRPC_STATUS_ERR_NONE 0 +#define EVRPC_STATUS_ERR_TIMEOUT 1 +#define EVRPC_STATUS_ERR_BADPAYLOAD 2 +#define EVRPC_STATUS_ERR_UNSTARTED 3 +#define EVRPC_STATUS_ERR_HOOKABORTED 4 + int error; + + /* for looking at headers or other information */ + struct evhttp_request *http_req; +}; + +/* the structure below needs to be synchronized with evrpc_req_generic */ + +/* Encapsulates a request */ +struct evrpc { + TAILQ_ENTRY(evrpc) next; + + /* the URI at which the request handler lives */ + const char* uri; + + /* creates a new request structure */ + void *(*request_new)(void *); + void *request_new_arg; + + /* frees the request structure */ + void (*request_free)(void *); + + /* unmarshals the buffer into the proper request structure */ + int (*request_unmarshal)(void *, struct evbuffer *); + + /* creates a new reply structure */ + void *(*reply_new)(void *); + void *reply_new_arg; + + /* frees the reply structure */ + void (*reply_free)(void *); + + /* verifies that the reply is valid */ + int (*reply_complete)(void *); + + /* marshals the reply into a buffer */ + void (*reply_marshal)(struct evbuffer*, void *); + + /* the callback invoked for each received rpc */ + void (*cb)(struct evrpc_req_generic *, void *); + void *cb_arg; + + /* reference for further configuration */ + struct evrpc_base *base; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_RPC_STRUCT_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/tag.h b/src/inc_for_view/event2/tag.h new file mode 100644 index 0000000..2f73bfc --- /dev/null +++ b/src/inc_for_view/event2/tag.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_TAG_H_INCLUDED_ +#define EVENT2_TAG_H_INCLUDED_ + +/** @file event2/tag.h + + Helper functions for reading and writing tagged data onto buffers. + + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif + +/* For int types. */ +#include + +struct evbuffer; + +/* + * Marshaling tagged data - We assume that all tags are inserted in their + * numeric order - so that unknown tags will always be higher than the + * known ones - and we can just ignore the end of an event buffer. + */ + +EVENT2_EXPORT_SYMBOL +void evtag_init(void); + +/** + Unmarshals the header and returns the length of the payload + + @param evbuf the buffer from which to unmarshal data + @param ptag a pointer in which the tag id is being stored + @returns -1 on failure or the number of bytes in the remaining payload. +*/ +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_header(struct evbuffer *evbuf, ev_uint32_t *ptag); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data, + ev_uint32_t len); +EVENT2_EXPORT_SYMBOL +void evtag_marshal_buffer(struct evbuffer *evbuf, ev_uint32_t tag, + struct evbuffer *data); + +/** + Encode an integer and store it in an evbuffer. + + We encode integers by nybbles; the first nibble contains the number + of significant nibbles - 1; this allows us to encode up to 64-bit + integers. This function is byte-order independent. + + @param evbuf evbuffer to store the encoded number + @param number a 32-bit integer + */ +EVENT2_EXPORT_SYMBOL +void evtag_encode_int(struct evbuffer *evbuf, ev_uint32_t number); +EVENT2_EXPORT_SYMBOL +void evtag_encode_int64(struct evbuffer *evbuf, ev_uint64_t number); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, + ev_uint32_t integer); +EVENT2_EXPORT_SYMBOL +void evtag_marshal_int64(struct evbuffer *evbuf, ev_uint32_t tag, + ev_uint64_t integer); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, + const char *string); + +EVENT2_EXPORT_SYMBOL +void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, + struct timeval *tv); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, + struct evbuffer *dst); +EVENT2_EXPORT_SYMBOL +int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag); +EVENT2_EXPORT_SYMBOL +int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength); +EVENT2_EXPORT_SYMBOL +int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength); +EVENT2_EXPORT_SYMBOL +int evtag_consume(struct evbuffer *evbuf); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint32_t *pinteger); +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_int64(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint64_t *pinteger); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, + void *data, size_t len); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, + char **pstring); + +EVENT2_EXPORT_SYMBOL +int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct timeval *ptv); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_TAG_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/tag_compat.h b/src/inc_for_view/event2/tag_compat.h new file mode 100644 index 0000000..a276c0d --- /dev/null +++ b/src/inc_for_view/event2/tag_compat.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_TAG_COMPAT_H_INCLUDED_ +#define EVENT2_TAG_COMPAT_H_INCLUDED_ + +/** @file event2/tag_compat.h + + Obsolete/deprecated functions from tag.h; provided only for backwards + compatibility. + */ + +/** + @name Misnamed functions + + @deprecated These macros are deprecated because their names don't follow + Libevent's naming conventions. Use evtag_encode_int and + evtag_encode_int64 instead. + + @{ +*/ +#define encode_int(evbuf, number) evtag_encode_int((evbuf), (number)) +#define encode_int64(evbuf, number) evtag_encode_int64((evbuf), (number)) +/**@}*/ + +#endif /* EVENT2_TAG_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/thread.h b/src/inc_for_view/event2/thread.h new file mode 100644 index 0000000..b519986 --- /dev/null +++ b/src/inc_for_view/event2/thread.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2008-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_THREAD_H_INCLUDED_ +#define EVENT2_THREAD_H_INCLUDED_ + +/** @file event2/thread.h + + Functions for multi-threaded applications using Libevent. + + When using a multi-threaded application in which multiple threads + add and delete events from a single event base, Libevent needs to + lock its data structures. + + Like the memory-management function hooks, all of the threading functions + _must_ be set up before an event_base is created if you want the base to + use them. + + Most programs will either be using Windows threads or Posix threads. You + can configure Libevent to use one of these event_use_windows_threads() or + event_use_pthreads() respectively. If you're using another threading + library, you'll need to configure threading functions manually using + evthread_set_lock_callbacks() and evthread_set_condition_callbacks(). + + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + @name Flags passed to lock functions + + @{ +*/ +/** A flag passed to a locking callback when the lock was allocated as a + * read-write lock, and we want to acquire or release the lock for writing. */ +#define EVTHREAD_WRITE 0x04 +/** A flag passed to a locking callback when the lock was allocated as a + * read-write lock, and we want to acquire or release the lock for reading. */ +#define EVTHREAD_READ 0x08 +/** A flag passed to a locking callback when we don't want to block waiting + * for the lock; if we can't get the lock immediately, we will instead + * return nonzero from the locking callback. */ +#define EVTHREAD_TRY 0x10 +/**@}*/ + +#if !defined(EVENT__DISABLE_THREAD_SUPPORT) || defined(EVENT_IN_DOXYGEN_) + +#define EVTHREAD_LOCK_API_VERSION 1 + +/** + @name Types of locks + + @{*/ +/** A recursive lock is one that can be acquired multiple times at once by the + * same thread. No other process can allocate the lock until the thread that + * has been holding it has unlocked it as many times as it locked it. */ +#define EVTHREAD_LOCKTYPE_RECURSIVE 1 +/* A read-write lock is one that allows multiple simultaneous readers, but + * where any one writer excludes all other writers and readers. */ +#define EVTHREAD_LOCKTYPE_READWRITE 2 +/**@}*/ + +/** This structure describes the interface a threading library uses for + * locking. It's used to tell evthread_set_lock_callbacks() how to use + * locking on this platform. + */ +struct evthread_lock_callbacks { + /** The current version of the locking API. Set this to + * EVTHREAD_LOCK_API_VERSION */ + int lock_api_version; + /** Which kinds of locks does this version of the locking API + * support? A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and + * EVTHREAD_LOCKTYPE_READWRITE. + * + * (Note that RECURSIVE locks are currently mandatory, and + * READWRITE locks are not currently used.) + **/ + unsigned supported_locktypes; + /** Function to allocate and initialize new lock of type 'locktype'. + * Returns NULL on failure. */ + void *(*alloc)(unsigned locktype); + /** Funtion to release all storage held in 'lock', which was created + * with type 'locktype'. */ + void (*free)(void *lock, unsigned locktype); + /** Acquire an already-allocated lock at 'lock' with mode 'mode'. + * Returns 0 on success, and nonzero on failure. */ + int (*lock)(unsigned mode, void *lock); + /** Release a lock at 'lock' using mode 'mode'. Returns 0 on success, + * and nonzero on failure. */ + int (*unlock)(unsigned mode, void *lock); +}; + +/** Sets a group of functions that Libevent should use for locking. + * For full information on the required callback API, see the + * documentation for the individual members of evthread_lock_callbacks. + * + * Note that if you're using Windows or the Pthreads threading library, you + * probably shouldn't call this function; instead, use + * evthread_use_windows_threads() or evthread_use_posix_threads() if you can. + */ +EVENT2_EXPORT_SYMBOL +int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *); + +#define EVTHREAD_CONDITION_API_VERSION 1 + +struct timeval; + +/** This structure describes the interface a threading library uses for + * condition variables. It's used to tell evthread_set_condition_callbacks + * how to use locking on this platform. + */ +struct evthread_condition_callbacks { + /** The current version of the conditions API. Set this to + * EVTHREAD_CONDITION_API_VERSION */ + int condition_api_version; + /** Function to allocate and initialize a new condition variable. + * Returns the condition variable on success, and NULL on failure. + * The 'condtype' argument will be 0 with this API version. + */ + void *(*alloc_condition)(unsigned condtype); + /** Function to free a condition variable. */ + void (*free_condition)(void *cond); + /** Function to signal a condition variable. If 'broadcast' is 1, all + * threads waiting on 'cond' should be woken; otherwise, only on one + * thread is worken. Should return 0 on success, -1 on failure. + * This function will only be called while holding the associated + * lock for the condition. + */ + int (*signal_condition)(void *cond, int broadcast); + /** Function to wait for a condition variable. The lock 'lock' + * will be held when this function is called; should be released + * while waiting for the condition to be come signalled, and + * should be held again when this function returns. + * If timeout is provided, it is interval of seconds to wait for + * the event to become signalled; if it is NULL, the function + * should wait indefinitely. + * + * The function should return -1 on error; 0 if the condition + * was signalled, or 1 on a timeout. */ + int (*wait_condition)(void *cond, void *lock, + const struct timeval *timeout); +}; + +/** Sets a group of functions that Libevent should use for condition variables. + * For full information on the required callback API, see the + * documentation for the individual members of evthread_condition_callbacks. + * + * Note that if you're using Windows or the Pthreads threading library, you + * probably shouldn't call this function; instead, use + * evthread_use_windows_threads() or evthread_use_pthreads() if you can. + */ +EVENT2_EXPORT_SYMBOL +int evthread_set_condition_callbacks( + const struct evthread_condition_callbacks *); + +/** + Sets the function for determining the thread id. + + @param base the event base for which to set the id function + @param id_fn the identify function Libevent should invoke to + determine the identity of a thread. +*/ +EVENT2_EXPORT_SYMBOL +void evthread_set_id_callback( + unsigned long (*id_fn)(void)); + +#if (defined(_WIN32) && !defined(EVENT__DISABLE_THREAD_SUPPORT)) || defined(EVENT_IN_DOXYGEN_) +/** Sets up Libevent for use with Windows builtin locking and thread ID + functions. Unavailable if Libevent is not built for Windows. + + @return 0 on success, -1 on failure. */ +EVENT2_EXPORT_SYMBOL +int evthread_use_windows_threads(void); +/** + Defined if Libevent was built with support for evthread_use_windows_threads() +*/ +#define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED 1 +#endif + +#if defined(EVENT__HAVE_PTHREADS) || defined(EVENT_IN_DOXYGEN_) +/** Sets up Libevent for use with Pthreads locking and thread ID functions. + Unavailable if Libevent is not build for use with pthreads. Requires + libraries to link against Libevent_pthreads as well as Libevent. + + @return 0 on success, -1 on failure. */ +EVENT2_EXPORT_SYMBOL +int evthread_use_pthreads(void); +/** Defined if Libevent was built with support for evthread_use_pthreads() */ +#define EVTHREAD_USE_PTHREADS_IMPLEMENTED 1 + +#endif + +/** Enable debugging wrappers around the current lock callbacks. If Libevent + * makes one of several common locking errors, exit with an assertion failure. + * + * If you're going to call this function, you must do so before any locks are + * allocated. + **/ +EVENT2_EXPORT_SYMBOL +void evthread_enable_lock_debugging(void); + +/* Old (misspelled) version: This is deprecated; use + * evthread_enable_log_debugging instead. */ +EVENT2_EXPORT_SYMBOL +void evthread_enable_lock_debuging(void); + +#endif /* EVENT__DISABLE_THREAD_SUPPORT */ + +struct event_base; +/** Make sure it's safe to tell an event base to wake up from another thread + or a signal handler. + + You shouldn't need to call this by hand; configuring the base with thread + support should be necessary and sufficient. + + @return 0 on success, -1 on failure. + */ +EVENT2_EXPORT_SYMBOL +int evthread_make_base_notifiable(struct event_base *base); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT2_THREAD_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/util.h b/src/inc_for_view/event2/util.h new file mode 100644 index 0000000..dd4bbb6 --- /dev/null +++ b/src/inc_for_view/event2/util.h @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_UTIL_H_INCLUDED_ +#define EVENT2_UTIL_H_INCLUDED_ + +/** @file event2/util.h + + Common convenience functions for cross-platform portability and + related socket manipulations. + + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif +#ifdef EVENT__HAVE_STDINT_H +#include +#elif defined(EVENT__HAVE_INTTYPES_H) +#include +#endif +#ifdef EVENT__HAVE_SYS_TYPES_H +#include +#endif +#ifdef EVENT__HAVE_STDDEF_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#ifdef EVENT__HAVE_NETDB_H +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif +#include +#endif + +#ifdef _WIN32 +#include +#ifdef EVENT__HAVE_GETADDRINFO +/* for EAI_* definitions. */ +#include +#endif +#else +#ifdef EVENT__HAVE_ERRNO_H +#include +#endif +#include +#endif + +#include + +/* Some openbsd autoconf versions get the name of this macro wrong. */ +#if defined(EVENT__SIZEOF_VOID__) && !defined(EVENT__SIZEOF_VOID_P) +#define EVENT__SIZEOF_VOID_P EVENT__SIZEOF_VOID__ +#endif + +/** + * @name Standard integer types. + * + * Integer type definitions for types that are supposed to be defined in the + * C99-specified stdint.h. Shamefully, some platforms do not include + * stdint.h, so we need to replace it. (If you are on a platform like this, + * your C headers are now over 10 years out of date. You should bug them to + * do something about this.) + * + * We define: + * + *
+ *
ev_uint64_t, ev_uint32_t, ev_uint16_t, ev_uint8_t
+ *
unsigned integer types of exactly 64, 32, 16, and 8 bits + * respectively.
+ *
ev_int64_t, ev_int32_t, ev_int16_t, ev_int8_t
+ *
signed integer types of exactly 64, 32, 16, and 8 bits + * respectively.
+ *
ev_uintptr_t, ev_intptr_t
+ *
unsigned/signed integers large enough + * to hold a pointer without loss of bits.
+ *
ev_ssize_t
+ *
A signed type of the same size as size_t
+ *
ev_off_t
+ *
A signed type typically used to represent offsets within a + * (potentially large) file
+ * + * @{ + */ +#ifdef EVENT__HAVE_UINT64_T +#define ev_uint64_t uint64_t +#define ev_int64_t int64_t +#elif defined(_WIN32) +#define ev_uint64_t unsigned __int64 +#define ev_int64_t signed __int64 +#elif EVENT__SIZEOF_LONG_LONG == 8 +#define ev_uint64_t unsigned long long +#define ev_int64_t long long +#elif EVENT__SIZEOF_LONG == 8 +#define ev_uint64_t unsigned long +#define ev_int64_t long +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint64_t ... +#define ev_int64_t ... +#else +#error "No way to define ev_uint64_t" +#endif + +#ifdef EVENT__HAVE_UINT32_T +#define ev_uint32_t uint32_t +#define ev_int32_t int32_t +#elif defined(_WIN32) +#define ev_uint32_t unsigned int +#define ev_int32_t signed int +#elif EVENT__SIZEOF_LONG == 4 +#define ev_uint32_t unsigned long +#define ev_int32_t signed long +#elif EVENT__SIZEOF_INT == 4 +#define ev_uint32_t unsigned int +#define ev_int32_t signed int +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint32_t ... +#define ev_int32_t ... +#else +#error "No way to define ev_uint32_t" +#endif + +#ifdef EVENT__HAVE_UINT16_T +#define ev_uint16_t uint16_t +#define ev_int16_t int16_t +#elif defined(_WIN32) +#define ev_uint16_t unsigned short +#define ev_int16_t signed short +#elif EVENT__SIZEOF_INT == 2 +#define ev_uint16_t unsigned int +#define ev_int16_t signed int +#elif EVENT__SIZEOF_SHORT == 2 +#define ev_uint16_t unsigned short +#define ev_int16_t signed short +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint16_t ... +#define ev_int16_t ... +#else +#error "No way to define ev_uint16_t" +#endif + +#ifdef EVENT__HAVE_UINT8_T +#define ev_uint8_t uint8_t +#define ev_int8_t int8_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uint8_t ... +#define ev_int8_t ... +#else +#define ev_uint8_t unsigned char +#define ev_int8_t signed char +#endif + +#ifdef EVENT__HAVE_UINTPTR_T +#define ev_uintptr_t uintptr_t +#define ev_intptr_t intptr_t +#elif EVENT__SIZEOF_VOID_P <= 4 +#define ev_uintptr_t ev_uint32_t +#define ev_intptr_t ev_int32_t +#elif EVENT__SIZEOF_VOID_P <= 8 +#define ev_uintptr_t ev_uint64_t +#define ev_intptr_t ev_int64_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_uintptr_t ... +#define ev_intptr_t ... +#else +#error "No way to define ev_uintptr_t" +#endif + +#ifdef EVENT__ssize_t +#define ev_ssize_t EVENT__ssize_t +#else +#define ev_ssize_t ssize_t +#endif + +/* Note that we define ev_off_t based on the compile-time size of off_t that + * we used to build Libevent, and not based on the current size of off_t. + * (For example, we don't define ev_off_t to off_t.). We do this because + * some systems let you build your software with different off_t sizes + * at runtime, and so putting in any dependency on off_t would risk API + * mismatch. + */ +#ifdef _WIN32 +#define ev_off_t ev_int64_t +#elif EVENT__SIZEOF_OFF_T == 8 +#define ev_off_t ev_int64_t +#elif EVENT__SIZEOF_OFF_T == 4 +#define ev_off_t ev_int32_t +#elif defined(EVENT_IN_DOXYGEN_) +#define ev_off_t ... +#else +#define ev_off_t off_t +#endif +/**@}*/ + +/* Limits for integer types. + + We're making two assumptions here: + - The compiler does constant folding properly. + - The platform does signed arithmetic in two's complement. +*/ + +/** + @name Limits for integer types + + These macros hold the largest or smallest values possible for the + ev_[u]int*_t types. + + @{ +*/ +#ifndef EVENT__HAVE_STDINT_H +#define EV_UINT64_MAX ((((ev_uint64_t)0xffffffffUL) << 32) | 0xffffffffUL) +#define EV_INT64_MAX ((((ev_int64_t) 0x7fffffffL) << 32) | 0xffffffffL) +#define EV_INT64_MIN ((-EV_INT64_MAX) - 1) +#define EV_UINT32_MAX ((ev_uint32_t)0xffffffffUL) +#define EV_INT32_MAX ((ev_int32_t) 0x7fffffffL) +#define EV_INT32_MIN ((-EV_INT32_MAX) - 1) +#define EV_UINT16_MAX ((ev_uint16_t)0xffffUL) +#define EV_INT16_MAX ((ev_int16_t) 0x7fffL) +#define EV_INT16_MIN ((-EV_INT16_MAX) - 1) +#define EV_UINT8_MAX 255 +#define EV_INT8_MAX 127 +#define EV_INT8_MIN ((-EV_INT8_MAX) - 1) +#else +#define EV_UINT64_MAX UINT64_MAX +#define EV_INT64_MAX INT64_MAX +#define EV_INT64_MIN INT64_MIN +#define EV_UINT32_MAX UINT32_MAX +#define EV_INT32_MAX INT32_MAX +#define EV_INT32_MIN INT32_MIN +#define EV_UINT16_MAX UINT16_MAX +#define EV_INT16_MAX INT16_MAX +#define EV_UINT8_MAX UINT8_MAX +#define EV_INT8_MAX INT8_MAX +#define EV_INT8_MIN INT8_MIN +/** @} */ +#endif + + +/** + @name Limits for SIZE_T and SSIZE_T + + @{ +*/ +#if EVENT__SIZEOF_SIZE_T == 8 +#define EV_SIZE_MAX EV_UINT64_MAX +#define EV_SSIZE_MAX EV_INT64_MAX +#elif EVENT__SIZEOF_SIZE_T == 4 +#define EV_SIZE_MAX EV_UINT32_MAX +#define EV_SSIZE_MAX EV_INT32_MAX +#elif defined(EVENT_IN_DOXYGEN_) +#define EV_SIZE_MAX ... +#define EV_SSIZE_MAX ... +#else +#error "No way to define SIZE_MAX" +#endif + +#define EV_SSIZE_MIN ((-EV_SSIZE_MAX) - 1) +/**@}*/ + +#ifdef _WIN32 +#define ev_socklen_t int +#elif defined(EVENT__socklen_t) +#define ev_socklen_t EVENT__socklen_t +#else +#define ev_socklen_t socklen_t +#endif + +#ifdef EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#if !defined(EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) \ + && !defined(ss_family) +#define ss_family __ss_family +#endif +#endif + +/** + * A type wide enough to hold the output of "socket()" or "accept()". On + * Windows, this is an intptr_t; elsewhere, it is an int. */ +#ifdef _WIN32 +#define evutil_socket_t intptr_t +#else +#define evutil_socket_t int +#endif + +/** + * Structure to hold information about a monotonic timer + * + * Use this with evutil_configure_monotonic_time() and + * evutil_gettime_monotonic(). + * + * This is an opaque structure; you can allocate one using + * evutil_monotonic_timer_new(). + * + * @see evutil_monotonic_timer_new(), evutil_monotonic_timer_free(), + * evutil_configure_monotonic_time(), evutil_gettime_monotonic() + */ +struct evutil_monotonic_timer +#ifdef EVENT_IN_DOXYGEN_ +{/*Empty body so that doxygen will generate documentation here.*/} +#endif +; + +#define EV_MONOT_PRECISE 1 +#define EV_MONOT_FALLBACK 2 + +/** Format a date string using RFC 1123 format (used in HTTP). + * If `tm` is NULL, current system's time will be used. + * The number of characters written will be returned. + * One should check if the return value is smaller than `datelen` to check if + * the result is truncated or not. + */ +EVENT2_EXPORT_SYMBOL int +evutil_date_rfc1123(char *date, const size_t datelen, const struct tm *tm); + +/** Allocate a new struct evutil_monotonic_timer for use with the + * evutil_configure_monotonic_time() and evutil_gettime_monotonic() + * functions. You must configure the timer with + * evutil_configure_monotonic_time() before using it. + */ +EVENT2_EXPORT_SYMBOL +struct evutil_monotonic_timer * evutil_monotonic_timer_new(void); + +/** Free a struct evutil_monotonic_timer that was allocated using + * evutil_monotonic_timer_new(). + */ +EVENT2_EXPORT_SYMBOL +void evutil_monotonic_timer_free(struct evutil_monotonic_timer *timer); + +/** Set up a struct evutil_monotonic_timer; flags can include + * EV_MONOT_PRECISE and EV_MONOT_FALLBACK. + */ +EVENT2_EXPORT_SYMBOL +int evutil_configure_monotonic_time(struct evutil_monotonic_timer *timer, + int flags); + +/** Query the current monotonic time from a struct evutil_monotonic_timer + * previously configured with evutil_configure_monotonic_time(). Monotonic + * time is guaranteed never to run in reverse, but is not necessarily epoch- + * based, or relative to any other definite point. Use it to make reliable + * measurements of elapsed time between events even when the system time + * may be changed. + * + * It is not safe to use this funtion on the same timer from multiple + * threads. + */ +EVENT2_EXPORT_SYMBOL +int evutil_gettime_monotonic(struct evutil_monotonic_timer *timer, + struct timeval *tp); + +/** Create two new sockets that are connected to each other. + + On Unix, this simply calls socketpair(). On Windows, it uses the + loopback network interface on 127.0.0.1, and only + AF_INET,SOCK_STREAM are supported. + + (This may fail on some Windows hosts where firewall software has cleverly + decided to keep 127.0.0.1 from talking to itself.) + + Parameters and return values are as for socketpair() +*/ +EVENT2_EXPORT_SYMBOL +int evutil_socketpair(int d, int type, int protocol, evutil_socket_t sv[2]); +/** Do platform-specific operations as needed to make a socket nonblocking. + + @param sock The socket to make nonblocking + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_socket_nonblocking(evutil_socket_t sock); + +/** Do platform-specific operations to make a listener socket reusable. + + Specifically, we want to make sure that another program will be able + to bind this address right after we've closed the listener. + + This differs from Windows's interpretation of "reusable", which + allows multiple listeners to bind the same address at the same time. + + @param sock The socket to make reusable + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_reuseable(evutil_socket_t sock); + +/** Do platform-specific operations to make a listener port reusable. + + Specifically, we want to make sure that multiple programs which also + set the same socket option will be able to bind, listen at the same time. + + This is a feature available only to Linux 3.9+ + + @param sock The socket to make reusable + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_listen_socket_reuseable_port(evutil_socket_t sock); + +/** Do platform-specific operations as needed to close a socket upon a + successful execution of one of the exec*() functions. + + @param sock The socket to be closed + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_make_socket_closeonexec(evutil_socket_t sock); + +/** Do the platform-specific call needed to close a socket returned from + socket() or accept(). + + @param sock The socket to be closed + @return 0 on success, -1 on failure + */ +EVENT2_EXPORT_SYMBOL +int evutil_closesocket(evutil_socket_t sock); +#define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s) + +/** Do platform-specific operations, if possible, to make a tcp listener + * socket defer accept()s until there is data to read. + * + * Not all platforms support this. You don't want to do this for every + * listener socket: only the ones that implement a protocol where the + * client transmits before the server needs to respond. + * + * @param sock The listening socket to to make deferred + * @return 0 on success (whether the operation is supported or not), + * -1 on failure +*/ +EVENT2_EXPORT_SYMBOL +int evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock); + +#ifdef _WIN32 +/** Return the most recent socket error. Not idempotent on all platforms. */ +#define EVUTIL_SOCKET_ERROR() WSAGetLastError() +/** Replace the most recent socket error with errcode */ +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { WSASetLastError(errcode); } while (0) +/** Return the most recent socket error to occur on sock. */ +EVENT2_EXPORT_SYMBOL +int evutil_socket_geterror(evutil_socket_t sock); +/** Convert a socket error to a string. */ +EVENT2_EXPORT_SYMBOL +const char *evutil_socket_error_to_string(int errcode); +#elif defined(EVENT_IN_DOXYGEN_) +/** + @name Socket error functions + + These functions are needed for making programs compatible between + Windows and Unix-like platforms. + + You see, Winsock handles socket errors differently from the rest of + the world. Elsewhere, a socket error is like any other error and is + stored in errno. But winsock functions require you to retrieve the + error with a special function, and don't let you use strerror for + the error codes. And handling EWOULDBLOCK is ... different. + + @{ +*/ +/** Return the most recent socket error. Not idempotent on all platforms. */ +#define EVUTIL_SOCKET_ERROR() ... +/** Replace the most recent socket error with errcode */ +#define EVUTIL_SET_SOCKET_ERROR(errcode) ... +/** Return the most recent socket error to occur on sock. */ +#define evutil_socket_geterror(sock) ... +/** Convert a socket error to a string. */ +#define evutil_socket_error_to_string(errcode) ... +/**@}*/ +#else +#define EVUTIL_SOCKET_ERROR() (errno) +#define EVUTIL_SET_SOCKET_ERROR(errcode) \ + do { errno = (errcode); } while (0) +#define evutil_socket_geterror(sock) (errno) +#define evutil_socket_error_to_string(errcode) (strerror(errcode)) +#endif + + +/** + * @name Manipulation macros for struct timeval. + * + * We define replacements + * for timeradd, timersub, timerclear, timercmp, and timerisset. + * + * @{ + */ +#ifdef EVENT__HAVE_TIMERADD +#define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp)) +#define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp)) +#else +#define evutil_timeradd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define evutil_timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /* !EVENT__HAVE_TIMERADD */ + +#ifdef EVENT__HAVE_TIMERCLEAR +#define evutil_timerclear(tvp) timerclear(tvp) +#else +#define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +/**@}*/ + +/** Return true iff the tvp is related to uvp according to the relational + * operator cmp. Recognized values for cmp are ==, <=, <, >=, and >. */ +#define evutil_timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) + +#ifdef EVENT__HAVE_TIMERISSET +#define evutil_timerisset(tvp) timerisset(tvp) +#else +#define evutil_timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#endif + +/** Replacement for offsetof on platforms that don't define it. */ +#ifdef offsetof +#define evutil_offsetof(type, field) offsetof(type, field) +#else +#define evutil_offsetof(type, field) ((off_t)(&((type *)0)->field)) +#endif + +/* big-int related functions */ +/** Parse a 64-bit value from a string. Arguments are as for strtol. */ +EVENT2_EXPORT_SYMBOL +ev_int64_t evutil_strtoll(const char *s, char **endptr, int base); + +/** Replacement for gettimeofday on platforms that lack it. */ +#ifdef EVENT__HAVE_GETTIMEOFDAY +#define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz)) +#else +struct timezone; +EVENT2_EXPORT_SYMBOL +int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); +#endif + +/** Replacement for snprintf to get consistent behavior on platforms for + which the return value of snprintf does not conform to C99. + */ +EVENT2_EXPORT_SYMBOL +int evutil_snprintf(char *buf, size_t buflen, const char *format, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 4))) +#endif +; +/** Replacement for vsnprintf to get consistent behavior on platforms for + which the return value of snprintf does not conform to C99. + */ +EVENT2_EXPORT_SYMBOL +int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 0))) +#endif +; + +/** Replacement for inet_ntop for platforms which lack it. */ +EVENT2_EXPORT_SYMBOL +const char *evutil_inet_ntop(int af, const void *src, char *dst, size_t len); +/** Replacement for inet_pton for platforms which lack it. */ +EVENT2_EXPORT_SYMBOL +int evutil_inet_pton(int af, const char *src, void *dst); +struct sockaddr; + +/** Parse an IPv4 or IPv6 address, with optional port, from a string. + + Recognized formats are: + - [IPv6Address]:port + - [IPv6Address] + - IPv6Address + - IPv4Address:port + - IPv4Address + + If no port is specified, the port in the output is set to 0. + + @param str The string to parse. + @param out A struct sockaddr to hold the result. This should probably be + a struct sockaddr_storage. + @param outlen A pointer to the number of bytes that that 'out' can safely + hold. Set to the number of bytes used in 'out' on success. + @return -1 if the address is not well-formed, if the port is out of range, + or if out is not large enough to hold the result. Otherwise returns + 0 on success. +*/ +EVENT2_EXPORT_SYMBOL +int evutil_parse_sockaddr_port(const char *str, struct sockaddr *out, int *outlen); + +/** Compare two sockaddrs; return 0 if they are equal, or less than 0 if sa1 + * preceeds sa2, or greater than 0 if sa1 follows sa2. If include_port is + * true, consider the port as well as the address. Only implemented for + * AF_INET and AF_INET6 addresses. The ordering is not guaranteed to remain + * the same between Libevent versions. */ +EVENT2_EXPORT_SYMBOL +int evutil_sockaddr_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2, + int include_port); + +/** As strcasecmp, but always compares the characters in locale-independent + ASCII. That's useful if you're handling data in ASCII-based protocols. + */ +EVENT2_EXPORT_SYMBOL +int evutil_ascii_strcasecmp(const char *str1, const char *str2); +/** As strncasecmp, but always compares the characters in locale-independent + ASCII. That's useful if you're handling data in ASCII-based protocols. + */ +EVENT2_EXPORT_SYMBOL +int evutil_ascii_strncasecmp(const char *str1, const char *str2, size_t n); + +/* Here we define evutil_addrinfo to the native addrinfo type, or redefine it + * if this system has no getaddrinfo(). */ +#ifdef EVENT__HAVE_STRUCT_ADDRINFO +#define evutil_addrinfo addrinfo +#else +/** A definition of struct addrinfo for systems that lack it. + + (This is just an alias for struct addrinfo if the system defines + struct addrinfo.) +*/ +struct evutil_addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for nodename */ + struct sockaddr *ai_addr; /* binary address */ + struct evutil_addrinfo *ai_next; /* next structure in linked list */ +}; +#endif +/** @name evutil_getaddrinfo() error codes + + These values are possible error codes for evutil_getaddrinfo() and + related functions. + + @{ +*/ +#if defined(EAI_ADDRFAMILY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_ADDRFAMILY EAI_ADDRFAMILY +#else +#define EVUTIL_EAI_ADDRFAMILY -901 +#endif +#if defined(EAI_AGAIN) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_AGAIN EAI_AGAIN +#else +#define EVUTIL_EAI_AGAIN -902 +#endif +#if defined(EAI_BADFLAGS) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_BADFLAGS EAI_BADFLAGS +#else +#define EVUTIL_EAI_BADFLAGS -903 +#endif +#if defined(EAI_FAIL) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_FAIL EAI_FAIL +#else +#define EVUTIL_EAI_FAIL -904 +#endif +#if defined(EAI_FAMILY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_FAMILY EAI_FAMILY +#else +#define EVUTIL_EAI_FAMILY -905 +#endif +#if defined(EAI_MEMORY) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_MEMORY EAI_MEMORY +#else +#define EVUTIL_EAI_MEMORY -906 +#endif +/* This test is a bit complicated, since some MS SDKs decide to + * remove NODATA or redefine it to be the same as NONAME, in a + * fun interpretation of RFC 2553 and RFC 3493. */ +#if defined(EAI_NODATA) && defined(EVENT__HAVE_GETADDRINFO) && (!defined(EAI_NONAME) || EAI_NODATA != EAI_NONAME) +#define EVUTIL_EAI_NODATA EAI_NODATA +#else +#define EVUTIL_EAI_NODATA -907 +#endif +#if defined(EAI_NONAME) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_NONAME EAI_NONAME +#else +#define EVUTIL_EAI_NONAME -908 +#endif +#if defined(EAI_SERVICE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SERVICE EAI_SERVICE +#else +#define EVUTIL_EAI_SERVICE -909 +#endif +#if defined(EAI_SOCKTYPE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SOCKTYPE EAI_SOCKTYPE +#else +#define EVUTIL_EAI_SOCKTYPE -910 +#endif +#if defined(EAI_SYSTEM) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_EAI_SYSTEM EAI_SYSTEM +#else +#define EVUTIL_EAI_SYSTEM -911 +#endif + +#define EVUTIL_EAI_CANCEL -90001 + +#if defined(AI_PASSIVE) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_PASSIVE AI_PASSIVE +#else +#define EVUTIL_AI_PASSIVE 0x1000 +#endif +#if defined(AI_CANONNAME) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_CANONNAME AI_CANONNAME +#else +#define EVUTIL_AI_CANONNAME 0x2000 +#endif +#if defined(AI_NUMERICHOST) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_NUMERICHOST AI_NUMERICHOST +#else +#define EVUTIL_AI_NUMERICHOST 0x4000 +#endif +#if defined(AI_NUMERICSERV) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_NUMERICSERV AI_NUMERICSERV +#else +#define EVUTIL_AI_NUMERICSERV 0x8000 +#endif +#if defined(AI_V4MAPPED) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_V4MAPPED AI_V4MAPPED +#else +#define EVUTIL_AI_V4MAPPED 0x10000 +#endif +#if defined(AI_ALL) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_ALL AI_ALL +#else +#define EVUTIL_AI_ALL 0x20000 +#endif +#if defined(AI_ADDRCONFIG) && defined(EVENT__HAVE_GETADDRINFO) +#define EVUTIL_AI_ADDRCONFIG AI_ADDRCONFIG +#else +#define EVUTIL_AI_ADDRCONFIG 0x40000 +#endif +/**@}*/ + +struct evutil_addrinfo; +/** + * This function clones getaddrinfo for systems that don't have it. For full + * details, see RFC 3493, section 6.1. + * + * Limitations: + * - When the system has no getaddrinfo, we fall back to gethostbyname_r or + * gethostbyname, with their attendant issues. + * - The AI_V4MAPPED and AI_ALL flags are not currently implemented. + * + * For a nonblocking variant, see evdns_getaddrinfo. + */ +EVENT2_EXPORT_SYMBOL +int evutil_getaddrinfo(const char *nodename, const char *servname, + const struct evutil_addrinfo *hints_in, struct evutil_addrinfo **res); + +/** Release storage allocated by evutil_getaddrinfo or evdns_getaddrinfo. */ +EVENT2_EXPORT_SYMBOL +void evutil_freeaddrinfo(struct evutil_addrinfo *ai); + +EVENT2_EXPORT_SYMBOL +const char *evutil_gai_strerror(int err); + +/** Generate n bytes of secure pseudorandom data, and store them in buf. + * + * Current versions of Libevent use an ARC4-based random number generator, + * seeded using the platform's entropy source (/dev/urandom on Unix-like + * systems; CryptGenRandom on Windows). This is not actually as secure as it + * should be: ARC4 is a pretty lousy cipher, and the current implementation + * provides only rudimentary prediction- and backtracking-resistance. Don't + * use this for serious cryptographic applications. + */ +EVENT2_EXPORT_SYMBOL +void evutil_secure_rng_get_bytes(void *buf, size_t n); + +/** + * Seed the secure random number generator if needed, and return 0 on + * success or -1 on failure. + * + * It is okay to call this function more than once; it will still return + * 0 if the RNG has been successfully seeded and -1 if it can't be + * seeded. + * + * Ordinarily you don't need to call this function from your own code; + * Libevent will seed the RNG itself the first time it needs good random + * numbers. You only need to call it if (a) you want to double-check + * that one of the seeding methods did succeed, or (b) you plan to drop + * the capability to seed (by chrooting, or dropping capabilities, or + * whatever), and you want to make sure that seeding happens before your + * program loses the ability to do it. + */ +EVENT2_EXPORT_SYMBOL +int evutil_secure_rng_init(void); + +/** + * Set a filename to use in place of /dev/urandom for seeding the secure + * PRNG. Return 0 on success, -1 on failure. + * + * Call this function BEFORE calling any other initialization or RNG + * functions. + * + * (This string will _NOT_ be copied internally. Do not free it while any + * user of the secure RNG might be running. Don't pass anything other than a + * real /dev/...random device file here, or you might lose security.) + * + * This API is unstable, and might change in a future libevent version. + */ +EVENT2_EXPORT_SYMBOL +int evutil_secure_rng_set_urandom_device_file(char *fname); + +/** Seed the random number generator with extra random bytes. + + You should almost never need to call this function; it should be + sufficient to invoke evutil_secure_rng_init(), or let Libevent take + care of calling evutil_secure_rng_init() on its own. + + If you call this function as a _replacement_ for the regular + entropy sources, then you need to be sure that your input + contains a fairly large amount of strong entropy. Doing so is + notoriously hard: most people who try get it wrong. Watch out! + + @param dat a buffer full of a strong source of random numbers + @param datlen the number of bytes to read from datlen + */ +EVENT2_EXPORT_SYMBOL +void evutil_secure_rng_add_bytes(const char *dat, size_t datlen); + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT1_EVUTIL_H_INCLUDED_ */ diff --git a/src/inc_for_view/event2/visibility.h b/src/inc_for_view/event2/visibility.h new file mode 100644 index 0000000..fb16dbe --- /dev/null +++ b/src/inc_for_view/event2/visibility.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EVENT2_VISIBILITY_H_INCLUDED_ +#define EVENT2_VISIBILITY_H_INCLUDED_ + +#include + +#if defined(event_EXPORTS) || defined(event_extra_EXPORTS) || defined(event_core_EXPORTS) +# if defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define EVENT2_EXPORT_SYMBOL __global +# elif defined __GNUC__ +# define EVENT2_EXPORT_SYMBOL __attribute__ ((visibility("default"))) +# elif defined(_MSC_VER) +# define EVENT2_EXPORT_SYMBOL extern __declspec(dllexport) +# else +# define EVENT2_EXPORT_SYMBOL /* unknown compiler */ +# endif +#else +# if defined(EVENT__NEED_DLLIMPORT) && defined(_MSC_VER) && !defined(EVENT_BUILDING_REGRESS_TEST) +# define EVENT2_EXPORT_SYMBOL extern __declspec(dllimport) +# else +# define EVENT2_EXPORT_SYMBOL +# endif +#endif + +#endif /* EVENT2_VISIBILITY_H_INCLUDED_ */ diff --git a/src/inc_for_view/field_stat2.h b/src/inc_for_view/field_stat2.h new file mode 100644 index 0000000..97b0c6e --- /dev/null +++ b/src/inc_for_view/field_stat2.h @@ -0,0 +1,66 @@ +#ifndef H_SCREEN_STAT_H_INCLUDE +#define H_SCREEN_STAT_H_INCLUDE +#include + +#ifndef __cplusplus +#error("This file should be compiled with C++ compiler") +#endif + +enum field_dsp_style_t +{ + FS_STYLE_FIELD=0, + FS_STYLE_COLUMN, + FS_STYLE_LINE, + FS_STYLE_STATUS +}; +enum field_calc_algo +{ + FS_CALC_CURRENT=0, + FS_CALC_SPEED +}; +enum field_op +{ + FS_OP_ADD=1, + FS_OP_SET +}; + + +typedef void* screen_stat_handle_t; + +enum FS_option +{ + OUTPUT_DEVICE, //VALUE is a const char*, indicate a file path string, SIZE = strlen(string+'\0')+1.DEFAULT:output to stdout. + PRINT_MODE, //VALUE is an interger,1:Rewrite ,2: Append. SIZE=4,DEFALUT:REWRITE. + STAT_CYCLE, //VALUE is an interger idicate interval seconds of every output, SIZE=4 ,DEFUALT:2 seconds. + PRINT_TRIGGER, //VALUE is an interger,1:Do print,0: Don't print.SIZE=4.DEFAULT:1. + CREATE_THREAD, //VALUE is an interger,1: Create a print thread,0:not create,output by call passive_output function, + //and the STAT_CYCLE is meaningless.SIZE=4,DEFAULT:0. + ID_INVISBLE, //value is field_id/status_id/column_id, not output this string, SIZE=4,DEFAULT: shutdown NO one. + FLUSH_BY_DATE, //value is 1(ture) or 0(false),SIZE=4,DEFAULT: Do not flush by date. + APP_NAME, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT is "?". + STATS_SERVER_IP, //VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. No DEFAULT. + STATS_SERVER_PORT, //VALUE is a unsigned short or a signed int, host order, SIZE= sizeof(unsigned short) or sizeof(int). No DEFAULT. +}; + +//Always success. +screen_stat_handle_t FS_create_handle(void); + +int FS_set_para(screen_stat_handle_t handle, enum FS_option type,const void* value,int size); +void FS_start(screen_stat_handle_t handle); +void FS_stop(screen_stat_handle_t* handle); + +//return field_id/line_id/column_id greater than zero if success,return an interger less than zero if failed. +//should NOT include "|:\n\r.\t<>[]#!@"or space in the parameter name. +int FS_register(screen_stat_handle_t handle,enum field_dsp_style_t style,enum field_calc_algo calc_type,const char* name); + +//numerator_id and denominator_id must be column/field/status style. +//scaling: negative value: zoom in; positive value: zoom out; +int FS_register_ratio(screen_stat_handle_t handle,int numerator_id,int denominator_id,int scaling,enum field_dsp_style_t style,enum field_calc_algo calc_type,const char* name); + +//id: when id's type is FIELD , column_id is ignore. +int FS_operate(screen_stat_handle_t handle,int id,int column_id,enum field_op op,long long value); + +void FS_passive_output(screen_stat_handle_t handle); + +#endif + diff --git a/src/inc_for_view/wiredLB.h b/src/inc_for_view/wiredLB.h new file mode 100644 index 0000000..21daa20 --- /dev/null +++ b/src/inc_for_view/wiredLB.h @@ -0,0 +1,70 @@ + +/* +*****************Wired Load Balancer******** +* Load balance form producer to the consumer. +* Cooperate with consul, which is a service discovery infrastructure. +* See document for detail instructions. +* Author: zhengchao@iie.ac.cn, MESA +* All right reserved by www.mesalab.cn 2018~2021 +********************************************************* +*/ + +#ifndef H_WIRED_LOAD_BALANCER_H_INCLUDE +#define H_WIRED_LOAD_BALANCER_H_INCLUDE +#include +#ifdef __cplusplus +extern "C" { +#endif +#define WLB_CONSUMER 0 +#define WLB_PRODUCER 1 +#define WLB_MAX_TAG_SIZE 1024 +#define ADDRSRLEN_MAX 46 + +enum WLB_OPTION +{ + WLB_OPT_ENABLE_OVERRIDE=0, // VALUE is an int, 1 for enable, 0 for disable. DEFAULT: 0. + WLB_OPT_HEALTH_CHECK_PORT, // VALUE is a unsigned short, SIZE=sizeof(unsigned short). DEFAULT:52100. + WLB_OPT_HEALTH_CHECK_INTERVAL, // VALUE is a unsigned short, SIZE=sizeof(unsigned short). DEFAULT:10 seconds. + WLB_CONS_OPT_DATA_PORT, // VALUE is an unsigned short, SIZE=sizeof(unsigned short). DEFAULT: 0. + WLB_CONS_OPT_PRIMARY_ADDR, // VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: use consul agent listen address. + WLB_CONS_OPT_SECONDARY_ADDR, // VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: no default. + WLB_CONS_OPT_CAPACITY, // VALUE is an int that range from 1 to 100, SIZE=sizeof(int). DEFAULT: 32. + WLB_CONS_OPT_COST, // VALUE is an int that range from 1 to 100, SIZE=sizeof(int). DEFAULT: 32. + WLB_CONS_OPT_USER_TAG, // VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: "null". Size must Not exceed WLB_MAX_TAG_SIZE. + WLB_PROD_OPT_OVERRIDE_PRIMARY_IP, // VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: "null", format: "10.2.0.1-250;123.57.35.100-250;" + WLB_PROD_OPT_OVERRIDE_SECONDARY_IP, // VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: "null", same format as WLB_PROD_OPT_OVERRIDE_PRIMARY_IP. + WLB_PROD_OPT_OVERRIDE_DATAPORT, // VALUE is an unsigned short, SIZE=sizeof(unsigned short). DEFAULT: 0. + WLB_PROD_OPT_OVERRIDE_USER_TAG, // Same requirement as WLB_CONS_OPT_USER_TAG. + WLB_PROD_OPT_DATACENTER // VALUE is a const char*, MUST end with '\0', SIZE= strlen(string+'\0')+1. DEFAULT: "null",list consumer of specific datacenter, case sensitive, format: "mesa-wired-bj" +}; +typedef void* WLB_handle_t; + +struct WLB_consumer_t +{ + char ip_addr[ADDRSRLEN_MAX]; + unsigned short data_port; + char user_tag[WLB_MAX_TAG_SIZE]; +}; +// Lookup is THREAD SAFE. +int wiredLB_lookup(WLB_handle_t handle, const void* key, int len, struct WLB_consumer_t* consumer); + +int wiredLB_list(WLB_handle_t handle,size_t n_cons, struct WLB_consumer_t* cons_array); + + +//Report is THREAD SAFE, NULL is allowed for runtime_info. +void wiredLB_report(WLB_handle_t handle,long proc_bytes, long proc_count, const char* runtime_info); + +//[IN] topic, MADATORY, use utf-8 for non English character. +//[IN] group_name, OPTIONALl, could be NULL, use utf-8 for non English character. +//[IN] role, WLB_COSUMER or WLB_PRODUCER +WLB_handle_t wiredLB_create(const char* topic, const char* group_name, int role); +int wiredLB_set_opt(WLB_handle_t handle, enum WLB_OPTION opt, const void* value, size_t size); +int wiredLB_init(WLB_handle_t handle); +void wiredLB_destroy(WLB_handle_t handle); + + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/src/inc_for_view/wired_cfg.h b/src/inc_for_view/wired_cfg.h new file mode 100644 index 0000000..6c4a1c3 --- /dev/null +++ b/src/inc_for_view/wired_cfg.h @@ -0,0 +1,39 @@ +#ifndef H_WIRED_CFG_H_INCLUDE +#define H_WIRED_CFG_H_INCLUDE +#include +#ifdef __cplusplus +extern "C" { +#endif +#define WCFG_RET_ERR -1 +#define WCFG_RET_NOT_EXIST 0 +#define WCFG_RET_OK 1 + +enum WCFG_OPTION +{ + LOCAL_ONLY=0, + REMOTE_TIMEOUT, + DCFG_ERR +}; + +void * wired_cfg_create(const char* app_name, const char* cfg_path); + +// return DCFG_RET_xx +int wired_cfg_set_opt(void*handle, enum WCFG_OPTION option, const char* val, size_t size); + +// return DCFG_RET_xx +int wired_cfg_init(void* handle); + +// convert the value as your own wish with sscanf +// handle [IN] which aquired by wired_cfg_create +// section [IN] section name in initialization file +// key [IN] keyword name in initialization file +// value [OUT] returned string +// size [IN] buffer size(bytes) +//default_value[IN] default string +int wired_cfg_read(void*handle, const char* section, const char* key,char* value, size_t size,const char* default_value); + +void wired_cfg_destroy(void* handle); +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/inc_internal/opts.h b/src/inc_internal/opts.h new file mode 100644 index 0000000..b56a3bc --- /dev/null +++ b/src/inc_internal/opts.h @@ -0,0 +1,54 @@ +#ifndef TFE_OPTS_H +#define TFE_OPTS_H +#include "tfe_types.h" +#include + +/* TFE Runtime Instances */ +struct tfe_instance +{ + void* main_logger;//MESA_handle_logger + + struct tfe_stat_ctx * stat_module; +}; + +struct tfe_forgesocket_config +{ + bool en_forgesocket; + char str_unix_domain_file[MAX_FILENAME_SIZE]; +}; +#define IO_MOD_KNI 1 +#define IO_MOD_TPROXY 2 +struct tfe_config +{ + /* Configure Files */ + char * cfgfile; + + /* Options */ + unsigned int input_io_module;//KNI, Tproxy, + char kni_path[MAX_FILENAME_SIZE]; + unsigned int debug : 1; + unsigned int detach : 1; + unsigned int sslcomp : 1; + unsigned int no_ssl2 : 1; + unsigned int no_ssl3 : 1; + unsigned int no_tls10 : 1; + unsigned int no_tls11 : 1; + unsigned int no_tls12 : 1; + unsigned int passthrough : 1; + unsigned int deny_ocsp : 1; + unsigned int contentlog_isdir : 1; + unsigned int contentlog_isspec : 1; + unsigned int certgen_writeall: 1; + + unsigned int use_cert_store; + unsigned int thread_num; + + char *crlurl; + + tfe_forgesocket_config * forgesocket_config; +}; + + +#define OPTS_DEBUG(opts) unlikely((opts)->debug) +#endif + diff --git a/src/inc_internal/tfe_types.h b/src/inc_internal/tfe_types.h new file mode 100644 index 0000000..efb0389 --- /dev/null +++ b/src/inc_internal/tfe_types.h @@ -0,0 +1,143 @@ +#include +#include //defines struct in_addr + +#define __TFE_STRING_MAX 2048 +#define MAX_FILENAME_SIZE 256 + +/* network-order */ +struct stream_tuple4_v4{ + struct in_addr saddr; /* network order */ + struct in_addr daddr; /* network order */ + in_port_t source; /* network order */ + in_port_t dest; /* network order */ +}; + + +#ifndef IPV6_ADDR_LEN +#define IPV6_ADDR_LEN (sizeof(struct in6_addr)) +#endif + +struct stream_tuple4_v6 +{ + struct in6_addr saddr; + struct in6_addr daddr; + in_port_t source; /* network order */ + in_port_t dest; /* network order */ +}; + + + +#define GRE_TAG_LEN (4) +struct layer_addr_gre +{ + uint16_t gre_id; +}; + + +#define VLAN_ID_MASK (0x0FFF) +#define VLAN_TAG_LEN (4) +struct layer_addr_vlan +{ + uint16_t vlan_id; /* network order */ +}; + +struct layer_addr_pppoe_session +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned int ver:4; + unsigned int type:4; +#endif +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned int type:4; + unsigned int ver:4; +#endif + unsigned char code; + unsigned short session_id; +}; + +#ifndef MAC_ADDR_LEN +#define MAC_ADDR_LEN (6) +#endif + +struct layer_addr_mac +{ + uint8_t src_mac[MAC_ADDR_LEN]; /* network order */ + uint8_t dst_mac[MAC_ADDR_LEN]; /* network order */ +}; + +struct layer_addr_ipv4 +{ + struct in_addr saddr; /* network order */ + struct in_addr daddr; /* network order */ + in_port_t source; /* network order */ + in_port_t dest; /* network order */ +}; + +struct layer_addr_ipv6 +{ + struct in6_addr saddr; /* network order */ + struct in6_addr daddr; /* network order */ + in_port_t source;/* network order */ + in_port_t dest;/* network order */ +}; + +struct layer_addr_tcp +{ + in_port_t source; /* network order */ + in_port_t dest; /* network order */ +}; + +struct layer_addr_udp +{ + in_port_t source; /* network order */ + in_port_t dest; /* network order */ +}; + +struct layer_addr_l2tp +{ + uint32_t tunnelid; /* network order */ + uint32_t sessionid; /* network order */ +}; +//地址类型定义 +enum addr_type_t{ + __ADDR_TYPE_INIT = 0, + ADDR_TYPE_IPV4, /* 1, 基于IPv4地址的四元组信息 */ + ADDR_TYPE_IPV6, /* 2, 基于IPv6地址的四元组信息 */ + ADDR_TYPE_VLAN, /* 3 */ + ADDR_TYPE_MAC, /* 4 */ + ADDR_TYPE_ARP = 5, /* 5 */ + ADDR_TYPE_GRE, /* 6 */ + ADDR_TYPE_MPLS, /* 7 */ + ADDR_TYPE_PPPOE_SES, /* 8 */ + ADDR_TYPE_TCP, /* 9 */ + ADDR_TYPE_UDP = 10, /* 10 */ + ADDR_TYPE_L2TP, /* 11 */ + //ADDR_TYPE_STREAM_TUPLE4_V4, /* 12, 混合地址类型, 基于IPv4地址的四元组信息 */ + //ADDR_TYPE_STREAM_TUPLE4_V6, /* 13, 混合地址类型, 基于IPv6地址的四元组信息 */ + __ADDR_TYPE_IP_PAIR_V4, /* 14, 纯IPv4地址对 */ + __ADDR_TYPE_IP_PAIR_V6, /* 15, 纯IPv6地址对 */ + __ADDR_TYPE_MAX, /* 16 */ +}; + +struct layer_addr +{ + enum addr_type_t addrtype; /* 地址类型, 详见 enum addr_type_t */ + /* 为了方便应用插件取地址, 此处使用联合体, 省去指针类型强制转换步骤 */ + union + { + struct stream_tuple4_v4 *tuple4_v4; + struct stream_tuple4_v6 *tuple4_v6; + struct layer_addr_ipv4 *ipv4; + struct layer_addr_ipv6 *ipv6; + struct layer_addr_vlan *vlan; + struct layer_addr_mac *mac; + struct layer_addr_gre *gre; + struct layer_addr_tcp *tcp; + struct layer_addr_udp *udp; + struct layer_addr_pppoe_session *pppoe_ses; + struct layer_addr_l2tp *l2tp; + void *paddr; + }; + uint8_t addrlen; /* 地址结构长度 */ +}; + diff --git a/src/inc_internal/tfe_util.h b/src/inc_internal/tfe_util.h new file mode 100644 index 0000000..015ba5e --- /dev/null +++ b/src/inc_internal/tfe_util.h @@ -0,0 +1,20 @@ +//#define ALLOC(t,n) (t *)calloc(sizeof(t),(n)) + + /* Allocates an array of objects using malloc() */ +#define ALLOC(type, number) \ + ((type *)calloc(sizeof(type), number)) + +#define log_err_printf(fmt, args...) \ + fprintf(stderr, "file %s, line %d, " fmt, \ + __FILE__, __LINE__, ##args) +#define log_dbg_printf(fmt, args...) \ + fprintf(stdout, "file %s, line %d, " fmt, \ + __FILE__, __LINE__, ##args) +#define likely(expr) __builtin_expect((expr), 1) +#define unlikely(expr) __builtin_expect((expr), 0) + +int addr_sock2layer(struct sockaddr * sock_addr, int sockaddrlen, struct layer_addr* layer_addr); +int addr_layer2sock(struct layer_addr* layer_addr,struct sockaddr * sock_addr); +char* tfe_strdup(const char* s); + + diff --git a/src/io_module_kni.cpp b/src/io_module_kni.cpp new file mode 100644 index 0000000..127d29d --- /dev/null +++ b/src/io_module_kni.cpp @@ -0,0 +1,5 @@ +#include +void* io_kni_init(const char* unix_domain_path, struct event_base * attach) +{ + return NULL; +} diff --git a/src/io_module_kni.h b/src/io_module_kni.h new file mode 100644 index 0000000..c85f8e3 --- /dev/null +++ b/src/io_module_kni.h @@ -0,0 +1,2 @@ +void* io_kni_init(const char* unix_domain_path, struct event_base * attach); + diff --git a/src/privsep.cc b/src/privsep.cc new file mode 100644 index 0000000..da98251 --- /dev/null +++ b/src/privsep.cc @@ -0,0 +1,980 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "privsep.h" + +#include "sys.h" +#include "util.h" +#include "compat.h" +#include "attrib.h" +#include "defaults.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Privilege separation functionality. + * + * The server code has limitations on the internal functionality that can be + * used, namely only those that are initialized before forking. + */ + +/* maximal message sizes */ +#define PRIVSEP_MAX_REQ_SIZE 512 /* arbitrary limit */ +#define PRIVSEP_MAX_ANS_SIZE (1+sizeof(int)) +/* command byte */ +#define PRIVSEP_REQ_CLOSE 0 /* closing command socket */ +#define PRIVSEP_REQ_OPENFILE 1 /* open content log file */ +#define PRIVSEP_REQ_OPENFILE_P 2 /* open content log file w/mkpath */ +#define PRIVSEP_REQ_OPENSOCK 3 /* open socket and pass fd */ +#define PRIVSEP_REQ_CERTFILE 4 /* open cert file in certgendir */ +/* response byte */ +#define PRIVSEP_ANS_SUCCESS 0 /* success */ +#define PRIVSEP_ANS_UNK_CMD 1 /* unknown command */ +#define PRIVSEP_ANS_INVALID 2 /* invalid message */ +#define PRIVSEP_ANS_DENIED 3 /* request denied */ +#define PRIVSEP_ANS_SYS_ERR 4 /* system error; arg=errno */ + +/* communication with signal handler */ +static volatile sig_atomic_t received_sighup; +static volatile sig_atomic_t received_sigint; +static volatile sig_atomic_t received_sigquit; +static volatile sig_atomic_t received_sigterm; +static volatile sig_atomic_t received_sigchld; +static volatile sig_atomic_t received_sigusr1; +/* write end of pipe used for unblocking select */ +static volatile sig_atomic_t selfpipe_wrfd; + +static void +privsep_server_signal_handler(int sig) +{ + int saved_errno; + +#ifdef DEBUG_PRIVSEP_SERVER + log_dbg_printf("privsep_server_signal_handler\n"); +#endif /* DEBUG_PRIVSEP_SERVER */ + + saved_errno = errno; + switch (sig) { + case SIGHUP: + received_sighup = 1; + break; + case SIGINT: + received_sigint = 1; + break; + case SIGQUIT: + received_sigquit = 1; + break; + case SIGTERM: + received_sigterm = 1; + break; + case SIGCHLD: + received_sigchld = 1; + break; + case SIGUSR1: + received_sigusr1 = 1; + break; + } + if (selfpipe_wrfd != -1) { + ssize_t n; + +#ifdef DEBUG_PRIVSEP_SERVER + log_dbg_printf("writing to selfpipe_wrfd %i\n", selfpipe_wrfd); +#endif /* DEBUG_PRIVSEP_SERVER */ + do { + n = write(selfpipe_wrfd, "!", 1); + } while (n == -1 && errno == EINTR); + if (n == -1) { + log_err_printf("Failed to write from signal handler: " + "%s (%i)\n", strerror(errno), errno); + /* ignore error */ + } +#ifdef DEBUG_PRIVSEP_SERVER + } else { + log_dbg_printf("selfpipe_wrfd is %i - not writing\n", selfpipe_wrfd); +#endif /* DEBUG_PRIVSEP_SERVER */ + } + errno = saved_errno; +} + +static int WUNRES +privsep_server_openfile_verify(tfe_config *opts, char *fn, int mkpath) +{ + if (mkpath && !opts->contentlog_isspec) + return -1; + if (!mkpath && !opts->contentlog_isdir) + return -1; + if (strstr(fn, mkpath ? opts->contentlog_basedir + : opts->contentlog) != fn || + strstr(fn, "/../")) + return -1; + return 0; +} + +static int WUNRES +privsep_server_openfile(char *fn, int mkpath) +{ + int fd; + + if (mkpath) { + char *filedir, *fn2; + + fn2 = strdup(fn); + if (!fn2) { + log_err_printf("Could not duplicate filname: %s (%i)\n", + strerror(errno), errno); + return -1; + } + filedir = dirname(fn2); + if (!filedir) { + log_err_printf("Could not get dirname: %s (%i)\n", + strerror(errno), errno); + free(fn2); + return -1; + } + if (sys_mkpath(filedir, DFLT_DIRMODE) == -1) { + log_err_printf("Could not create directory '%s': %s (%i)\n", + filedir, strerror(errno), errno); + free(fn2); + return -1; + } + free(fn2); + } + + fd = open(fn, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE); + if (fd == -1) { + log_err_printf("Failed to open '%s': %s (%i)\n", + fn, strerror(errno), errno); + return -1; + } + return fd; +} + +static int WUNRES +privsep_server_opensock_verify(tfe_config *opts, void *arg) +{ + for (proxyspec *spec = opts->spec; spec; spec = spec->next) { + if (spec == arg) + return 0; + } + return 1; +} + +static int WUNRES +privsep_server_opensock(proxyspec *spec) +{ + evutil_socket_t fd; + int on = 1; + int rv; + + fd = socket(spec->listen_addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + log_err_printf("Error from socket(): %s (%i)\n", + strerror(errno), errno); + evutil_closesocket(fd); + return -1; + } + + rv = evutil_make_socket_nonblocking(fd); + if (rv == -1) { + log_err_printf("Error making socket nonblocking: %s (%i)\n", + strerror(errno), errno); + evutil_closesocket(fd); + return -1; + } + + rv = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on)); + if (rv == -1) { + log_err_printf("Error from setsockopt(SO_KEEPALIVE): %s (%i)\n", + strerror(errno), errno); + evutil_closesocket(fd); + return -1; + } + + rv = evutil_make_listen_socket_reuseable(fd); + if (rv == -1) { + log_err_printf("Error from setsockopt(SO_REUSABLE): %s\n", + strerror(errno)); + evutil_closesocket(fd); + return -1; + } + + if (spec->natsocket && (spec->natsocket(fd) == -1)) { + log_err_printf("Error from spec->natsocket()\n"); + evutil_closesocket(fd); + return -1; + } + + rv = bind(fd, (struct sockaddr *)&spec->listen_addr, + spec->listen_addrlen); + if (rv == -1) { + log_err_printf("Error from bind(): %s\n", strerror(errno)); + evutil_closesocket(fd); + return -1; + } + + return fd; +} + +static int WUNRES +privsep_server_certfile_verify(tfe_config *opts, char *fn) +{ + if (!opts->certgendir) + return -1; + if (strstr(fn, opts->certgendir) != fn || strstr(fn, "/../")) + return -1; + return 0; +} + +static int WUNRES +privsep_server_certfile(char *fn) +{ + int fd; + + fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, DFLT_FILEMODE); + if (fd == -1 && errno != EEXIST) { + log_err_printf("Failed to open '%s': %s (%i)\n", + fn, strerror(errno), errno); + return -1; + } + return fd; +} + +/* + * Handle a single request on a readable server socket. + * Returns 0 on success, 1 on EOF and -1 on error. + */ +static int WUNRES +privsep_server_handle_req(tfe_config *opts, int srvsock) +{ + char req[PRIVSEP_MAX_REQ_SIZE]; + char ans[PRIVSEP_MAX_ANS_SIZE]; + ssize_t n; + int mkpath = 0; + + if ((n = sys_recvmsgfd(srvsock, req, sizeof(req), + NULL)) == -1) { + if (errno == EPIPE || errno == ECONNRESET) { + /* unfriendly EOF, leave server */ + return 1; + } + log_err_printf("Failed to receive msg: %s (%i)\n", + strerror(errno), errno); + return -1; + } + if (n == 0) { + /* EOF, leave server; will not happen for SOCK_DGRAM sockets */ + return 1; + } + log_dbg_printf("Received privsep req type %02x sz %zd on srvsock %i\n", + req[0], n, srvsock); + switch (req[0]) { + case PRIVSEP_REQ_CLOSE: { + /* client indicates EOF through close message */ + return 1; + } + case PRIVSEP_REQ_OPENFILE_P: + mkpath = 1; + case PRIVSEP_REQ_OPENFILE: { + char *fn; + int fd; + + if (n < 2) { + ans[0] = PRIVSEP_ANS_INVALID; + if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + } + if (!(fn = malloc(n))) { + ans[0] = PRIVSEP_ANS_SYS_ERR; + *((int*)&ans[1]) = errno; + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), + -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } + memcpy(fn, req + 1, n - 1); + fn[n - 1] = '\0'; + if (privsep_server_openfile_verify(opts, fn, mkpath) == -1) { + free(fn); + ans[0] = PRIVSEP_ANS_DENIED; + if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } + if ((fd = privsep_server_openfile(fn, mkpath)) == -1) { + free(fn); + ans[0] = PRIVSEP_ANS_SYS_ERR; + *((int*)&ans[1]) = errno; + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), + -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } else { + free(fn); + ans[0] = PRIVSEP_ANS_SUCCESS; + if (sys_sendmsgfd(srvsock, ans, 1, fd) == -1) { + close(fd); + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + close(fd); + return 0; + } + /* not reached */ + break; + } + case PRIVSEP_REQ_OPENSOCK: { + proxyspec *arg; + int s; + + if (n != sizeof(char) + sizeof(arg)) { + ans[0] = PRIVSEP_ANS_INVALID; + if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } + arg = *(proxyspec**)(&req[1]); + if (privsep_server_opensock_verify(opts, arg) == -1) { + ans[0] = PRIVSEP_ANS_DENIED; + if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } + if ((s = privsep_server_opensock(arg)) == -1) { + ans[0] = PRIVSEP_ANS_SYS_ERR; + *((int*)&ans[1]) = errno; + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), + -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } else { + ans[0] = PRIVSEP_ANS_SUCCESS; + if (sys_sendmsgfd(srvsock, ans, 1, s) == -1) { + evutil_closesocket(s); + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + evutil_closesocket(s); + return 0; + } + /* not reached */ + break; + } + case PRIVSEP_REQ_CERTFILE: { + char *fn; + int fd; + + if (n < 2) { + ans[0] = PRIVSEP_ANS_INVALID; + if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + } + if (!(fn = malloc(n))) { + ans[0] = PRIVSEP_ANS_SYS_ERR; + *((int*)&ans[1]) = errno; + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), + -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } + memcpy(fn, req + 1, n - 1); + fn[n - 1] = '\0'; + if (privsep_server_certfile_verify(opts, fn) == -1) { + free(fn); + ans[0] = PRIVSEP_ANS_DENIED; + if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } + if ((fd = privsep_server_certfile(fn)) == -1) { + free(fn); + ans[0] = PRIVSEP_ANS_SYS_ERR; + *((int*)&ans[1]) = errno; + if (sys_sendmsgfd(srvsock, ans, 1 + sizeof(int), + -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + return 0; + } else { + free(fn); + ans[0] = PRIVSEP_ANS_SUCCESS; + if (sys_sendmsgfd(srvsock, ans, 1, fd) == -1) { + close(fd); + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + close(fd); + return 0; + } + /* not reached */ + break; + } + default: + ans[0] = PRIVSEP_ANS_UNK_CMD; + if (sys_sendmsgfd(srvsock, ans, 1, -1) == -1) { + log_err_printf("Sending message failed: %s (%i" + ")\n", strerror(errno), errno); + return -1; + } + } + return 0; +} + +/* + * Privilege separation server (main privileged monitor loop) + * + * sigpipe is the self-pipe trick pipe used for communicating signals to + * the main event loop and break out of select() without race conditions. + * srvsock[] is a dynamic array of connected privsep server sockets to serve. + * Caller is responsible for freeing memory after returning, if necessary. + * childpid is the pid of the child process to forward signals to. + * + * Returns 0 on a successful clean exit and -1 on errors. + */ +static int +privsep_server(tfe_config *opts, int sigpipe, int srvsock[], size_t nsrvsock, + pid_t childpid) +{ + int srveof[nsrvsock]; + size_t i = 0; + + for (i = 0; i < nsrvsock; i++) { + srveof[i] = 0; + } + + for (;;) { + fd_set readfds; + int maxfd, rv; + +#ifdef DEBUG_PRIVSEP_SERVER + log_dbg_printf("privsep_server select()\n"); +#endif /* DEBUG_PRIVSEP_SERVER */ + do { + FD_ZERO(&readfds); + FD_SET(sigpipe, &readfds); + maxfd = sigpipe; + for (i = 0; i < nsrvsock; i++) { + if (!srveof[i]) { + FD_SET(srvsock[i], &readfds); + maxfd = util_max(maxfd, srvsock[i]); + } + } + rv = select(maxfd + 1, &readfds, NULL, NULL, NULL); +#ifdef DEBUG_PRIVSEP_SERVER + log_dbg_printf("privsep_server woke up (1)\n"); +#endif /* DEBUG_PRIVSEP_SERVER */ + } while (rv == -1 && errno == EINTR); + if (rv == -1) { + log_err_printf("select() failed: %s (%i)\n", + strerror(errno), errno); + return -1; + } +#ifdef DEBUG_PRIVSEP_SERVER + log_dbg_printf("privsep_server woke up (2)\n"); +#endif /* DEBUG_PRIVSEP_SERVER */ + + if (FD_ISSET(sigpipe, &readfds)) { + char buf[16]; + ssize_t n; + /* first drain the signal pipe, then deal with + * all the individual signal flags */ + n = read(sigpipe, buf, sizeof(buf)); + if (n == -1) { + log_err_printf("read(sigpipe) failed:" + " %s (%i)\n", + strerror(errno), errno); + return -1; + } + if (received_sigquit) { + if (kill(childpid, SIGQUIT) == -1) { + log_err_printf("kill(%i,SIGQUIT) " + "failed: %s (%i)\n", + childpid, + strerror(errno), errno); + } + received_sigquit = 0; + } + if (received_sigterm) { + if (kill(childpid, SIGTERM) == -1) { + log_err_printf("kill(%i,SIGTERM) " + "failed: %s (%i)\n", + childpid, + strerror(errno), errno); + } + received_sigterm = 0; + } + if (received_sighup) { + if (kill(childpid, SIGHUP) == -1) { + log_err_printf("kill(%i,SIGHUP) " + "failed: %s (%i)\n", + childpid, + strerror(errno), errno); + } + received_sighup = 0; + } + if (received_sigusr1) { + if (kill(childpid, SIGUSR1) == -1) { + log_err_printf("kill(%i,SIGUSR1) " + "failed: %s (%i)\n", + childpid, + strerror(errno), errno); + } + received_sigusr1 = 0; + } + if (received_sigint) { + /* if we don't detach from the TTY, the + * child process receives SIGINT directly */ + if (opts->detach) { + if (kill(childpid, SIGINT) == -1) { + log_err_printf("kill(%i,SIGINT" + ") failed: " + "%s (%i)\n", + childpid, + strerror(errno), + errno); + } + } + received_sigint = 0; + } + if (received_sigchld) { + /* break the loop; because we are using + * SOCKET_DGRAM we don't get EOF conditions + * on the disconnected socket ends here + * unless we attempt to write or read, so + * we depend on SIGCHLD to notify us of + * our child erroring out or crashing */ + break; + } + } + + for (i = 0; i < nsrvsock; i++) { + if (FD_ISSET(srvsock[i], &readfds)) { + int rv = privsep_server_handle_req(opts, + srvsock[i]); + if (rv == -1) { + log_err_printf("Failed to handle " + "privsep req " + "on srvsock %i\n", + srvsock[i]); + return -1; + } + if (rv == 1) { +#ifdef DEBUG_PRIVSEP_SERVER + log_dbg_printf("srveof[%zu]=1\n", i); +#endif /* DEBUG_PRIVSEP_SERVER */ + srveof[i] = 1; + } + } + } + + /* + * We cannot exit as long as we need the signal handling, + * which is as long as the child process is running. + * The only way out of here is receiving SIGCHLD. + */ + } + + return 0; +} + +int +privsep_client_openfile(int clisock, const char *fn, int mkpath) +{ + char ans[PRIVSEP_MAX_ANS_SIZE]; + char req[1 + strlen(fn)]; + int fd = -1; + ssize_t n; + + req[0] = mkpath ? PRIVSEP_REQ_OPENFILE_P : PRIVSEP_REQ_OPENFILE; + memcpy(req + 1, fn, sizeof(req) - 1); + + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + return -1; + } + + if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + return -1; + } + + if (n < 1) { + errno = EINVAL; + return -1; + } + + switch (ans[0]) { + case PRIVSEP_ANS_SUCCESS: + break; + case PRIVSEP_ANS_DENIED: + errno = EACCES; + return -1; + case PRIVSEP_ANS_SYS_ERR: + if (n < (ssize_t)(1 + sizeof(int))) { + errno = EINVAL; + return -1; + } + errno = *((int*)&ans[1]); + return -1; + case PRIVSEP_ANS_UNK_CMD: + case PRIVSEP_ANS_INVALID: + default: + errno = EINVAL; + return -1; + } + + return fd; +} + +int +privsep_client_opensock(int clisock, const proxyspec *spec) +{ + char ans[PRIVSEP_MAX_ANS_SIZE]; + char req[1 + sizeof(spec)]; + int fd = -1; + ssize_t n; + + req[0] = PRIVSEP_REQ_OPENSOCK; + *((const proxyspec **)&req[1]) = spec; + + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + return -1; + } + + if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + return -1; + } + + if (n < 1) { + errno = EINVAL; + return -1; + } + + switch (ans[0]) { + case PRIVSEP_ANS_SUCCESS: + break; + case PRIVSEP_ANS_DENIED: + errno = EACCES; + return -1; + case PRIVSEP_ANS_SYS_ERR: + if (n < (ssize_t)(1 + sizeof(int))) { + errno = EINVAL; + return -1; + } + errno = *((int*)&ans[1]); + return -1; + case PRIVSEP_ANS_UNK_CMD: + case PRIVSEP_ANS_INVALID: + default: + errno = EINVAL; + return -1; + } + + return fd; +} + +int +privsep_client_certfile(int clisock, const char *fn) +{ + char ans[PRIVSEP_MAX_ANS_SIZE]; + char req[1 + strlen(fn)]; + int fd = -1; + ssize_t n; + + req[0] = PRIVSEP_REQ_CERTFILE; + memcpy(req + 1, fn, sizeof(req) - 1); + + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + return -1; + } + + if ((n = sys_recvmsgfd(clisock, ans, sizeof(ans), &fd)) == -1) { + return -1; + } + + if (n < 1) { + errno = EINVAL; + return -1; + } + + switch (ans[0]) { + case PRIVSEP_ANS_SUCCESS: + break; + case PRIVSEP_ANS_DENIED: + errno = EACCES; + return -1; + case PRIVSEP_ANS_SYS_ERR: + if (n < (ssize_t)(1 + sizeof(int))) { + errno = EINVAL; + return -1; + } + errno = *((int*)&ans[1]); + return -1; + case PRIVSEP_ANS_UNK_CMD: + case PRIVSEP_ANS_INVALID: + default: + errno = EINVAL; + return -1; + } + + return fd; +} + +int +privsep_client_close(int clisock) +{ + char req[1]; + + req[0] = PRIVSEP_REQ_CLOSE; + + if (sys_sendmsgfd(clisock, req, sizeof(req), -1) == -1) { + close(clisock); + return -1; + } + + close(clisock); + return 0; +} + +/* + * Fork and set up privilege separated monitor process. + * Returns -1 on error before forking, 1 as parent, or 0 as child. + * The array of clisock's will get filled with nclisock privsep client + * sockets only for the child; on error and in the parent process it + * will not be touched. + */ +int +privsep_fork(tfe_config *opts, int clisock[], size_t nclisock) +{ + int selfpipev[2]; /* self-pipe trick: signal handler -> select */ + int chldpipev[2]; /* el cheapo interprocess sync early after fork */ + int sockcliv[nclisock][2]; + pid_t pid; + + received_sigquit = 0; + received_sighup = 0; + received_sigint = 0; + received_sigchld = 0; + received_sigusr1 = 0; + + if (pipe(selfpipev) == -1) { + log_err_printf("Failed to create self-pipe: %s (%i)\n", + strerror(errno), errno); + return -1; + } + log_dbg_printf("Created self-pipe [r=%i,w=%i]\n", + selfpipev[0], selfpipev[1]); + + if (pipe(chldpipev) == -1) { + log_err_printf("Failed to create chld-pipe: %s (%i)\n", + strerror(errno), errno); + return -1; + } + log_dbg_printf("Created chld-pipe [r=%i,w=%i]\n", + chldpipev[0], chldpipev[1]); + + for (size_t i = 0; i < nclisock; i++) { + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockcliv[i]) == -1) { + log_err_printf("Failed to create socket pair %zu: " + "%s (%i)\n", i, strerror(errno), errno); + return -1; + } + log_dbg_printf("Created socketpair %zu [p=%i,c=%i]\n", + i, sockcliv[i][0], sockcliv[i][1]); + } + + pid = fork(); + if (pid == -1) { + log_err_printf("Failed to fork: %s (%i)\n", + strerror(errno), errno); + close(selfpipev[0]); + close(selfpipev[1]); + close(chldpipev[0]); + close(chldpipev[1]); + for (size_t i = 0; i < nclisock; i++) { + close(sockcliv[i][0]); + close(sockcliv[i][1]); + } + return -1; + } else if (pid == 0) { + /* child */ + close(selfpipev[0]); + close(selfpipev[1]); + for (size_t i = 0; i < nclisock; i++) + close(sockcliv[i][0]); + /* wait until parent has installed signal handlers, + * intentionally ignoring errors */ + char buf[1]; + ssize_t n; + close(chldpipev[1]); + do { + n = read(chldpipev[0], buf, sizeof(buf)); + } while (n == -1 && errno == EINTR); + close(chldpipev[0]); + + /* return the privsep client sockets */ + for (size_t i = 0; i < nclisock; i++) + clisock[i] = sockcliv[i][1]; + return 0; + } + /* parent */ + for (size_t i = 0; i < nclisock; i++) + close(sockcliv[i][1]); + selfpipe_wrfd = selfpipev[1]; + + /* close file descriptors opened by preinit's only needed in client; + * we still call the preinit's before forking in order to provide + * better user feedback and less privsep complexity */ + nat_preinit_undo(); + + /* If the child exits before the parent installs the signal handler + * here, we have a race condition; this is solved by the client + * blocking on the reading end of a pipe (chldpipev[0]). */ + if (signal(SIGHUP, privsep_server_signal_handler) == SIG_ERR) { + log_err_printf("Failed to install SIGHUP handler: %s (%i)\n", + strerror(errno), errno); + return -1; + } + if (signal(SIGINT, privsep_server_signal_handler) == SIG_ERR) { + log_err_printf("Failed to install SIGINT handler: %s (%i)\n", + strerror(errno), errno); + return -1; + } + if (signal(SIGTERM, privsep_server_signal_handler) == SIG_ERR) { + log_err_printf("Failed to install SIGTERM handler: %s (%i)\n", + strerror(errno), errno); + return -1; + } + if (signal(SIGQUIT, privsep_server_signal_handler) == SIG_ERR) { + log_err_printf("Failed to install SIGQUIT handler: %s (%i)\n", + strerror(errno), errno); + return -1; + } + if (signal(SIGUSR1, privsep_server_signal_handler) == SIG_ERR) { + log_err_printf("Failed to install SIGUSR1 handler: %s (%i)\n", + strerror(errno), errno); + return -1; + } + if (signal(SIGCHLD, privsep_server_signal_handler) == SIG_ERR) { + log_err_printf("Failed to install SIGCHLD handler: %s (%i)\n", + strerror(errno), errno); + return -1; + } + + /* unblock the child */ + close(chldpipev[0]); + close(chldpipev[1]); + + int socksrv[nclisock]; + for (size_t i = 0; i < nclisock; i++) + socksrv[i] = sockcliv[i][0]; + if (privsep_server(opts, selfpipev[0], socksrv, nclisock, pid) == -1) { + log_err_printf("Privsep server failed: %s (%i)\n", + strerror(errno), errno); + /* fall through */ + } +#ifdef DEBUG_PRIVSEP_SERVER + log_dbg_printf("privsep_server exited\n"); +#endif /* DEBUG_PRIVSEP_SERVER */ + + for (size_t i = 0; i < nclisock; i++) + close(sockcliv[i][0]); + selfpipe_wrfd = -1; /* tell signal handler not to write anymore */ + close(selfpipev[0]); + close(selfpipev[1]); + + int status; + wait(&status); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + log_err_printf("Child proc %lld exited with status %d\n", + (long long)pid, WEXITSTATUS(status)); + } else { + log_dbg_printf("Child proc %lld exited with status %d\n", + (long long)pid, WEXITSTATUS(status)); + } + } else if (WIFSIGNALED(status)) { + log_err_printf("Child proc %lld killed by signal %d\n", + (long long)pid, WTERMSIG(status)); + } else { + log_err_printf("Child proc %lld neither exited nor killed\n", + (long long)pid); + } + + return 1; +} + +/* vim: set noet ft=c: */ + + + diff --git a/src/privsep.h b/src/privsep.h new file mode 100644 index 0000000..d12f8d8 --- /dev/null +++ b/src/privsep.h @@ -0,0 +1,44 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PRIVSEP_H +#define PRIVSEP_H + +#include "attrib.h" +#include "opts.h" + +int privsep_fork(struct tfe_config *, int[], size_t); + +int privsep_client_openfile(int, const char *, int); +int privsep_client_opensock(int, const proxyspec *spec); +int privsep_client_certfile(int, const char *); +int privsep_client_close(int); + +#endif /* !PRIVSEP_H */ + +/* vim: set noet ft=c: */ diff --git a/src/proxy.h b/src/proxy.h new file mode 100644 index 0000000..4c22f04 --- /dev/null +++ b/src/proxy.h @@ -0,0 +1,17 @@ + +#ifndef PROXY_H +#define PROXY_H + +#include "opts.h" +#include "attrib.h" + +typedef struct tfe_proxy tfe_proxy; + +tfe_proxy * tfe_proxy_new(struct tfe_config *) NONNULL(1) MALLOC; +void tfe_proxy_run(tfe_proxy *) NONNULL(1); +void proxy_loopbreak(tfe_proxy *) NONNULL(1); +void proxy_free(tfe_proxy *) NONNULL(1); + +#endif /* !PROXY_H */ + +/* vim: set noet ft=c: */ diff --git a/src/pxyconn.h b/src/pxyconn.h new file mode 100644 index 0000000..685c075 --- /dev/null +++ b/src/pxyconn.h @@ -0,0 +1,141 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PXYCONN_H +#define PXYCONN_H + +#include "compat.h" +#include "opts.h" +#include "attrib.h" +#include "pxythrmgr.h" + +#include +#include + +#include +#include + +/* + * Proxy connection context state, describes a proxy connection + * with source and destination socket bufferevents, SSL context and + * other session state. One of these exists per handled proxy + * connection. + */ + +/* single dst or src socket bufferevent descriptor */ +typedef struct pxy_conn_desc +{ + struct bufferevent *bev; + SSL *ssl; + unsigned int closed : 1; +} pxy_conn_desc_t; + +/* actual proxy connection state consisting of two connection descriptors, + * connection-wide state and the specs and options */ + +typedef struct pxy_conn_ctx +{ + /* per-connection state */ + struct pxy_conn_desc src; + struct pxy_conn_desc dst; + + /* status flags */ + unsigned int connected : 1; /* 0 until both ends are connected */ + unsigned int enomem : 1; /* 1 if out of memory */ + /* ssl */ + unsigned int sni_peek_retries : 6; /* max 64 SNI parse retries */ + unsigned int immutable_cert : 1; /* 1 if the cert cannot be changed */ + unsigned int generated_cert : 1; /* 1 if we generated a new cert */ + unsigned int passthrough : 1; /* 1 if SSL passthrough is active */ + /* http */ + unsigned int seen_req_header : 1; /* 0 until request header complete */ + unsigned int seen_resp_header : 1; /* 0 until response hdr complete */ + unsigned int sent_http_conn_close : 1; /* 0 until Conn: close sent */ + unsigned int ocsp_denied : 1; /* 1 if OCSP was denied */ + /* autossl */ + unsigned int clienthello_search : 1; /* 1 if waiting for hello */ + unsigned int clienthello_found : 1; /* 1 if conn upgrade to SSL */ + + /* server name indicated by client in SNI TLS extension */ + char *sni; + + /* log strings from socket */ + char *srchost_str; + char *srcport_str; + char *dsthost_str; + char *dstport_str; + + /* log strings from HTTP request */ + char *http_method; + char *http_uri; + char *http_host; + char *http_content_type; + + /* log strings from HTTP response */ + char *http_status_code; + char *http_status_text; + char *http_content_length; + + /* log strings related to SSL */ + char *ssl_names; + char *origcrtfpr; + char *usedcrtfpr; + + /* store fd and fd event while connected is 0 */ + evutil_socket_t fd; + struct event *ev; + + /* peer fd, used by forge socket */ + evutil_socket_t peer_fd; + + /* original destination address, family and certificate */ + struct sockaddr_storage addr; + socklen_t addrlen; + int af; + X509 *origcrt; + + /* references to event base and configuration */ + struct event_base *evbase; + struct evdns_base *dnsbase; + int thridx; + tfe_thread_manager_ctx *thrmgr; + proxyspec *spec; + + tfe_config *opts; + tfe_instance *instance; + + /* Protocol Ctxs*/ + void *protocol_conn_ctx; +} pxy_conn_ctx_t; + +void pxy_conn_setup(evutil_socket_t fd_downstream, evutil_socket_t fd_upstream, struct sockaddr *peeraddr, + int peeraddrlen, tfe_thread_manager_ctx *thrmgr, proxyspec *spec, tfe_config *opts); + +#endif /* !PXYCONN_H */ + +/* vim: set noet ft=c: */ diff --git a/src/session_cache.cpp b/src/session_cache.cpp new file mode 100644 index 0000000..1db2e4a --- /dev/null +++ b/src/session_cache.cpp @@ -0,0 +1,14 @@ + +struct sess_cache +{ +}; +struct sess_cache* session_cache_init() +{ + +} + +void session_cache_set(struct sess_cache* cache, struct sockaddr *addr, socklen_t addrlen, const char* sni,SSL_SESSION* session) +{ + +} + diff --git a/src/ssl.cc b/src/ssl.cc new file mode 100644 index 0000000..8720e3f --- /dev/null +++ b/src/ssl.cc @@ -0,0 +1,2376 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ssl.h" + +#include "compat.h" +#include "defaults.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_DH +#include +#endif /* !OPENSSL_NO_DH */ +#include +#include +#include + + +/* + * Collection of helper functions on top of the OpenSSL API. + */ + + +/* + * Workaround for bug in OpenSSL 1.0.0k and 1.0.1e + * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=703031 + * http://openssl.6102.n7.nabble.com/NULL-ptr-deref-when-calling-SSL-get-certificate-with-1-0-0k-td43636.html + */ +#if (OPENSSL_VERSION_NUMBER == 0x0090819fL) || \ + (OPENSSL_VERSION_NUMBER == 0x100000bfL) || \ + (OPENSSL_VERSION_NUMBER == 0x1000105fL) +/* + * OpenSSL internal declarations from ssl_locl.h, reduced to what is needed. + */ +struct cert_pkey_st { + X509 *x509; + /* + EVP_PKEY *privatekey; + const EVP_MD *digest; + */ +}; +struct cert_st { + struct cert_pkey_st *key; + /* ... */ +}; + +/* + * Replacement function for SSL_get_certificate(). + */ +X509 * +ssl_ssl_cert_get(SSL *s) +{ + return s->cert ? s->cert->key->x509 : NULL; +} +#endif /* OpenSSL 0.9.8y, 1.0.0k or 1.0.1e */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + /* If the fields p and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. q may remain NULL. + */ + if ((dh->p == NULL && p == NULL) + || (dh->g == NULL && g == NULL)) + return 0; + + if (p != NULL) + { + BN_free(dh->p); + dh->p = p; + } + if (q != NULL) + { + BN_free(dh->q); + dh->q = q; + } + if (g != NULL) + { + BN_free(dh->g); + dh->g = g; + } + + if (q != NULL) + { + dh->length = BN_num_bits(q); + } + + return 1; +} +#endif + +/* + * Print OpenSSL version and build-time configuration to standard error and + * return. + */ +void +ssl_openssl_version(void) +{ + fprintf(stderr, "compiled against %s (%lx)\n", + OPENSSL_VERSION_TEXT, + (long unsigned int) OPENSSL_VERSION_NUMBER); + fprintf(stderr, "rtlinked against %s (%lx)\n", + SSLeay_version(SSLEAY_VERSION), + SSLeay()); + if ((OPENSSL_VERSION_NUMBER ^ SSLeay()) & 0xfffff000L) + { + fprintf(stderr, "---------------------------------------" + "---------------------------------------\n"); + fprintf(stderr, "WARNING: OpenSSL version mismatch may " + "lead to crashes or other problems!\n"); + fprintf(stderr, "If there are multiple versions of " + "OpenSSL available, make sure to use\n"); + fprintf(stderr, "the same version of the library at " + "runtime as well as for compiling against.\n"); + fprintf(stderr, "---------------------------------------" + "---------------------------------------\n"); + } +#ifdef LIBRESSL_VERSION_NUMBER + fprintf(stderr, "LibreSSL detected: %s (%lx)\n", + LIBRESSL_VERSION_TEXT, + (long unsigned int)LIBRESSL_VERSION_NUMBER); +#endif /* LIBRESSL_VERSION_NUMBER */ +#ifdef OPENSSL_IS_BORINGSSL + fprintf(stderr, "BoringSSL detected\n") +#endif /* OPENSSL_IS_BORINGSSL */ +#ifndef OPENSSL_NO_TLSEXT + fprintf(stderr, "OpenSSL has support for TLS extensions\n" + "TLS Server Name Indication (SNI) supported\n"); +#else /* OPENSSL_NO_TLSEXT */ + fprintf(stderr, "OpenSSL has no support for TLS extensions\n" + "TLS Server Name Indication (SNI) not supported\n"); +#endif /* OPENSSL_NO_TLSEXT */ +#ifdef OPENSSL_THREADS +#ifndef OPENSSL_NO_THREADID + fprintf(stderr, "OpenSSL is thread-safe with THREADID\n"); +#else /* OPENSSL_NO_THREADID */ + fprintf(stderr, "OpenSSL is thread-safe without THREADID\n"); +#endif /* OPENSSL_NO_THREADID */ +#else /* !OPENSSL_THREADS */ + fprintf(stderr, "OpenSSL is not thread-safe\n"); +#endif /* !OPENSSL_THREADS */ +#ifdef SSL_MODE_RELEASE_BUFFERS + fprintf(stderr, "Using SSL_MODE_RELEASE_BUFFERS\n"); +#else /* !SSL_MODE_RELEASE_BUFFERS */ + fprintf(stderr, "Not using SSL_MODE_RELEASE_BUFFERS\n"); +#endif /* !SSL_MODE_RELEASE_BUFFERS */ +#if (OPENSSL_VERSION_NUMBER == 0x0090819fL) || \ + (OPENSSL_VERSION_NUMBER == 0x100000bfL) || \ + (OPENSSL_VERSION_NUMBER == 0x1000105fL) + fprintf(stderr, "Using direct access workaround when loading certs\n"); +#endif /* OpenSSL 0.9.8y, 1.0.0k or 1.0.1e */ + + fprintf(stderr, "SSL/TLS protocol availability: %s\n", + SSL_PROTO_SUPPORT_S); + + fprintf(stderr, "SSL/TLS algorithm availability:"); +#ifndef OPENSSL_NO_SHA0 + fprintf(stderr, " SHA0"); +#else /* !OPENSSL_NO_SHA0 */ + fprintf(stderr, " !SHA0"); +#endif /* !OPENSSL_NO_SHA0 */ +#ifndef OPENSSL_NO_RSA + fprintf(stderr, " RSA"); +#else /* !OPENSSL_NO_RSA */ + fprintf(stderr, " !RSA"); +#endif /* !OPENSSL_NO_RSA */ +#ifndef OPENSSL_NO_DSA + fprintf(stderr, " DSA"); +#else /* !OPENSSL_NO_DSA */ + fprintf(stderr, " !DSA"); +#endif /* !OPENSSL_NO_DSA */ +#ifndef OPENSSL_NO_ECDSA + fprintf(stderr, " ECDSA"); +#else /* !OPENSSL_NO_ECDSA */ + fprintf(stderr, " !ECDSA"); +#endif /* !OPENSSL_NO_ECDSA */ +#ifndef OPENSSL_NO_DH + fprintf(stderr, " DH"); +#else /* !OPENSSL_NO_DH */ + fprintf(stderr, " !DH"); +#endif /* !OPENSSL_NO_DH */ +#ifndef OPENSSL_NO_ECDH + fprintf(stderr, " ECDH"); +#else /* !OPENSSL_NO_ECDH */ + fprintf(stderr, " !ECDH"); +#endif /* !OPENSSL_NO_ECDH */ +#ifndef OPENSSL_NO_EC + fprintf(stderr, " EC"); +#else /* !OPENSSL_NO_EC */ + fprintf(stderr, " !EC"); +#endif /* !OPENSSL_NO_EC */ + fprintf(stderr, "\n"); + + fprintf(stderr, "OpenSSL option availability:"); +#ifdef SSL_OP_NO_COMPRESSION + fprintf(stderr, " SSL_OP_NO_COMPRESSION"); +#else /* !SSL_OP_NO_COMPRESSION */ + fprintf(stderr, " !SSL_OP_NO_COMPRESSION"); +#endif /* SSL_OP_NO_COMPRESSION */ +#ifdef SSL_OP_NO_TICKET + fprintf(stderr, " SSL_OP_NO_TICKET"); +#else /* !SSL_OP_NO_TICKET */ + fprintf(stderr, " !SSL_OP_NO_TICKET"); +#endif /* SSL_OP_NO_TICKET */ +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + fprintf(stderr, " SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); +#else /* !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ + fprintf(stderr, " !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); +#endif /* !SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + fprintf(stderr, " SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); +#else /* !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ + fprintf(stderr, " !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); +#endif /* !SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + fprintf(stderr, " SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); +#else /* !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION */ + fprintf(stderr, " !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); +#endif /* !SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION */ +#ifdef SSL_OP_TLS_ROLLBACK_BUG + fprintf(stderr, " SSL_OP_TLS_ROLLBACK_BUG"); +#else /* !SSL_OP_TLS_ROLLBACK_BUG */ + fprintf(stderr, " !SSL_OP_TLS_ROLLBACK_BUG"); +#endif /* !SSL_OP_TLS_ROLLBACK_BUG */ + fprintf(stderr, "\n"); +} + +/* + * 1 if OpenSSL has been initialized, 0 if not. When calling a _load() + * function the first time, OpenSSL will automatically be initialized. + * Not protected by a mutex and thus not thread-safe. + */ +static int ssl_initialized = 0; + +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L +struct CRYPTO_dynlock_value +{ + pthread_mutex_t mutex; +}; +static pthread_mutex_t *ssl_mutex; +static int ssl_mutex_num; + +/* + * OpenSSL thread-safety locking callback, #1. + */ +static void +ssl_thr_locking_cb( + int mode, int type, UNUSED const char *file, + UNUSED int line) +{ + if (type < ssl_mutex_num) + { + if (mode & CRYPTO_LOCK) + pthread_mutex_lock(&ssl_mutex[type]); + else + pthread_mutex_unlock(&ssl_mutex[type]); + } +} + +/* + * OpenSSL thread-safety locking callback, #2. + */ +static struct CRYPTO_dynlock_value * +ssl_thr_dyn_create_cb(UNUSED const char *file, UNUSED int line) +{ + struct CRYPTO_dynlock_value *dl; + + if ((dl = (CRYPTO_dynlock_value *)malloc(sizeof(struct CRYPTO_dynlock_value)))) + { + if (pthread_mutex_init(&dl->mutex, NULL)) + { + free(dl); + return NULL; + } + } + return dl; +} + +/* + * OpenSSL thread-safety locking callback, #3. + */ +static void ssl_thr_dyn_lock_cb( + int mode, struct CRYPTO_dynlock_value *dl, + UNUSED const char *file, UNUSED int line) +{ + if (mode & CRYPTO_LOCK) + { + pthread_mutex_lock(&dl->mutex); + } else + { + pthread_mutex_unlock(&dl->mutex); + } +} + +/* + * OpenSSL thread-safety locking callback, #4. + */ +static void ssl_thr_dyn_destroy_cb( + struct CRYPTO_dynlock_value *dl, + UNUSED const char *file, UNUSED int line) +{ + pthread_mutex_destroy(&dl->mutex); + free(dl); +} + +#ifdef OPENSSL_NO_THREADID +/* + * OpenSSL thread-safety thread ID callback, legacy version. + */ +static unsigned long +ssl_thr_id_cb(void) { + return (unsigned long) pthread_self(); +} +#else /* !OPENSSL_NO_THREADID */ +/* + * OpenSSL thread-safety thread ID callback, up-to-date version. + */ +static void +ssl_thr_id_cb(CRYPTO_THREADID *id) +{ + CRYPTO_THREADID_set_numeric(id, (unsigned long) pthread_self()); +} +#endif /* !OPENSSL_NO_THREADID */ +#endif /* OPENSSL_THREADS */ + +/* + * Initialize OpenSSL and verify the random number generator works. + * Returns -1 on failure, 0 on success. + */ +int +ssl_init(void) +{ +#ifndef PURIFY + int fd; +#endif /* !PURIFY */ + char buf[256]; + + if (ssl_initialized) + return 0; + + /* general initialization */ + SSL_library_init(); +#ifdef PURIFY + CRYPTO_malloc_init(); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif /* PURIFY */ + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + /* thread-safety */ +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + ssl_mutex_num = CRYPTO_num_locks(); + ssl_mutex = (pthread_mutex_t *)malloc(ssl_mutex_num * sizeof(*ssl_mutex)); + + for (int i = 0; i < ssl_mutex_num; i++) + { + if (pthread_mutex_init(&ssl_mutex[i], NULL)) + { + log_err_printf("Failed to initialize mutex\n"); + return -1; + } + } + CRYPTO_set_locking_callback(ssl_thr_locking_cb); + CRYPTO_set_dynlock_create_callback(ssl_thr_dyn_create_cb); + CRYPTO_set_dynlock_lock_callback(ssl_thr_dyn_lock_cb); + CRYPTO_set_dynlock_destroy_callback(ssl_thr_dyn_destroy_cb); +#ifdef OPENSSL_NO_THREADID + CRYPTO_set_id_callback(ssl_thr_id_cb); +#else /* !OPENSSL_NO_THREADID */ + CRYPTO_THREADID_set_callback(ssl_thr_id_cb); +#endif /* !OPENSSL_NO_THREADID */ +#endif /* OPENSSL_THREADS */ + + /* randomness */ +#ifndef PURIFY + if ((fd = open("/dev/urandom", O_RDONLY)) == -1) + { + log_err_printf("Error opening /dev/urandom for reading: %s\n", + strerror(errno)); + return -1; + } + while (!RAND_status()) + { + if (read(fd, buf, sizeof(buf)) == -1) + { + log_err_printf("Error reading from /dev/urandom: %s\n", + strerror(errno)); + close(fd); + return -1; + } + RAND_seed(buf, sizeof(buf)); + } + close(fd); + if (!RAND_poll()) + { + log_err_printf("RAND_poll() failed.\n"); + return -1; + } +#else /* PURIFY */ + log_err_printf("Warning: not seeding OpenSSL RAND due to PURITY!\n"); + memset(buf, 0, sizeof(buf)); + while (!RAND_status()) { + RAND_seed(buf, sizeof(buf)); + } +#endif /* PURIFY */ + +#ifdef USE_FOOTPRINT_HACKS + /* HACK: disable compression by zeroing the global comp algo stack. + * This lowers the per-connection memory footprint by ~500k. */ + STACK_OF(SSL_COMP)* comp_methods = SSL_COMP_get_compression_methods(); + sk_SSL_COMP_zero(comp_methods); +#endif /* USE_FOOTPRINT_HACKS */ + + ssl_initialized = 1; + return 0; +} + +/* + * Re-initialize OpenSSL after forking. Returns 0 on success, -1 on failure. + */ +int +ssl_reinit(void) +{ + if (!ssl_initialized) + return 0; + +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + for (int i = 0; i < ssl_mutex_num; i++) + { + if (pthread_mutex_init(&ssl_mutex[i], NULL)) + { + return -1; + } + } +#endif /* OPENSSL_THREADS */ + + return 0; +} + +/* + * Deinitialize OpenSSL and free as much memory as possible. + * Some 10k-100k will still remain resident no matter what. + */ +void +ssl_fini(void) +{ + if (!ssl_initialized) + return; + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_state(0); /* current thread */ +#endif + +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); +#ifdef OPENSSL_NO_THREADID + CRYPTO_set_id_callback(NULL); +#else /* !OPENSSL_NO_THREADID */ + CRYPTO_THREADID_set_callback(NULL); +#endif /* !OPENSSL_NO_THREADID */ + for (int i = 0; i < ssl_mutex_num; i++) + { + pthread_mutex_destroy(&ssl_mutex[i]); + } + free(ssl_mutex); +#endif + + ENGINE_cleanup(); + CONF_modules_finish(); + CONF_modules_unload(1); + CONF_modules_free(); + + EVP_cleanup(); + ERR_free_strings(); + CRYPTO_cleanup_all_ex_data(); +} + +/* + * Format raw SHA1 hash into newly allocated string, with or without colons. + */ +char * +ssl_sha1_to_str(unsigned char *rawhash, int colons) +{ + char *str; + int rv; + + rv = asprintf(&str, colons ? + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X" + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X" : + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + rawhash[0], rawhash[1], rawhash[2], rawhash[3], + rawhash[4], rawhash[5], rawhash[6], rawhash[7], + rawhash[8], rawhash[9], rawhash[10], rawhash[11], + rawhash[12], rawhash[13], rawhash[14], rawhash[15], + rawhash[16], rawhash[17], rawhash[18], rawhash[19]); + if (rv == -1) + return NULL; + return str; +} + +/* + * Format SSL state into newly allocated string. + * Returns pointer to string that must be freed by caller, or NULL on error. + */ +char * +ssl_ssl_state_to_str(SSL *ssl) +{ + char *str = NULL; + int rv; + + rv = asprintf(&str, "%08x = %s%s%04x = %s (%s) [%s]", + SSL_get_state(ssl), + (SSL_get_state(ssl) & SSL_ST_CONNECT) ? "SSL_ST_CONNECT|" : "", + (SSL_get_state(ssl) & SSL_ST_ACCEPT) ? "SSL_ST_ACCEPT|" : "", + SSL_get_state(ssl) & SSL_ST_MASK, + SSL_state_string(ssl), + SSL_state_string_long(ssl), + SSL_is_server(ssl) ? "accept socket" : "connect socket"); + + return (rv < 0) ? NULL : str; +} + +/* + * Generates a NSS key log format compatible string containing the client + * random and the master key, intended to be used to decrypt externally + * captured network traffic using tools like Wireshark. + * + * Only supports the CLIENT_RANDOM method (SSL 3.0 - TLS 1.2). + * + * https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format + */ +char * ssl_ssl_masterkey_to_str(SSL *ssl) +{ + char *str = NULL; + int rv; + unsigned char *k, *r; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + unsigned char kbuf[48], rbuf[32]; + k = &kbuf[0]; + r = &rbuf[0]; + SSL_SESSION_get_master_key(SSL_get0_session(ssl), k, sizeof(kbuf)); + SSL_get_client_random(ssl, r, sizeof(rbuf)); +#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + k = ssl->session->master_key; + r = ssl->s3->client_random; +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + rv = asprintf(&str, + "CLIENT_RANDOM " + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + " " + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "%02X%02X%02X%02X%02X%02X%02X%02X" + "\n", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], + r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15], + r[16], r[17], r[18], r[19], r[20], r[21], r[22], r[23], + r[24], r[25], r[26], r[27], r[28], r[29], r[30], r[31], + k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7], + k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15], + k[16], k[17], k[18], k[19], k[20], k[21], k[22], k[23], + k[24], k[25], k[26], k[27], k[28], k[29], k[30], k[31], + k[32], k[33], k[34], k[35], k[36], k[37], k[38], k[39], + k[40], k[41], k[42], k[43], k[44], k[45], k[46], k[47]); + + return (rv < 0) ? NULL : str; +} + +#ifndef OPENSSL_NO_DH +static unsigned char dh_g[] = {0x02}; +static unsigned char dh512_p[] = { + 0xAB, 0xC0, 0x34, 0x16, 0x95, 0x8B, 0x57, 0xE5, 0x5C, 0xB3, 0x4E, 0x6E, + 0x16, 0x0B, 0x35, 0xC5, 0x6A, 0xCC, 0x4F, 0xD3, 0xE5, 0x46, 0xE2, 0x23, + 0x6A, 0x5B, 0xBB, 0x5D, 0x3D, 0x52, 0xEA, 0xCE, 0x4F, 0x7D, 0xCA, 0xFF, + 0xB4, 0x8B, 0xC9, 0x78, 0xDC, 0xA0, 0xFC, 0xBE, 0xF3, 0xB5, 0xE6, 0x61, + 0xA6, 0x6D, 0x58, 0xFC, 0xA0, 0x0F, 0xF7, 0x9B, 0x97, 0xE6, 0xC7, 0xE8, + 0x1F, 0xCD, 0x16, 0x73}; +static unsigned char dh1024_p[] = { + 0x99, 0x28, 0x34, 0x48, 0x9E, 0xB7, 0xD1, 0x4F, 0x0D, 0x17, 0x09, 0x97, + 0xB9, 0x9B, 0x20, 0xFE, 0xE5, 0x65, 0xE0, 0xE2, 0x56, 0x37, 0x80, 0xA2, + 0x9F, 0x2C, 0x2D, 0x87, 0x10, 0x58, 0x39, 0xAD, 0xF3, 0xC5, 0xA9, 0x08, + 0x24, 0xC7, 0xAA, 0xA9, 0x29, 0x3A, 0x13, 0xDF, 0x4E, 0x0A, 0x6D, 0x11, + 0x39, 0xB1, 0x1C, 0x3F, 0xFE, 0xFE, 0x0A, 0x5E, 0xAD, 0x2E, 0x5C, 0x10, + 0x97, 0x38, 0xAC, 0xE8, 0xEB, 0xAA, 0x4A, 0xA1, 0xC0, 0x5C, 0x1D, 0x27, + 0x65, 0x9C, 0xC8, 0x53, 0xAC, 0x35, 0xDD, 0x84, 0x1F, 0x47, 0x0E, 0x04, + 0xF1, 0x90, 0x61, 0x62, 0x2E, 0x29, 0x2C, 0xC6, 0x28, 0x91, 0x6D, 0xF0, + 0xE2, 0x5E, 0xCE, 0x60, 0x3E, 0xF7, 0xF8, 0x37, 0x99, 0x4D, 0x9F, 0xFB, + 0x68, 0xEC, 0x7F, 0x9D, 0x32, 0x74, 0xD1, 0xAA, 0xD4, 0x4C, 0xF5, 0xCD, + 0xC2, 0xD7, 0xD7, 0xAC, 0xDA, 0x69, 0xF5, 0x2B}; +static unsigned char dh2048_p[] = { + 0xAB, 0x88, 0x97, 0xCA, 0xF1, 0xE1, 0x60, 0x39, 0xFA, 0xB1, 0xA8, 0x7D, + 0xB3, 0x7A, 0x38, 0x08, 0xF0, 0x7A, 0x3D, 0x21, 0xC4, 0xE6, 0xB8, 0x32, + 0x3D, 0xAB, 0x0F, 0xE7, 0x8C, 0xA1, 0x59, 0x47, 0xB2, 0x0A, 0x7A, 0x3A, + 0x20, 0x2A, 0x1B, 0xD4, 0xBA, 0xFC, 0x4C, 0xC5, 0xEE, 0xA2, 0xB9, 0xB9, + 0x65, 0x47, 0xCC, 0x13, 0x99, 0xD7, 0xA6, 0xCA, 0xFF, 0x23, 0x05, 0x91, + 0xAB, 0x5C, 0x82, 0xB8, 0xB4, 0xFD, 0xB1, 0x2E, 0x5B, 0x0F, 0x8E, 0x03, + 0x3C, 0x23, 0xD6, 0x6A, 0xE2, 0x83, 0x95, 0xD2, 0x8E, 0xEB, 0xDF, 0x3A, + 0xAF, 0x89, 0xF0, 0xA0, 0x14, 0x09, 0x12, 0xF6, 0x54, 0x54, 0x93, 0xF4, + 0xD4, 0x41, 0x56, 0x7A, 0x0E, 0x56, 0x20, 0x1F, 0x1D, 0xBA, 0x3F, 0x07, + 0xD2, 0x89, 0x1B, 0x40, 0xD0, 0x1C, 0x08, 0xDF, 0x00, 0x7F, 0x34, 0xF4, + 0x28, 0x4E, 0xF7, 0x53, 0x8D, 0x4A, 0x00, 0xC3, 0xC0, 0x89, 0x9E, 0x63, + 0x96, 0xE9, 0x52, 0xDF, 0xA5, 0x2C, 0x00, 0x4E, 0xB0, 0x82, 0x6A, 0x10, + 0x28, 0x8D, 0xB9, 0xE7, 0x7A, 0xCB, 0xC3, 0xD6, 0xC1, 0xC0, 0x4D, 0x91, + 0xC4, 0x6F, 0xD3, 0x99, 0xD1, 0x86, 0x71, 0x67, 0x0A, 0xA1, 0xFC, 0xF4, + 0x7D, 0x40, 0x88, 0x8D, 0xAC, 0xCB, 0xBC, 0xEA, 0x17, 0x85, 0x0B, 0xC6, + 0x12, 0x3E, 0x4A, 0xB9, 0x60, 0x74, 0x93, 0x54, 0x14, 0x39, 0x10, 0xBF, + 0x21, 0xB0, 0x8B, 0xB1, 0x55, 0x3F, 0xBB, 0x6A, 0x1F, 0x42, 0x82, 0x0A, + 0x40, 0x3A, 0x15, 0xCD, 0xD3, 0x79, 0xD0, 0x02, 0xA4, 0xF5, 0x79, 0x78, + 0x03, 0xBD, 0x47, 0xCC, 0xD5, 0x08, 0x6A, 0x46, 0xAE, 0x36, 0xE4, 0xCD, + 0xB1, 0x17, 0x48, 0x30, 0xB4, 0x02, 0xBC, 0x50, 0x68, 0xE3, 0xA2, 0x76, + 0xD0, 0x5C, 0xB9, 0xE6, 0xBE, 0x4C, 0xFD, 0x50, 0xEF, 0xD0, 0x3F, 0x39, + 0x4F, 0x53, 0x16, 0x3B}; +static unsigned char dh4096_p[] = { + 0xB1, 0xCC, 0x09, 0x86, 0xEE, 0xF9, 0xB9, 0xC9, 0xB9, 0x87, 0xC4, 0xB9, + 0xD7, 0x31, 0x95, 0x84, 0x94, 0x65, 0xED, 0x82, 0x64, 0x11, 0xA7, 0x0A, + 0xFE, 0xC2, 0x60, 0xAE, 0x7C, 0x74, 0xFB, 0x72, 0x8F, 0x0D, 0xA6, 0xDD, + 0x02, 0x49, 0x5B, 0x69, 0xD6, 0x96, 0x05, 0xBE, 0x5E, 0x9B, 0x09, 0x83, + 0xD8, 0xF3, 0x91, 0x55, 0x30, 0x86, 0x97, 0x6C, 0x48, 0x7B, 0x99, 0x82, + 0xCC, 0x1E, 0x1E, 0x25, 0xE6, 0x25, 0xCC, 0xA3, 0x66, 0xDE, 0x8A, 0x78, + 0xEE, 0x7F, 0x4F, 0x86, 0x95, 0x06, 0xBE, 0x64, 0x86, 0xFD, 0x60, 0x6A, + 0x3F, 0x0D, 0x8F, 0x62, 0x17, 0x89, 0xDB, 0xE1, 0x01, 0xC1, 0x75, 0x3A, + 0x78, 0x42, 0xA8, 0x26, 0xEC, 0x00, 0x78, 0xF3, 0xDA, 0x40, 0x8D, 0x0D, + 0x4D, 0x53, 0x82, 0xD7, 0x21, 0xC8, 0x46, 0xC9, 0xE3, 0x80, 0xB4, 0xCF, + 0xEA, 0x46, 0x85, 0xE9, 0xC4, 0x9D, 0xD0, 0xC0, 0x4D, 0x27, 0x0F, 0xF8, + 0x34, 0x3B, 0x86, 0x8F, 0xFC, 0x40, 0x56, 0x49, 0x64, 0x76, 0x61, 0xBC, + 0x35, 0x6A, 0xB8, 0xC5, 0x32, 0x19, 0x00, 0x5E, 0x21, 0x1C, 0x34, 0xCB, + 0x74, 0x5B, 0x60, 0x85, 0x8C, 0x38, 0x52, 0x50, 0x4D, 0xAA, 0x25, 0xE4, + 0x1A, 0xE6, 0xE4, 0xDF, 0x0A, 0xD2, 0x8F, 0x2B, 0xD1, 0x35, 0xC7, 0x92, + 0x7D, 0x6F, 0x54, 0x61, 0x8E, 0x3F, 0xFB, 0xE2, 0xC8, 0x81, 0xD0, 0xAC, + 0x64, 0xE2, 0xA8, 0x30, 0xEA, 0x8E, 0xAD, 0xFE, 0xC0, 0x9E, 0x0B, 0xBF, + 0x34, 0xAC, 0x79, 0x96, 0x38, 0x31, 0x1E, 0xEA, 0xF2, 0x7E, 0xEE, 0x0A, + 0x10, 0x34, 0x7C, 0x1A, 0x30, 0x5F, 0xAF, 0x96, 0x2F, 0x7F, 0xB5, 0x1D, + 0xA7, 0x3D, 0x35, 0x7A, 0x30, 0x70, 0x40, 0xE7, 0xD6, 0x22, 0x1E, 0xD0, + 0x9A, 0x34, 0xC7, 0x6B, 0xE4, 0xF1, 0x78, 0xED, 0xD9, 0xCD, 0x18, 0xBF, + 0x2A, 0x1A, 0x98, 0xB7, 0x6C, 0x6E, 0x18, 0x40, 0xB5, 0xBE, 0xDF, 0xE4, + 0x78, 0x8E, 0x34, 0xB2, 0x7B, 0xE5, 0x88, 0xE6, 0xFD, 0x24, 0xBD, 0xBB, + 0x2E, 0x30, 0x72, 0x54, 0xC7, 0xF4, 0xA0, 0xF1, 0x25, 0xFF, 0xB1, 0x37, + 0x42, 0x07, 0x8C, 0xF2, 0xB9, 0xA1, 0xA4, 0xA7, 0x76, 0x39, 0xB8, 0x11, + 0x17, 0xF3, 0xA8, 0x2E, 0x78, 0x68, 0xF4, 0xBF, 0x98, 0x25, 0x59, 0x17, + 0x59, 0x9B, 0x0D, 0x0B, 0x9B, 0xE3, 0x0F, 0xFF, 0xDC, 0xC8, 0x47, 0x21, + 0xE1, 0x0B, 0x9A, 0x44, 0x79, 0xC7, 0x5F, 0x8E, 0x83, 0x1E, 0x04, 0xA1, + 0xB2, 0x9F, 0x9B, 0xFC, 0xB3, 0x4E, 0xD9, 0xF9, 0x8F, 0x03, 0xBC, 0x0A, + 0x04, 0x00, 0x5C, 0x59, 0xB7, 0x51, 0xAA, 0x75, 0xF8, 0x7A, 0x03, 0x07, + 0x81, 0x6D, 0x67, 0x3E, 0x28, 0x37, 0xE4, 0x74, 0x5B, 0x8C, 0x2A, 0x4B, + 0x6C, 0x10, 0x92, 0x75, 0xA5, 0x79, 0x4B, 0x6D, 0x30, 0xB7, 0x6E, 0xD6, + 0x9E, 0x16, 0xC2, 0x87, 0x69, 0x34, 0xFE, 0xD7, 0x2A, 0x4F, 0xD6, 0xC0, + 0xF3, 0xCD, 0x9C, 0x46, 0xED, 0xC0, 0xB2, 0x84, 0x8D, 0x7E, 0x93, 0xD2, + 0xE9, 0xBE, 0x59, 0x18, 0x92, 0xC1, 0x2C, 0xD6, 0x6C, 0x71, 0x50, 0xA1, + 0x98, 0xDA, 0xD1, 0xAC, 0xDB, 0x88, 0x40, 0x1F, 0x69, 0xDC, 0xDB, 0xB2, + 0xA0, 0x90, 0x01, 0x8E, 0x12, 0xD6, 0x40, 0x1A, 0x8E, 0xC5, 0x69, 0x9C, + 0x91, 0x67, 0xAC, 0xD8, 0x4C, 0x27, 0xCD, 0x08, 0xB8, 0x32, 0x97, 0xE1, + 0x13, 0x0C, 0xFF, 0xB1, 0x06, 0x65, 0x03, 0x98, 0x6F, 0x9E, 0xF7, 0xB8, + 0xA8, 0x75, 0xBA, 0x59, 0xFD, 0x23, 0x98, 0x94, 0x80, 0x9C, 0xA7, 0x46, + 0x32, 0x98, 0x28, 0x7A, 0x0A, 0x3A, 0xA6, 0x95, 0x16, 0x6A, 0x52, 0x8E, + 0x8F, 0x2C, 0xC9, 0x49, 0xB7, 0x59, 0x99, 0x2A, 0xE6, 0xCA, 0x82, 0x88, + 0x36, 0xD3, 0x2B, 0xA4, 0x73, 0xFA, 0x89, 0xBB, +}; + +/* + * OpenSSL temporary DH callback which loads DH parameters from static memory. + */ +DH * ssl_tmp_dh_callback(UNUSED SSL *s, int is_export, int keylength) +{ + DH *dh; + int rv = 0; + + if (!(dh = DH_new())) + { + log_err_printf("DH_new() failed\n"); + return NULL; + } + switch (keylength) + { + case 512: + rv = DH_set0_pqg(dh, + BN_bin2bn(dh512_p, sizeof(dh512_p), NULL), + NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + case 1024: + rv = DH_set0_pqg(dh, + BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL), + NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + case 2048: + rv = DH_set0_pqg(dh, + BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL), + NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + case 4096: + rv = DH_set0_pqg(dh, + BN_bin2bn(dh4096_p, sizeof(dh4096_p), NULL), + NULL, + BN_bin2bn(dh_g, sizeof(dh_g), NULL)); + break; + default: + log_err_printf("Unhandled DH keylength %i%s\n", + keylength, + (is_export ? " (export)" : "")); + DH_free(dh); + return NULL; + } + if (!rv) + { + log_err_printf("Failed to load DH p and g from memory\n"); + DH_free(dh); + return NULL; + } + return (dh); +} + +/* + * Load DH parameters from a PEM file. + * Not thread-safe. + */ +DH * ssl_dh_load(const char *filename) +{ + DH *dh; + FILE *fh; + + if (ssl_init() == -1) + return NULL; + + if (!(fh = fopen(filename, "r"))) + { + return NULL; + } + dh = PEM_read_DHparams(fh, NULL, NULL, NULL); + fclose(fh); + return dh; +} +#endif /* !OPENSSL_NO_DH */ + +#ifndef OPENSSL_NO_EC +/* + * Load an Elliptic Curve by name. If curvename is NULL, a default curve is + * loaded. + */ +EC_KEY * ssl_ec_by_name(const char *curvename) +{ + int nid; + + if (!curvename) + curvename = DFLT_CURVE; + + if ((nid = OBJ_sn2nid(curvename)) == NID_undef) + { + return NULL; + } + return EC_KEY_new_by_curve_name(nid); +} +#endif /* !OPENSSL_NO_EC */ + +/* + * Add a X509v3 extension to a certificate and handle errors. + * Returns -1 on errors, 0 on success. + */ +int ssl_x509_v3ext_add(X509V3_CTX *ctx, X509 *crt, char *k, char *v) +{ + X509_EXTENSION *ext; + + if (!(ext = X509V3_EXT_conf(NULL, ctx, k, v))) + { + return -1; + } + if (X509_add_ext(crt, ext, -1) != 1) + { + X509_EXTENSION_free(ext); + return -1; + } + X509_EXTENSION_free(ext); + return 0; +} + +/* + * Copy a X509v3 extension from one certificate to another. + * If the extension is not present in the original certificate, + * the extension will not be added to the destination certificate. + * Returns 1 if ext was copied, 0 if not present in origcrt, -1 on error. + */ +int ssl_x509_v3ext_copy_by_nid(X509 *crt, X509 *origcrt, int nid) +{ + X509_EXTENSION *ext; + int pos; + + pos = X509_get_ext_by_NID(origcrt, nid, -1); + if (pos == -1) + return 0; + ext = X509_get_ext(origcrt, pos); + if (!ext) + return -1; + if (X509_add_ext(crt, ext, -1) != 1) + return -1; + return 1; +} + +/* + * Best effort randomness generator. + * Not for real life cryptography applications. + * Returns 0 on success, -1 on failure. + */ +int ssl_rand(void *p, size_t sz) +{ + int rv; + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + rv = RAND_pseudo_bytes((unsigned char *) p, sz); + if (rv == 1) + return 0; +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + rv = RAND_bytes((unsigned char *) p, sz); + if (rv == 1) + return 0; + return -1; +} + +/* + * Copy the serial number from src certificate to dst certificate + * and modify it by a random offset. + * If reading the serial fails for some reason, generate a new + * random serial and store it in the dst certificate. + * Using the same serial is not a good idea since some SSL stacks + * check for duplicate certificate serials. + * Returns 0 on success, -1 on error. + */ +int ssl_x509_serial_copyrand(X509 *dstcrt, X509 *srccrt) +{ + ASN1_INTEGER *srcptr, *dstptr; + BIGNUM *bnserial; + unsigned int rand; + int rv; + +#ifndef PURIFY + rv = ssl_rand(&rand, sizeof(rand)); +#else /* PURIFY */ + rand = 0xF001; + rv = 0; +#endif /* PURIFY */ + dstptr = X509_get_serialNumber(dstcrt); + srcptr = X509_get_serialNumber(srccrt); + if ((rv == -1) || !dstptr || !srcptr) + return -1; + bnserial = ASN1_INTEGER_to_BN(srcptr, NULL); + if (!bnserial) + { + /* random 32-bit serial */ + ASN1_INTEGER_set(dstptr, rand); + } else + { + /* original serial plus random 32-bit offset */ + BN_add_word(bnserial, rand); + BN_to_ASN1_INTEGER(bnserial, dstptr); + BN_free(bnserial); + } + return 0; +} + +/* + * Create a fake X509v3 certificate, signed by the provided CA, + * based on the original certificate retrieved from the real server. + * The returned certificate is created using X509_new() and thus must + * be freed by the caller using X509_free(). + * The optional argument extraname is added to subjectAltNames if provided. + */ +X509 * +ssl_x509_forge(X509 *cacrt, EVP_PKEY *cakey, X509 *origcrt, EVP_PKEY *key, + const char *extraname, const char *crlurl) +{ + X509_NAME *subject, *issuer; + GENERAL_NAMES *names; + GENERAL_NAME *gn; + X509 *crt; + int rv; + + subject = X509_get_subject_name(origcrt); + issuer = X509_get_subject_name(cacrt); + if (!subject || !issuer) + return NULL; + + crt = X509_new(); + if (!crt) + return NULL; + + if (!X509_set_version(crt, 0x02) || + !X509_set_subject_name(crt, subject) || + !X509_set_issuer_name(crt, issuer) || + ssl_x509_serial_copyrand(crt, origcrt) == -1 || + !X509_gmtime_adj(X509_get_notBefore(crt), (long) -60 * 60 * 24) || + !X509_gmtime_adj(X509_get_notAfter(crt), (long) 60 * 60 * 24 * 364) || + !X509_set_pubkey(crt, key)) + goto errout; + + /* add standard v3 extensions; cf. RFC 2459 */ + + X509V3_CTX ctx; + X509V3_set_ctx(&ctx, cacrt, crt, NULL, NULL, 0); + if (ssl_x509_v3ext_add(&ctx, crt, "subjectKeyIdentifier", + "hash") == -1 || + ssl_x509_v3ext_add(&ctx, crt, "authorityKeyIdentifier", + "keyid,issuer:always") == -1) + goto errout; + + rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_basic_constraints); + if (rv == 0) + rv = ssl_x509_v3ext_add(&ctx, crt, "basicConstraints", + "CA:FALSE"); + if (rv == -1) + goto errout; + + rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_key_usage); + if (rv == 0) + rv = ssl_x509_v3ext_add(&ctx, crt, "keyUsage", + "digitalSignature," + "keyEncipherment"); + if (rv == -1) + goto errout; + + rv = ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_ext_key_usage); + if (rv == 0) + rv = ssl_x509_v3ext_add(&ctx, crt, "extendedKeyUsage", + "serverAuth"); + if (rv == -1) + goto errout; + + if (crlurl) + { + char *crlurlval; + if (asprintf(&crlurlval, "URI:%s", crlurl) < 0) + goto errout; + if (ssl_x509_v3ext_add(&ctx, crt, "crlDistributionPoints", + crlurlval) == -1) + { + free(crlurlval); + goto errout; + } + free(crlurlval); + } + + if (!extraname) + { + /* no extraname provided: copy original subjectAltName ext */ + if (ssl_x509_v3ext_copy_by_nid(crt, origcrt, + NID_subject_alt_name) == -1) + goto errout; + } else + { + names = (GENERAL_NAMES *)X509_get_ext_d2i(origcrt, NID_subject_alt_name, 0, 0); + if (!names) + { + /* no subjectAltName present: add new one */ + char *cfval; + if (asprintf(&cfval, "DNS:%s", extraname) < 0) + goto errout; + if (ssl_x509_v3ext_add(&ctx, crt, "subjectAltName", + cfval) == -1) + { + free(cfval); + goto errout; + } + free(cfval); + } else + { + /* add extraname to original subjectAltName + * and add it to the new certificate */ + gn = GENERAL_NAME_new(); + if (!gn) + goto errout2; + gn->type = GEN_DNS; + gn->d.dNSName = ASN1_IA5STRING_new(); + if (!gn->d.dNSName) + goto errout3; + ASN1_STRING_set(gn->d.dNSName, + (unsigned char *) extraname, + strlen(extraname)); + sk_GENERAL_NAME_push(names, gn); + X509_EXTENSION *ext = X509V3_EXT_i2d( + NID_subject_alt_name, 0, names); + if (!X509_add_ext(crt, ext, -1)) + { + if (ext) + { + X509_EXTENSION_free(ext); + } + goto errout3; + } + X509_EXTENSION_free(ext); + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + } + } +#ifdef DEBUG_CERTIFICATE + ssl_x509_v3ext_add(&ctx, crt, "nsComment", "Generated by " PKGLABEL); +#endif /* DEBUG_CERTIFICATE */ + + const EVP_MD *md; + switch (EVP_PKEY_type(EVP_PKEY_base_id(cakey))) + { +#ifndef OPENSSL_NO_RSA + case EVP_PKEY_RSA: + switch (X509_get_signature_nid(origcrt)) + { + case NID_md5WithRSAEncryption: md = EVP_md5(); + break; + case NID_ripemd160WithRSA: md = EVP_ripemd160(); + break; + case NID_sha1WithRSAEncryption: md = EVP_sha1(); + break; + case NID_sha224WithRSAEncryption: md = EVP_sha224(); + break; + case NID_sha256WithRSAEncryption: md = EVP_sha256(); + break; + case NID_sha384WithRSAEncryption: md = EVP_sha384(); + break; + case NID_sha512WithRSAEncryption: md = EVP_sha512(); + break; +#ifndef OPENSSL_NO_SHA0 + case NID_shaWithRSAEncryption: md = EVP_sha(); + break; +#endif /* !OPENSSL_NO_SHA0 */ + default: md = EVP_sha256(); + break; + } + break; +#endif /* !OPENSSL_NO_RSA */ +#ifndef OPENSSL_NO_DSA + case EVP_PKEY_DSA: + switch (X509_get_signature_nid(origcrt)) + { + case NID_dsaWithSHA1: + case NID_dsaWithSHA1_2: md = EVP_sha1(); + break; + case NID_dsa_with_SHA224: md = EVP_sha224(); + break; + case NID_dsa_with_SHA256: md = EVP_sha256(); + break; +#ifndef OPENSSL_NO_SHA0 + case NID_dsaWithSHA: md = EVP_sha(); + break; +#endif /* !OPENSSL_NO_SHA0 */ + default: md = EVP_sha256(); + break; + } + break; +#endif /* !OPENSSL_NO_DSA */ +#ifndef OPENSSL_NO_ECDSA + case EVP_PKEY_EC: + switch (X509_get_signature_nid(origcrt)) + { + case NID_ecdsa_with_SHA1: md = EVP_sha1(); + break; + case NID_ecdsa_with_SHA224: md = EVP_sha224(); + break; + case NID_ecdsa_with_SHA256: md = EVP_sha256(); + break; + case NID_ecdsa_with_SHA384: md = EVP_sha384(); + break; + case NID_ecdsa_with_SHA512: md = EVP_sha512(); + break; + default: md = EVP_sha256(); + break; + } + break; +#endif /* !OPENSSL_NO_ECDSA */ + default: goto errout; + } + if (!X509_sign(crt, cakey, md)) + goto errout; + + return crt; + +errout3: + GENERAL_NAME_free(gn); +errout2: + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); +errout: + X509_free(crt); + return NULL; +} + +/* + * Load a X509 certificate chain from a PEM file. + * Returns the first certificate in *crt and all subsequent certificates in + * *chain. If crt is NULL, the first certificate is prepended to *chain + * instead of returned separately. If *chain is NULL, a new stack of X509* + * is created in *chain, else the certs are pushed onto an existing stack. + * Returns -1 on error. + * Not thread-safe. + * + * By accessing (SSLCTX*)->extra_certs directly on OpenSSL before 1.0.2, we + * depend on OpenSSL internals in this function. OpenSSL 1.0.2 introduced + * the SSL_get0_chain_certs() API for accessing the certificate chain. + */ +int ssl_x509chain_load(X509 **crt, STACK_OF(X509) **chain, const char *filename) +{ + X509 *tmpcrt; + SSL_CTX *tmpctx; + SSL *tmpssl; + STACK_OF(X509) *tmpchain; + int rv; + + if (ssl_init() == -1) + return -1; + + tmpctx = SSL_CTX_new(SSLv23_server_method()); + if (!tmpctx) + goto leave1; + + rv = SSL_CTX_use_certificate_chain_file(tmpctx, filename); + if (rv != 1) + goto leave2; + tmpssl = SSL_new(tmpctx); + if (!tmpssl) + goto leave2; + + tmpcrt = SSL_get_certificate(tmpssl); + if (!tmpcrt) + goto leave3; + + if (!*chain) + { + *chain = sk_X509_new_null(); + if (!*chain) + goto leave3; + } + +#if (OPENSSL_VERSION_NUMBER < 0x1000200fL) || defined(LIBRESSL_VERSION_NUMBER) + tmpchain = tmpctx->extra_certs; +#else /* OpenSSL >= 1.0.2 */ + rv = SSL_CTX_get0_chain_certs(tmpctx, &tmpchain); + if (rv != 1) + goto leave3; +#endif /* OpenSSL >= 1.0.2 */ + + if (crt) + { + *crt = tmpcrt; + } else + { + sk_X509_push(*chain, tmpcrt); + } + ssl_x509_refcount_inc(tmpcrt); + + for (int i = 0; i < sk_X509_num(tmpchain); i++) + { + tmpcrt = sk_X509_value(tmpchain, i); + ssl_x509_refcount_inc(tmpcrt); + sk_X509_push(*chain, tmpcrt); + } + SSL_free(tmpssl); + SSL_CTX_free(tmpctx); + return 0; + +leave3: + SSL_free(tmpssl); +leave2: + SSL_CTX_free(tmpctx); +leave1: + return -1; +} + +/* + * Use a X509 certificate chain for an SSL context. + * Copies the certificate stack to the SSL_CTX internal data structures + * and increases reference counts accordingly. + */ +void ssl_x509chain_use(SSL_CTX *sslctx, X509 *crt, STACK_OF(X509) *chain) +{ + SSL_CTX_use_certificate(sslctx, crt); + + for (int i = 0; i < sk_X509_num(chain); i++) + { + X509 *tmpcrt; + + tmpcrt = sk_X509_value(chain, i); + ssl_x509_refcount_inc(tmpcrt); + SSL_CTX_add_extra_chain_cert(sslctx, tmpcrt); + } +} + +/* + * Load a X509 certificate from a PEM file. + * Returned X509 must be freed using X509_free() by the caller. + * Not thread-safe. + */ +X509 *ssl_x509_load(const char *filename) +{ + X509 *crt = NULL; + SSL_CTX *tmpctx; + SSL *tmpssl; + int rv; + + if (ssl_init() == -1) + return NULL; + + tmpctx = SSL_CTX_new(SSLv23_server_method()); + if (!tmpctx) + goto leave1; + rv = SSL_CTX_use_certificate_file(tmpctx, filename, SSL_FILETYPE_PEM); + if (rv != 1) + goto leave2; + tmpssl = SSL_new(tmpctx); + if (!tmpssl) + goto leave2; + crt = SSL_get_certificate(tmpssl); + if (crt) + ssl_x509_refcount_inc(crt); + SSL_free(tmpssl); +leave2: + SSL_CTX_free(tmpctx); +leave1: + return crt; +} + +/* + * Load a private key from a PEM file. + * Returned EVP_PKEY must be freed using EVP_PKEY_free() by the caller. + * Not thread-safe. + */ +EVP_PKEY *ssl_key_load(const char *filename) +{ + EVP_PKEY *key = NULL; + SSL_CTX *tmpctx; + SSL *tmpssl; + int rv; + + if (ssl_init() == -1) + return NULL; + + tmpctx = SSL_CTX_new(SSLv23_server_method()); + if (!tmpctx) + goto leave1; + rv = SSL_CTX_use_PrivateKey_file(tmpctx, filename, SSL_FILETYPE_PEM); + if (rv != 1) + goto leave2; + tmpssl = SSL_new(tmpctx); + if (!tmpssl) + goto leave2; + key = SSL_get_privatekey(tmpssl); + if (key) + ssl_key_refcount_inc(key); + SSL_free(tmpssl); +leave2: + SSL_CTX_free(tmpctx); +leave1: + return key; +} + +/* + * Generate a new RSA key. + * Returned EVP_PKEY must be freed using EVP_PKEY_free() by the caller. + */ +EVP_PKEY *ssl_key_genrsa(const int keysize) +{ + EVP_PKEY *pkey; + RSA *rsa; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + BIGNUM *bn; + int rv; + rsa = RSA_new(); + bn = BN_new(); + BN_dec2bn(&bn, "3"); + rv = RSA_generate_key_ex(rsa, keysize, bn, NULL); + BN_free(bn); + if (rv != 1) { + RSA_free(rsa); + return NULL; + } +#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + rsa = RSA_generate_key(keysize, 3, NULL, NULL); + if (!rsa) + return NULL; +#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ + pkey = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(pkey, rsa); /* does not increment refcount */ + return pkey; +} + +/* + * Returns the subjectKeyIdentifier compatible key id of the public key. + * keyid will receive a binary SHA-1 hash of SSL_KEY_IDSZ bytes. + * Returns 0 on success, -1 on failure. + */ +int ssl_key_identifier_sha1(EVP_PKEY *key, unsigned char *keyid) +{ + X509_PUBKEY *pubkey = NULL; + const unsigned char *pk; + int length; + + /* X509_PUBKEY_set() will attempt to free pubkey if != NULL */ + if (X509_PUBKEY_set(&pubkey, key) != 1 || !pubkey) + return -1; + if (!X509_PUBKEY_get0_param(NULL, &pk, &length, NULL, pubkey)) + goto errout; + if (!EVP_Digest(pk, length, keyid, NULL, EVP_sha1(), NULL)) + goto errout; + X509_PUBKEY_free(pubkey); + return 0; + +errout: + X509_PUBKEY_free(pubkey); + return -1; +} + +/* + * Returns the result of ssl_key_identifier_sha1() as hex characters with or + * without colons in a newly allocated string. + */ +char *ssl_key_identifier(EVP_PKEY *key, int colons) +{ + unsigned char id[SSL_KEY_IDSZ]; + + if (ssl_key_identifier_sha1(key, id) == -1) + return NULL; + + return ssl_sha1_to_str(id, colons); +} + +/* + * Returns the one-line representation of the subject DN in a newly allocated + * string which must be freed by the caller. + */ +char * +ssl_x509_subject(X509 *crt) +{ + return X509_NAME_oneline(X509_get_subject_name(crt), NULL, 0); +} + +/* + * Parse the common name from the subject distinguished name. + * Returns string allocated using malloc(), caller must free(). + * Returns NULL on errors. + */ +char * +ssl_x509_subject_cn(X509 *crt, size_t *psz) +{ + X509_NAME *ptr; + char *cn; + size_t sz; + + ptr = X509_get_subject_name(crt); /* does not inc refcounts */ + if (!ptr) + return NULL; + sz = (size_t) X509_NAME_get_text_by_NID(ptr, NID_commonName, NULL, 0) + 1; + + if ((sz == 0) || !(cn = (char *) malloc(sz))) + return NULL; + if (X509_NAME_get_text_by_NID(ptr, NID_commonName, cn, sz) == -1) + { + free(cn); + return NULL; + } + *psz = sz; + return cn; +} + +/* + * Write the SHA1 fingerprint of certificate to fpr as SSL_X509_FPRSZ (20) + * bytes long binary buffer. + * Returns -1 on error, 0 on success. + */ +int +ssl_x509_fingerprint_sha1(X509 *crt, unsigned char *fpr) +{ + unsigned int sz = SSL_X509_FPRSZ; + + return X509_digest(crt, EVP_sha1(), fpr, &sz) ? 0 : -1; +} + +/* + * Returns the result of ssl_x509_fingerprint_sha1() as hex characters with or + * without colons in a newly allocated string. + */ +char * +ssl_x509_fingerprint(X509 *crt, int colons) +{ + unsigned char fpr[SSL_X509_FPRSZ]; + + if (ssl_x509_fingerprint_sha1(crt, fpr) == -1) + return NULL; + + return ssl_sha1_to_str(fpr, colons); +} + +#ifndef OPENSSL_NO_DH +/* + * Increment the reference count of DH parameters in a thread-safe + * manner. + */ +void +ssl_dh_refcount_inc(DH *dh) +{ +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_add(&dh->references, 1, CRYPTO_LOCK_DH); +#else /* !OPENSSL_THREADS */ + DH_up_ref(dh); +#endif /* !OPENSSL_THREADS */ +} +#endif /* !OPENSSL_NO_DH */ + +/* + * Increment the reference count of an X509 certificate in a thread-safe + * manner. + */ +void +ssl_key_refcount_inc(EVP_PKEY *key) +{ +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_add(&key->references, 1, CRYPTO_LOCK_EVP_PKEY); +#else /* !OPENSSL_THREADS */ + EVP_PKEY_up_ref(key); +#endif /* !OPENSSL_THREADS */ +} + +/* + * Increment the reference count of an X509 certificate in a thread-safe + * manner. This differs from X509_dup() in that it does not create new, + * full copy of the certificate, but only increases the reference count. + */ +void +ssl_x509_refcount_inc(X509 *crt) +{ +#if defined(OPENSSL_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_add(&crt->references, 1, CRYPTO_LOCK_X509); +#else /* !OPENSSL_THREADS */ + X509_up_ref(crt); +#endif /* !OPENSSL_THREADS */ +} + +/* + * Match a URL/URI hostname against a single certificate DNS name + * using RFC 6125 rules (6.4.3 Checking of Wildcard Certificates): + * + * 1. The client SHOULD NOT attempt to match a presented identifier in + * which the wildcard character comprises a label other than the + * left-most label (e.g., do not match bar.*.example.net). + * + * 2. If the wildcard character is the only character of the left-most + * label in the presented identifier, the client SHOULD NOT compare + * against anything but the left-most label of the reference + * identifier (e.g., *.example.com would match foo.example.com but + * not bar.foo.example.com or example.com). + * + * 3. The client MAY match a presented identifier in which the wildcard + * character is not the only character of the label (e.g., + * baz*.example.net and *baz.example.net and b*z.example.net would + * be taken to match baz1.example.net and foobaz.example.net and + * buzz.example.net, respectively). However, the client SHOULD NOT + * attempt to match a presented identifier where the wildcard + * character is embedded within an A-label or U-label [IDNA-DEFS] of + * an internationalized domain name [IDNA-PROTO]. + * + * The optional partial matching in rule 3 is not implemented. + * Returns 1 on match, 0 on no match. + */ +int +ssl_dnsname_match( + const char *certname, size_t certnamesz, + const char *hostname, size_t hostnamesz) +{ + if (hostnamesz < certnamesz) + return 0; + if ((hostnamesz == certnamesz) && + !memcmp(certname, hostname, certnamesz)) + return 1; + if (!memcmp(certname, "xn--", 4)) + return 0; + if ((certnamesz == 1) && (certname[0] == '*') && + !memchr(hostname, '.', hostnamesz)) + return 1; + if ((certnamesz > 2) && (certname[0] == '*') && (certname[1] == '.') && + !memcmp(&certname[1], + &hostname[hostnamesz - (certnamesz - 1)], + certnamesz - 1) && + (memchr(hostname, '.', hostnamesz) == + &hostname[hostnamesz - (certnamesz - 1)])) + return 1; + return 0; +} + +/* + * Transform a NULL-terminated hostname into a matching wildcard hostname, + * e.g. "test.example.org" -> "*.example.org". + * Returns string which must be free()'d by the caller, or NULL on error. + */ +char * +ssl_wildcardify(const char *hostname) +{ + char *dot, *wildcarded; + size_t dotsz; + + if (!(dot = (char *) strchr(hostname, '.'))) + return strdup("*"); + dotsz = strlen(dot); + if (!(wildcarded = (char *) malloc(dotsz + 2))) + return NULL; + wildcarded[0] = '*'; + for (size_t i = 0; i < dotsz; i++) + { + wildcarded[i + 1] = dot[i]; + } + wildcarded[dotsz + 1] = '\0'; + return wildcarded; +} + +/* + * Match DNS name against certificate subject CN and subjectAltNames DNS names. + * Returns 1 if any name matches, 0 if none matches. + */ +int +ssl_x509_names_match(X509 *crt, const char *dnsname) +{ + GENERAL_NAMES *altnames; + char *cn; + size_t cnsz, dnsnamesz; + + dnsnamesz = strlen(dnsname); + + cn = ssl_x509_subject_cn(crt, &cnsz); + if (cn && ssl_dnsname_match(cn, cnsz, dnsname, dnsnamesz)) + { + free(cn); + return 1; + } + if (cn) + { + free(cn); + } + + altnames = (GENERAL_NAMES *) X509_get_ext_d2i(crt, NID_subject_alt_name, 0, 0); + if (!altnames) + return 0; + for (int i = 0; i < sk_GENERAL_NAME_num(altnames); i++) + { + GENERAL_NAME *gn = sk_GENERAL_NAME_value(altnames, i); + if (gn->type == GEN_DNS) + { + unsigned char *altname; + int altnamesz; + ASN1_STRING_to_UTF8(&altname, gn->d.dNSName); + altnamesz = ASN1_STRING_length(gn->d.dNSName); + if (altnamesz < 0) + break; + if (ssl_dnsname_match((char *) altname, + (size_t) altnamesz, + dnsname, dnsnamesz)) + { + OPENSSL_free((char *) altname); + GENERAL_NAMES_free(altnames); + return 1; + } + OPENSSL_free((char *) altname); + } + } + GENERAL_NAMES_free(altnames); + return 0; +} + +/* + * Returns a NULL terminated array of pointers to all common names found + * in the Subject DN CN and subjectAltNames extension (DNSName only). + * Caller must free returned buffer and all pointers within. + * Embedded NULL characters in hostnames are replaced with '!'. + */ +char **ssl_x509_names(X509 *crt) +{ + GENERAL_NAMES *altnames; + char *cn; + size_t cnsz; + char **res, **p; + size_t count; + + cn = ssl_x509_subject_cn(crt, &cnsz); + altnames = (GENERAL_NAMES *) X509_get_ext_d2i(crt, NID_subject_alt_name, NULL, NULL); + + count = (size_t) (altnames ? sk_GENERAL_NAME_num(altnames) : 0) + (cn ? 2 : 1); + res = (char **) malloc(count * sizeof(char *)); + + if (!res) + return NULL; + p = res; + if (cn) + *(p++) = cn; + if (!altnames) + { + *p = NULL; + return res; + } + for (int i = 0; i < sk_GENERAL_NAME_num(altnames); i++) + { + GENERAL_NAME *gn = sk_GENERAL_NAME_value(altnames, i); + if (gn->type == GEN_DNS) + { + unsigned char *altname; + int altnamesz; + + ASN1_STRING_to_UTF8(&altname, gn->d.dNSName); + if (!altname) + break; + altnamesz = ASN1_STRING_length(gn->d.dNSName); + if (altnamesz < 0) + { + OPENSSL_free((char *) altname); + break; + } + *p = (char *) malloc(altnamesz + 1); + if (!*p) + { + OPENSSL_free((char *) altname); + GENERAL_NAMES_free(altnames); + for (p = res; *p; p++) + free(*p); + free(res); + return NULL; + } + for (int j = 0; j < altnamesz; j++) + { + (*p)[j] = altname[j] ? altname[j] : '!'; + } + (*p)[altnamesz] = '\0'; + OPENSSL_free((char *) altname); + p++; + } + } + *p = NULL; + GENERAL_NAMES_free(altnames); + return res; +} + +/* + * Returns a printable representation of a certificate's common names found + * in the Subject DN CN and subjectAltNames extension, separated by slashes. + * Caller must free returned buffer. + * Embedded NULL characters in hostnames are replaced with '!'. + * If no CN and no subjectAltNames are found, returns "-". + */ +char *ssl_x509_names_to_str(X509 *crt) +{ + char **names; + size_t sz; + char *buf = NULL, *next; + + names = ssl_x509_names(crt); + if (!names) + return strdup("-"); + + sz = 0; + for (char **p = names; *p; p++) + { + sz += strlen(*p) + 1; + } + if (!sz) + { + goto out1; + } + + if (!(buf = (char *) malloc(sz))) + goto out2; + next = buf; + for (char **p = names; *p; p++) + { + char *src = *p; + while (*src) + { + *next++ = *src++; + } + *next++ = '/'; + } + *--next = '\0'; +out2: + for (char **p = names; *p; p++) + free(*p); +out1: + free(names); + return buf; +} + +/* + * Returns a zero-terminated buffer containing the ASN1 IA5 string. + * Returned buffer must be free()'d by caller. + */ +static char *ssl_ia5string_strdup(ASN1_IA5STRING *ia5) +{ + char *str; + + if (!ia5 || !ia5->length) + return NULL; + + str = (char *) malloc(ia5->length + 1); + if (!str) + return NULL; + memcpy(str, ia5->data, ia5->length); + str[ia5->length] = 0; + return str; +} + +/* + * Returns a NULL terminated array of pointers to copies of Authority + * Information Access (AIA) URLs of a given type contained in the certificate. + * Caller must free returned buffer and all pointers within. + */ +char **ssl_x509_aias(X509 *crt, const int type) +{ + AUTHORITY_INFO_ACCESS *aias; + char **res; + int count, i, j; + + aias = (AUTHORITY_INFO_ACCESS *) X509_get_ext_d2i(crt, NID_info_access, NULL, NULL); + if (!aias || !(count = sk_ACCESS_DESCRIPTION_num(aias))) + return NULL; + + res = (char **) malloc((count + 1) * sizeof(char *)); + if (!res) + { + sk_ACCESS_DESCRIPTION_pop_free(aias, ACCESS_DESCRIPTION_free); + return NULL; + } + + for (i = 0, j = 0; i < count; i++) + { + ACCESS_DESCRIPTION *aia; + + aia = sk_ACCESS_DESCRIPTION_value(aias, i); + if (aia && + OBJ_obj2nid(aia->method) == type && + aia->location->type == GEN_URI) + { + res[j] = ssl_ia5string_strdup(aia->location->d.ia5); + if (res[j]) + j++; + } + } + res[j] = NULL; + sk_ACCESS_DESCRIPTION_pop_free(aias, ACCESS_DESCRIPTION_free); + return res; +} + +/* + * Returns a NULL terminated array of pointers to copies of Authority + * Information Access (AIA) URLs of type OCSP contained in the certificate. + * Caller must free returned buffer and all pointers within. + */ +char **ssl_x509_ocsps(X509 *crt) +{ + return ssl_x509_aias(crt, NID_ad_OCSP); +} + +/* + * Check whether the certificate is valid based on current time. + * Return 1 if valid, 0 otherwise. + */ +int ssl_x509_is_valid(X509 *crt) +{ + if (X509_cmp_current_time(X509_get_notAfter(crt)) <= 0) + return 0; + if (X509_cmp_current_time(X509_get_notBefore(crt)) > 0) + return 0; + return 1; +} + +/* + * Print X509 certificate data to a newly allocated string. + * Caller must free returned string. + * Returns NULL on errors. + */ +char * +ssl_x509_to_str(X509 *crt) +{ + BIO *bio; + char *p, *ret; + size_t sz; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + return NULL; + + X509_print(bio, crt); + sz = (size_t) BIO_get_mem_data(bio, &p); + + if (!(ret = (char *) malloc(sz + 1))) + { + BIO_free(bio); + return NULL; + } + memcpy(ret, p, sz); + ret[sz] = '\0'; + BIO_free(bio); + return ret; +} + +/* + * Convert X509 certificate to PEM format in a newly allocated string. + * Caller must free returned string. + * Returns NULL on errors. + */ +char *ssl_x509_to_pem(X509 *crt) +{ + BIO *bio; + char *p, *ret; + size_t sz; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + return NULL; + + PEM_write_bio_X509(bio, crt); + sz = (size_t) BIO_get_mem_data(bio, &p); + + if (!(ret = (char *) malloc(sz + 1))) + { + BIO_free(bio); + return NULL; + } + + memcpy(ret, p, sz); + ret[sz] = '\0'; + BIO_free(bio); + return ret; +} + +/* + * Print SSL_SESSION data to a newly allocated string. + * Caller must free returned string. + * Returns NULL on errors. + */ +char *ssl_session_to_str(SSL_SESSION *sess) +{ + BIO *bio; + char *p, *ret; + size_t sz; + + bio = BIO_new(BIO_s_mem()); + if (!bio) + return NULL; + + SSL_SESSION_print(bio, sess); + sz = (size_t) BIO_get_mem_data(bio, &p); /* sets p to internal buffer */ + + if (!(ret = (char *) malloc(sz + 1))) + { + BIO_free(bio); + return NULL; + } + memcpy(ret, p, sz); + ret[sz] = '\0'; + BIO_free(bio); + return ret; +} + +/* + * Returns non-zero if the session timeout has not expired yet, + * zero if the session has expired or an error occured. + */ +int ssl_session_is_valid(SSL_SESSION *sess) +{ + time_t curtimet; + long curtime, timeout; + + curtimet = time(NULL); + if (curtimet == (time_t) -1) + return 0; + curtime = curtimet; + if ((curtime < 0) || ((time_t) curtime != curtimet)) + return 0; + timeout = SSL_SESSION_get_timeout(sess); + if (curtime < timeout) + return 0; + return (SSL_SESSION_get_time(sess) > curtime - timeout); +} + +/* + * Returns 1 if buf contains a DER encoded OCSP request which can be parsed. + * Returns 0 otherwise. + */ +int ssl_is_ocspreq(const unsigned char *buf, size_t sz) +{ + OCSP_REQUEST *req; + const unsigned char *p; + + p = (const unsigned char *) buf; + req = d2i_OCSP_REQUEST(NULL, &p, sz); /* increments p */ + if (!req) + return 0; + OCSP_REQUEST_free(req); + return 1; +} + +/* + * Ugly hack to manually parse a clientHello message from a memory buffer. + * This is needed in order to be able to support SNI and STARTTLS. + * + * The OpenSSL SNI API only allows to read the indicated server name at the + * time when we have to provide the server certificate. OpenSSL does not + * allow to asynchroniously read the indicated server name, wait for some + * unrelated event to happen, and then later to provide the server certificate + * to use and continue the handshake. Therefore we resort to parsing the + * server name from the ClientHello manually before OpenSSL gets to work on it. + * + * For STARTTLS support in autossl mode, we need to peek into the buffer of + * received octets and decide whether we have something that resembles a + * (possibly incomplete) ClientHello message, so we can upgrade the connection + * to SSL automatically. + * + * This function takes a buffer containing (part of) a ClientHello message as + * seen on the network as input. + * + * Returns: + * 1 if buf does not contain a complete ClientHello message; + * *clienthello may point to the start of a truncated ClientHello message, + * indicating that the caller should retry later with more bytes available + * 0 if buf contains a complete ClientHello message; + * *clienthello will point to the start of the complete ClientHello message + * + * If a servername pointer was supplied by the caller, and a server name + * extension was found and parsed, the server name is returned in *servername + * as a newly allocated string that must be freed by the caller. This may + * only occur for a return value of 0. + * + * If search is non-zero, then the buffer will be searched for a ClientHello + * message beginning at offsets >= 0, whereas if search is zero, only + * ClientHello messages starting at offset 0 will be considered. + * + * This code currently supports SSL 2.0, SSL 3.0 and TLS 1.0-1.2. + * + * References: + * draft-hickman-netscape-ssl-00: The SSL Protocol + * RFC 6101: The Secure Sockets Layer (SSL) Protocol Version 3.0 + * RFC 2246: The TLS Protocol Version 1.0 + * RFC 3546: Transport Layer Security (TLS) Extensions + * RFC 4346: The Transport Layer Security (TLS) Protocol Version 1.1 + * RFC 4366: Transport Layer Security (TLS) Extensions + * RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 + * RFC 6066: Transport Layer Security (TLS) Extensions: Extension Definitions + */ +int +ssl_tls_clienthello_parse( + const unsigned char *buf, ssize_t sz, int search, + const unsigned char **clienthello, char **servername) +{ +#ifdef DEBUG_CLIENTHELLO_PARSER +#define DBG_printf(...) log_dbg_printf("ClientHello parser: " __VA_ARGS__) +#else /* !DEBUG_CLIENTHELLO_PARSER */ +#define DBG_printf(...) +#endif /* !DEBUG_CLIENTHELLO_PARSER */ + const unsigned char *p = buf; + ssize_t n = sz; + char *sn = NULL; + + ssize_t tlsextslen; + ssize_t sidlen; + ssize_t suiteslen; + ssize_t compslen; + ssize_t msglen; + ssize_t recordlen; + + //===== + + *clienthello = NULL; + + DBG_printf("parsing buffer of sz %zd\n", sz); + + do + { + if (*clienthello) + { + /* + * Rewind after skipping an invalid ClientHello by + * restarting the search one byte after the beginning + * of the last candidate + */ + p = (*clienthello) + 1; + n = sz - (p - buf); + if (sn) + { + free(sn); + sn = NULL; + } + } + + if (search) + { + /* Search for a potential ClientHello */ + while ((n > 0) && (*p != 0x16) && (*p != 0x80)) + { + p++; + n--; + } + if (n <= 0) + { + /* Search completed without a match; reset + * clienthello to NULL to indicate to the + * caller that this buffer does not need to be + * retried */ + DBG_printf("===> No match:" + " rv 1, *clienthello NULL\n"); + *clienthello = NULL; + return 1; + } + } + *clienthello = p; + DBG_printf("candidate at offset %td\n", p - buf); + + DBG_printf("byte 0: %02x\n", *p); + /* +0 0x80 +2 0x01 SSLv2 short header, clientHello; + * +0 0x16 +1 0x03 SSLv3/TLSv1.x handshake, clientHello */ + if (*p == 0x80) + { + /* SSLv2 handled here */ + p++; + n--; + + if (n < 10) + { /* length + 9 */ + DBG_printf("===> [SSLv2] Truncated:" + " rv 1, *clienthello set\n"); + return 1; + } + + DBG_printf("length: %02x\n", p[0]); + if (n - 1 < p[0]) + { + DBG_printf("===> [SSLv2] Truncated:" + " rv 1, *clienthello set\n"); + return 1; + } + p++; + n--; + + DBG_printf("msgtype: %02x\n", p[0]); + if (*p != 0x01) + continue; + p++; + n--; + + DBG_printf("version: %02x %02x\n", p[0], p[1]); + /* byte order is actually swapped for SSLv2 */ + if (!( +#ifdef HAVE_SSLV2 + (p[0] == 0x00 && p[1] == 0x02) || +#endif /* HAVE_SSLV2 */ + (p[0] == 0x03 && p[1] <= 0x03))) + continue; + p += 2; + n -= 2; + + DBG_printf("cipher-spec-len: %02x %02x\n", p[0], p[1]); + ssize_t cipherspec_len = p[0] << 8 | p[1]; + p += 2; + n -= 2; + + DBG_printf("session-id-len: %02x %02x\n", p[0], p[1]); + ssize_t sessionid_len = p[0] << 8 | p[1]; + p += 2; + n -= 2; + + DBG_printf("challenge-len: %02x %02x\n", p[0], p[1]); + ssize_t challenge_len = p[0] << 8 | p[1]; + p += 2; + n -= 2; + if (challenge_len < 16 || challenge_len > 32) + continue; + + if (n < cipherspec_len + + sessionid_len + + challenge_len) + { + DBG_printf("===> [SSLv2] Truncated:" + " rv 1, *clienthello set\n"); + return 1; + } + + p += cipherspec_len + sessionid_len + challenge_len; + n -= cipherspec_len + sessionid_len + challenge_len; + goto done_parsing; + } else if (*p != 0x16) + { + /* this can only happen if search is 0 */ + DBG_printf("===> No match: rv 1, *clienthello NULL\n"); + *clienthello = NULL; + return 1; + } + p++; + n--; + + if (n < 2) + { + DBG_printf("===> Truncated: rv 1, *clienthello set\n"); + return 1; + } + DBG_printf("version: %02x %02x\n", p[0], p[1]); + /* This supports up to TLS 1.2 (0x03 0x03) and will need to be + * updated for TLS 1.3 once that is standardized and still + * compatible with this parser; remember to also update the + * inner version check below */ + if (p[0] != 0x03 || p[1] > 0x03) + continue; + p += 2; + n -= 2; + + if (n < 2) + { + DBG_printf("===> Truncated: rv 1, *clienthello set\n"); + return 1; + } + DBG_printf("length: %02x %02x\n", p[0], p[1]); + + recordlen = p[1] + (p[0] << 8); + DBG_printf("recordlen=%zd\n", recordlen); + p += 2; + n -= 2; + if (recordlen < 36) /* arbitrary size too small for a c-h */ + continue; + if (n < recordlen) + { + DBG_printf("n < recordlen: n=%zd\n", n); + DBG_printf("===> Truncated: rv 1, *clienthello set\n"); + return 1; + } + + /* from here we give up on a candidate if there is not enough + * data available in the buffer, because we already checked the + * availability of the whole record. */ + + if (n < 1) + continue; + DBG_printf("message type: %i\n", *p); + if (*p != 0x01) /* message type: ClientHello */ + continue; + p++; + n--; + + if (n < 3) + continue; + DBG_printf("message len: %02x %02x %02x\n", p[0], p[1], p[2]); + msglen = p[2] + (p[1] << 8) + (p[0] << 16); + DBG_printf("msglen=%zd\n", msglen); + p += 3; + n -= 3; + if (msglen < 32) /* arbitrary size too small for a c-h */ + continue; + if (msglen != recordlen - 4) + { + DBG_printf("msglen != recordlen - 4\n"); + continue; + } + if (n < msglen) + continue; + n = msglen; /* only parse first message */ + + if (n < 2) + continue; + DBG_printf("clienthello version %02x %02x\n", p[0], p[1]); + /* inner version check, see outer one above */ + if (p[0] != 0x03 || p[1] > 0x03) + continue; + p += 2; + n -= 2; + + if (n < 32) + continue; + DBG_printf("clienthello random %02x %02x %02x %02x ...\n", + p[0], p[1], p[2], p[3]); + DBG_printf("compare localtime: %08x\n", + (unsigned int) time(NULL)); + p += 32; + n -= 32; + + if (n < 1) + continue; + DBG_printf("clienthello sidlen %02x\n", *p); + sidlen = *p; /* session id length, 0..32 */ + p += 1; + n -= 1; + if (n < sidlen) + continue; + p += sidlen; + n -= sidlen; + + if (n < 2) + continue; + DBG_printf("clienthello cipher suites length %02x %02x\n", + p[0], p[1]); + + suiteslen = p[1] + (p[0] << 8); + p += 2; + n -= 2; + if (n < suiteslen) + continue; + p += suiteslen; + n -= suiteslen; + + if (n < 1) + continue; + DBG_printf("clienthello compress methods length %02x\n", *p); + + compslen = *p; + p++; + n--; + if (n < compslen) + continue; + p += compslen; + n -= compslen; + + /* begin of extensions */ + + if (n == 0) + { + /* valid ClientHello without extensions */ + DBG_printf("===> Match: rv 0, *clienthello set\n"); + if (servername) + *servername = NULL; + return 0; + } + if (n < 2) + continue; + DBG_printf("tlsexts length %02x %02x\n", p[0], p[1]); + tlsextslen = p[1] + (p[0] << 8); + DBG_printf("tlsextslen %zd\n", tlsextslen); + p += 2; + n -= 2; + if (n < tlsextslen) + continue; + n = tlsextslen; /* only parse exts, ignore trailing bits */ + + while (n > 0) + { + if (n < 4) + goto continue_search; + DBG_printf("tlsext type %02x %02x len %02x %02x\n", + p[0], p[1], p[2], p[3]); + unsigned short exttype = p[1] + (p[0] << 8); + ssize_t extlen = p[3] + (p[2] << 8); + p += 4; + n -= 4; + if (n < extlen) + goto continue_search; + switch (exttype) + { + case 0: + { + ssize_t extn = extlen; + const unsigned char *extp = p; + + if (extn < 2) + goto continue_search; + DBG_printf("list length %02x %02x\n", + extp[0], extp[1]); + ssize_t namelistlen = extp[1] + (extp[0] << 8); + DBG_printf("namelistlen = %zd\n", namelistlen); + extp += 2; + extn -= 2; + + if (namelistlen != extn) + goto continue_search; + + while (extn > 0) + { + if (extn < 3) + goto continue_search; + DBG_printf("ServerName type %02x" + " len %02x %02x\n", + extp[0], extp[1], extp[2]); + unsigned char sntype = extp[0]; + ssize_t snlen = extp[2] + (extp[1] << 8); + extp += 3; + extn -= 3; + if (snlen > extn) + goto continue_search; + if (snlen > TLSEXT_MAXLEN_host_name) + goto continue_search; + /* + * We copy the first name only. + * RFC 6066: "The ServerNameList MUST + * NOT contain more than one name of + * the same name_type." + */ + if (servername && + sntype == 0 && sn == NULL) + { + sn = malloc(snlen + 1); + memcpy(sn, extp, snlen); + sn[snlen] = '\0'; + /* deliberately not checking + * for malformed hostnames + * containing invalid chars */ + } + extp += snlen; + extn -= snlen; + } + break; + } + default: DBG_printf("skipped\n"); + break; + } + p += extlen; + n -= extlen; + } /* while have more extensions */ + +done_parsing:; +#ifdef DEBUG_CLIENTHELLO_PARSER + if (n > 0) { + DBG_printf("unparsed next bytes %02x %02x %02x %02x\n", + p[0], p[1], p[2], p[3]); + } +#endif /* DEBUG_CLIENTHELLO_PARSER */ + DBG_printf("%zd bytes unparsed\n", n); + + /* Valid ClientHello with or without server name */ + DBG_printf("===> Match: rv 0, *clienthello set\n"); + if (servername) + *servername = sn; + return 0; +continue_search:; + } while (search && n > 0); + + /* No valid ClientHello messages found, not even a truncated one */ + DBG_printf("===> No match: rv 1, *clienthello NULL\n"); + *clienthello = NULL; + if (sn) + { + free(sn); + sn = NULL; + } + return 1; +} + +/* vim: set noet ft=c: */ diff --git a/src/ssl.h b/src/ssl.h new file mode 100644 index 0000000..851b012 --- /dev/null +++ b/src/ssl.h @@ -0,0 +1,230 @@ +/*- + * SSLsplit - transparent SSL/TLS interception + * https://www.roe.ch/SSLsplit + * + * Copyright (c) 2009-2018, Daniel Roethlisberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SSL_H +#define SSL_H + +#include "tfe_types.h" + +#include +#include +#include +#include +#include +#include + +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) && !defined(OPENSSL_NO_THREADID) +#define OPENSSL_NO_THREADID +#endif + +#if (OPENSSL_VERSION_NUMBER < 0x0090806FL) && !defined(OPENSSL_NO_TLSEXT) +#define OPENSSL_NO_TLSEXT +#endif + +/* + * ECDH is disabled when building against OpenSSL < 1.0.0e due to issues with + * thread-safety and security in server mode ephemereal ECDH cipher suites. + * http://www.openssl.org/news/secadv_20110906.txt + */ +#if (OPENSSL_VERSION_NUMBER < 0x1000005FL) && !defined(OPENSSL_NO_ECDH) +#define OPENSSL_NO_ECDH +#endif + +#if (OPENSSL_VERSION_NUMBER < 0x0090802FL) && !defined(OPENSSL_NO_ECDSA) +#define OPENSSL_NO_ECDSA +#endif + +#if (OPENSSL_VERSION_NUMBER < 0x0090802FL) && !defined(OPENSSL_NO_EC) +#define OPENSSL_NO_EC +#endif + +/* + * SHA0 was removed in OpenSSL 1.1.0, including OPENSSL_NO_SHA0. + */ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_SHA0) +#define OPENSSL_NO_SHA0 +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define ASN1_STRING_get0_data(value) ASN1_STRING_data(value) +#define SSL_is_server(ssl) (ssl->type != SSL_ST_CONNECT) +#define X509_get_signature_nid(x509) (OBJ_obj2nid(x509->sig_alg->algorithm)) +int DH_set0_pqg(DH *, BIGNUM *, BIGNUM *, BIGNUM *); +#endif + +/* + * The constructors returning a SSL_METHOD * were changed to return + * a const SSL_METHOD * between 0.9.8 and 1.0.0. + */ +#if (OPENSSL_VERSION_NUMBER < 0x1000000fL) +#define CONST_SSL_METHOD SSL_METHOD +#else /* >= OpenSSL 1.0.0 */ +#define CONST_SSL_METHOD const SSL_METHOD +#endif /* >= OpensSL 1.0.0 */ + +/* + * Workaround for bug in OpenSSL 0.9.8y, 1.0.0k and 1.0.1e + * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=703031 + * http://openssl.6102.n7.nabble.com/NULL-ptr-deref-when-calling-SSL-get-certificate-with-1-0-0k-td43636.html + */ +#if (OPENSSL_VERSION_NUMBER == 0x0090819fL) || \ + (OPENSSL_VERSION_NUMBER == 0x100000bfL) || \ + (OPENSSL_VERSION_NUMBER == 0x1000105fL) +#define SSL_get_certificate(x) ssl_ssl_cert_get(x) +X509 * ssl_ssl_cert_get(SSL *); +#endif /* OpenSSL 0.9.8y or 1.0.0k or 1.0.1e */ + +#ifdef OPENSSL_NO_TLSEXT +#ifndef TLSEXT_MAXLEN_host_name +#define TLSEXT_MAXLEN_host_name 255 +#endif /* !TLSEXT_MAXLEN_host_name */ +#endif /* OPENSSL_NO_TLSEXT */ + +/* + * SSL_OP_NO_* is used as an indication that OpenSSL is sufficiently recent + * to have the respective protocol implemented. + * + * OPENSSL_NO_SSL2 indicates the complete removal of SSL 2.0 support. + * + * OPENSSL_NO_SSL3 indicates that no SSL 3.0 connections will be made by + * default, but support is still present, unless OPENSSL_NO_SSL3_METHOD is + * also defined. + */ +#if defined(SSL_OP_NO_SSLv2) && !defined(OPENSSL_NO_SSL2) && \ + defined(WITH_SSLV2) +#define HAVE_SSLV2 +#endif /* SSL_OP_NO_SSLv2 && !OPENSSL_NO_SSL2 && WITH_SSLV2 */ +#if defined(SSL_OP_NO_SSLv3) && !defined(OPENSSL_NO_SSL3_METHOD) +#define HAVE_SSLV3 +#endif /* SSL_OP_NO_SSLv2 && !OPENSSL_NO_SSL3_METHOD */ +#ifdef SSL_OP_NO_TLSv1 +#define HAVE_TLSV10 +#endif /* SSL_OP_NO_TLSv1 */ +#ifdef SSL_OP_NO_TLSv1_1 +#define HAVE_TLSV11 +#endif /* SSL_OP_NO_TLSv1_1 */ +#ifdef SSL_OP_NO_TLSv1_2 +#define HAVE_TLSV12 +#endif /* SSL_OP_NO_TLSv1_2 */ + +#ifdef HAVE_SSLV2 +#define SSL2_S "ssl2 " +#else /* !HAVE_SSLV2 */ +#define SSL2_S "" +#endif /* !HAVE_SSLV2 */ +#ifdef HAVE_SSLV3 +#define SSL3_S "ssl3 " +#else /* !HAVE_SSLV3 */ +#define SSL3_S "" +#endif /* !HAVE_SSLV3 */ +#ifdef HAVE_TLSV10 +#define TLS10_S "tls10 " +#else /* !HAVE_TLSV10 */ +#define TLS10_S "" +#endif /* !HAVE_TLSV10 */ +#ifdef HAVE_TLSV11 +#define TLS11_S "tls11 " +#else /* !HAVE_TLSV11 */ +#define TLS11_S "" +#endif /* !HAVE_TLSV11 */ +#ifdef HAVE_TLSV12 +#define TLS12_S "tls12 " +#else /* !HAVE_TLSV12 */ +#define TLS12_S "" +#endif /* !HAVE_TLSV12 */ +#define SSL_PROTO_SUPPORT_S SSL2_S SSL3_S TLS10_S TLS11_S TLS12_S + +void ssl_openssl_version(void); +int ssl_init(void) WUNRES; +int ssl_reinit(void) WUNRES; +void ssl_fini(void); + +char * ssl_sha1_to_str(unsigned char *, int) NONNULL(1) MALLOC; + +char * ssl_ssl_state_to_str(SSL *) NONNULL(1) MALLOC; +char * ssl_ssl_masterkey_to_str(SSL *) NONNULL(1) MALLOC; + +#ifndef OPENSSL_NO_DH +DH * ssl_tmp_dh_callback(SSL *, int, int) NONNULL(1) MALLOC; +DH * ssl_dh_load(const char *) NONNULL(1) MALLOC; +void ssl_dh_refcount_inc(DH *) NONNULL(1); +#endif /* !OPENSSL_NO_DH */ + +#ifndef OPENSSL_NO_EC +EC_KEY * ssl_ec_by_name(const char *) MALLOC; +#endif /* !OPENSSL_NO_EC */ + +EVP_PKEY * ssl_key_load(const char *) NONNULL(1) MALLOC; +EVP_PKEY * ssl_key_genrsa(const int) MALLOC; +void ssl_key_refcount_inc(EVP_PKEY *) NONNULL(1); +#define SSL_KEY_IDSZ 20 +int ssl_key_identifier_sha1(EVP_PKEY *, unsigned char *) NONNULL(1,2); +char * ssl_key_identifier(EVP_PKEY *, int) NONNULL(1) MALLOC; + +#ifndef OPENSSL_NO_TLSEXT +int ssl_x509_v3ext_add(X509V3_CTX *, X509 *, char *, char *) NONNULL(1,2,3,4); +int ssl_x509_v3ext_copy_by_nid(X509 *, X509 *, int) NONNULL(1,2); +#endif /* !OPENSSL_NO_TLSEXT */ +int ssl_x509_serial_copyrand(X509 *, X509 *) NONNULL(1,2); +X509 * ssl_x509_forge(X509 *, EVP_PKEY *, X509 *, EVP_PKEY *, + const char *, const char *) + NONNULL(1,2,3,4) MALLOC; +X509 * ssl_x509_load(const char *) NONNULL(1) MALLOC; +char * ssl_x509_subject(X509 *) NONNULL(1) MALLOC; +char * ssl_x509_subject_cn(X509 *, size_t *) NONNULL(1,2) MALLOC; +#define SSL_X509_FPRSZ 20 +int ssl_x509_fingerprint_sha1(X509 *, unsigned char *) NONNULL(1,2); +char * ssl_x509_fingerprint(X509 *, int) NONNULL(1) MALLOC; +char ** ssl_x509_names(X509 *) NONNULL(1) MALLOC; +int ssl_x509_names_match(X509 *, const char *) NONNULL(1,2); +char * ssl_x509_names_to_str(X509 *) NONNULL(1) MALLOC; +char ** ssl_x509_aias(X509 *, const int) NONNULL(1) MALLOC; +char ** ssl_x509_ocsps(X509 *) NONNULL(1) MALLOC; +int ssl_x509_is_valid(X509 *) NONNULL(1) WUNRES; +char * ssl_x509_to_str(X509 *) NONNULL(1) MALLOC; +char * ssl_x509_to_pem(X509 *) NONNULL(1) MALLOC; +void ssl_x509_refcount_inc(X509 *) NONNULL(1); + +int ssl_x509chain_load(X509 **, STACK_OF(X509) **, const char *) NONNULL(2,3); +void ssl_x509chain_use(SSL_CTX *, X509 *, STACK_OF(X509) *) NONNULL(1,2,3); + +char * ssl_session_to_str(SSL_SESSION *) NONNULL(1) MALLOC; +int ssl_session_is_valid(SSL_SESSION *) NONNULL(1); + +int ssl_is_ocspreq(const unsigned char *, size_t) NONNULL(1) WUNRES; + +int ssl_tls_clienthello_parse(const unsigned char *, ssize_t, int, + const unsigned char **, char **) + NONNULL(1,4) WUNRES; +int ssl_dnsname_match(const char *, size_t, const char *, size_t) + NONNULL(1,3) WUNRES; +char * ssl_wildcardify(const char *) NONNULL(1) MALLOC; + +#endif /* !SSL_H */ + +/* vim: set noet ft=c: */ diff --git a/src/ssl_stream.cpp b/src/ssl_stream.cpp new file mode 100644 index 0000000..34a8544 --- /dev/null +++ b/src/ssl_stream.cpp @@ -0,0 +1,901 @@ +#include "ssl.h" + + +/* forward declaration of OpenSSL callbacks */ +#ifndef OPENSSL_NO_TLSEXT +static int pxy_ossl_servername_cb(SSL * ssl, int * al, void * arg); +#endif /* !OPENSSL_NO_TLSEXT */ +static int pxy_ossl_sessnew_cb(SSL *, SSL_SESSION *); +static void pxy_ossl_sessremove_cb(SSL_CTX *, SSL_SESSION *); +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static SSL_SESSION * pxy_ossl_sessget_cb(SSL *, unsigned char *, int, int *); +#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ +static SSL_SESSION * pxy_ossl_sessget_cb(SSL *, const unsigned char *, int, + int *); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + +struct peek_sni_ctx +{ + /* ssl */ + unsigned char sni_peek_retries; /* max 64 SNI parse retries */ + char* sni; + struct event* ev; + struct event_base* evbase; + +}; +void peek_sni_ctx_free(void* ctx) +{ + struct peek_sni_ctx * _ctx=(struct peek_sni_ctx *)ctx; + event_free(_ctx->ev); + _ctx->ev = NULL; + free(_ctx->sni); + _ctx->sni=NULL; + free(_ctx); + return; +} + +static void peek_sni_cb(evutil_socket_t fd, short what, void * arg) +{ + struct promise* promise=(struct promise*)arg; + struct peek_sni_ctx* ctx= (struct peek_sni_ctx*)promise->ctx; + + unsigned char buf[1024]; + ssize_t n=0; + const unsigned char * chello=NULL; + int rv=0; + + n = recv(fd, buf, sizeof(buf), MSG_PEEK); + if (n == -1) + { + log_err_printf("Error peeking on fd, aborting connection\n"); + goto promise_failed; + } + + if (n == 0) + { + goto promise_failed; + } + + rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &ctx->sni); + if ((rv == 1) && !chello) + { + log_err_printf("Peeking did not yield a (truncated) ClientHello message, aborting connection\n"); + goto promise_failed; + } + + if ((rv == 1) && chello && (ctx->sni_peek_retries++ < 50)) + { + /* ssl_tls_clienthello_parse indicates that we + * should retry later when we have more data, and we + * haven't reached the maximum retry count yet. + * Reschedule this event as timeout-only event in + * order to prevent busy looping over the read event. + * Because we only peeked at the pending bytes and + * never actually read them, fd is still ready for + * reading now. We use 25 * 0.2 s = 5 s timeout. */ + struct timeval retry_delay = {0, 100}; + + event_free(ctx->ev); + ctx->ev = event_new(ctx->evbase, fd, 0, peek_sni_cb, promise); + assert(ctx->ev!=NULL); + event_add(ctx->ev, &retry_delay); + return; + } + promise_set_ctx(promise, NULL, NULL); + promise->f.cb_success(ctx->sni,promise->f.user); + peek_sni_ctx_free(ctx); + + return; + +promise_failed: + promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"too many tries",promise->f.user); + peek_sni_ctx_free(ctx); + promise_set_ctx(promise, NULL,NULL); + return; + +} + +void ssl_async_peek_sni( struct future* future, evutil_socket_t fd, struct event_base *evbase) +{ + struct event * ev=NULL; + struct promise* p=future_to_promise(future); + struct peek_sni_ctx* ctx=ALLOC(struct peek_sni_ctx, 1); + ctx->ev = event_new(evbase, fd, EV_READ, peek_sni_cb, p); + event_add(evbase, NULL); + promise_set_ctx(p, ctx,peek_sni_ctx_free); + return; +} + +struct ssl_connect_origin_ctx +{ + struct bufferevent *bev; + SSL* ssl; + +}; +void ssl_connect_origin_ctx_free(struct ssl_connect_origin_ctx* ctx) +{ + ctx->ssl=NULL; + bufferevent_free(ctx->bev); + ctx->bev=NULL; + //Do not free bev and ssl, reserved for user. + free(ctx); +} +/* + * Callback for meta events on the up- and downstream connection bufferevents. + * Called when EOF has been reached, a connection has been made, and on errors. + */ +static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, void * arg) +{ + struct promise* promise=(struct promise*)arg; + struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx*)promise_dettach_ctx(promise); + + struct tfe_stream_private* _stream = (struct tfe_stream_private*)arg; + + if (events & BEV_EVENT_ERROR) + { + promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"ssl connect failed",promise->f.user); + } + else + { + if (events & BEV_EVENT_CONNECTED) + { + promise->f.cb_success(ctx->bev,promise->f.user); + ctx->bev=NULL; + } + + else + { + assert(0); + } + } + ssl_connect_origin_ctx_free(ctx); + return +} + +void ssl_async_connect_origin(struct future* future, evutil_socket_t fd, const char* sni, struct event_base *evbase, struct tfe_config *opts) +{ + struct promise* p=future_to_promise(future); + struct ssl_connect_origin_ctx* ctx=ALLOC(struct ssl_connect_origin_ctx, 1); + ctx->ssl=upstream_ssl_create(opts,sni); + ctx->bev = bufferevent_openssl_socket_new(evbase, fd, ctx->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS); + promise_set_ctx(p, ctx, ssl_connect_origin_ctx_free); + bufferevent_openssl_set_allow_dirty_shutdown(ctx->bev, 1); + bufferevent_setcb(ctx->bev, NULL, NULL, ssl_connect_origin_eventcb, p); + bufferevent_enable(ctx->bev, EV_READ | EV_WRITE); +} + +/* + * Dump information on a certificate to the debug log. + */ +static void pxy_debug_crt(X509 * crt) +{ + char * sj = ssl_x509_subject(crt); + if (sj) + { + log_dbg_printf("Subject DN: %s\n", sj); + free(sj); + } + + char * names = ssl_x509_names_to_str(crt); + if (names) + { + log_dbg_printf("Common Names: %s\n", names); + free(names); + } + + char * fpr; + if (!(fpr = ssl_x509_fingerprint(crt, 1))) + { + log_err_printf("Warning: Error generating X509 fingerprint\n"); + } + else + { + log_dbg_printf("Fingerprint: %s\n", fpr); + free(fpr); + } + +#ifdef DEBUG_CERTIFICATE + /* dump certificate */ + log_dbg_print_free(ssl_x509_to_str(crt)); + log_dbg_print_free(ssl_x509_to_pem(crt)); +#endif /* DEBUG_CERTIFICATE */ +} + +static void +pxy_log_connect_nonhttp(pxy_conn_ctx_t * ctx) +{ + char * msg; + int rv; + + /* + * The following ifdef's within asprintf arguments list generates + * warnings with -Wembedded-directive on some compilers. + * Not fixing the code in order to avoid more code duplication. + */ + + if (!ctx->src.ssl) + { + rv = asprintf(&msg, "%s %s %s %s %s" + #ifdef HAVE_LOCAL_PROCINFO + " %s" + #endif /* HAVE_LOCAL_PROCINFO */ + "\n", + ctx->passthrough ? "passthrough" : "tcp", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str) + ); + } + else + { + rv = asprintf(&msg, "%s %s %s %s %s " + "sni:%s names:%s " + "sproto:%s:%s dproto:%s:%s " + "origcrt:%s usedcrt:%s\n", + ctx->clienthello_found ? "upgrade" : "ssl", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str), + STRORDASH(ctx->sni), + STRORDASH(ctx->ssl_names), + SSL_get_version(ctx->src.ssl), + SSL_get_cipher(ctx->src.ssl), + SSL_get_version(ctx->dst.ssl), + SSL_get_cipher(ctx->dst.ssl), + STRORDASH(ctx->origcrtfpr), + STRORDASH(ctx->usedcrtfpr) + ); + } + if ((rv < 0) || !msg) + { + ctx->enomem = 1; + goto out; + } + if (!ctx->opts->detach) + { + log_err_printf("%s", msg); + } + if (ctx->opts->connectlog) + { + } + else + { + free(msg); + } +out: + return; +} + +static void +pxy_log_connect_http(pxy_conn_ctx_t * ctx) +{ + char * msg; + int rv; + +#ifdef DEBUG_PROXY + if (ctx->passthrough) { + log_err_printf("Warning: pxy_log_connect_http called while in " + "passthrough mode\n"); + return; + } +#endif + + + /* + * The following ifdef's within asprintf arguments list generates + * warnings with -Wembedded-directive on some compilers. + * Not fixing the code in order to avoid more code duplication. + */ + + if (!ctx->spec->ssl) + { + rv = asprintf(&msg, "http %s %s %s %s %s %s %s %s %s %s\n", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str), + STRORDASH(ctx->http_host), + STRORDASH(ctx->http_method), + STRORDASH(ctx->http_uri), + STRORDASH(ctx->http_status_code), + STRORDASH(ctx->http_content_length), + ctx->ocsp_denied ? " ocsp:denied" : ""); + } + else + { + rv = asprintf(&msg, "https %s %s %s %s %s %s %s %s %s " + "sni:%s names:%s " + "sproto:%s:%s dproto:%s:%s " + "origcrt:%s usedcrt:%s" + #ifdef HAVE_LOCAL_PROCINFO + " %s" + #endif /* HAVE_LOCAL_PROCINFO */ + "%s\n", + STRORDASH(ctx->srchost_str), + STRORDASH(ctx->srcport_str), + STRORDASH(ctx->dsthost_str), + STRORDASH(ctx->dstport_str), + STRORDASH(ctx->http_host), + STRORDASH(ctx->http_method), + STRORDASH(ctx->http_uri), + STRORDASH(ctx->http_status_code), + STRORDASH(ctx->http_content_length), + STRORDASH(ctx->sni), + STRORDASH(ctx->ssl_names), + SSL_get_version(ctx->src.ssl), + SSL_get_cipher(ctx->src.ssl), + SSL_get_version(ctx->dst.ssl), + SSL_get_cipher(ctx->dst.ssl), + STRORDASH(ctx->origcrtfpr), + STRORDASH(ctx->usedcrtfpr), + ctx->ocsp_denied ? " ocsp:denied" : ""); + } + if ((rv < 0) || !msg) + { + ctx->enomem = 1; + goto out; + } + if (!ctx->opts->detach) + { + log_err_printf("%s", msg); + } + if (ctx->opts->connectlog) + { + } + + else + { + free(msg); + } +out: + return; +} + +/* + * Called by OpenSSL when a new src SSL session is created. + * OpenSSL increments the refcount before calling the callback and will + * decrement it again if we return 0. Returning 1 will make OpenSSL skip + * the refcount decrementing. In other words, return 0 if we did not + * keep a pointer to the object (which we never do here). + */ +#ifdef HAVE_SSLV2 +#define MAYBE_UNUSED +#else /* !HAVE_SSLV2 */ +#define MAYBE_UNUSED UNUSED +#endif /* !HAVE_SSLV2 */ +static int +pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL * ssl, SSL_SESSION * sess) +#undef MAYBE_UNUSED +{ +#ifdef DEBUG_SESSION_CACHE + log_dbg_printf("===> OpenSSL new session callback:\n"); + if (sess) { + log_dbg_print_free(ssl_session_to_str(sess)); + } else { + log_dbg_printf("(null)\n"); + } +#endif /* DEBUG_SESSION_CACHE */ +#ifdef HAVE_SSLV2 + /* Session resumption seems to fail for SSLv2 with protocol + * parsing errors, so we disable caching for SSLv2. */ + if (SSL_version(ssl) == SSL2_VERSION) { + log_err_printf("Warning: Session resumption denied to SSLv2" + "client.\n"); + return 0; + } +#endif /* HAVE_SSLV2 */ + if (sess) + { + cachemgr_ssess_set(sess); + } + return 0; +} + +/* + * Called by OpenSSL when a src SSL session should be removed. + * OpenSSL calls SSL_SESSION_free() after calling the callback; + * we do not need to free the reference here. + */ +static void +pxy_ossl_sessremove_cb(UNUSED SSL_CTX * sslctx, SSL_SESSION * sess) +{ +#ifdef DEBUG_SESSION_CACHE + log_dbg_printf("===> OpenSSL remove session callback:\n"); + if (sess) { + log_dbg_print_free(ssl_session_to_str(sess)); + } else { + log_dbg_printf("(null)\n"); + } +#endif /* DEBUG_SESSION_CACHE */ + if (sess) + { + cachemgr_ssess_del(sess); + } +} + +/* + * Called by OpenSSL when a src SSL session is requested by the client. + */ +static SSL_SESSION * +#if OPENSSL_VERSION_NUMBER < 0x10100000L +pxy_ossl_sessget_cb(UNUSED SSL * ssl, unsigned char * id, int idlen, int * copy) +#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ +pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *copy) +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ +{ + SSL_SESSION * sess; + +#ifdef DEBUG_SESSION_CACHE + log_dbg_printf("===> OpenSSL get session callback:\n"); +#endif /* DEBUG_SESSION_CACHE */ + + *copy = 0; /* SSL should not increment reference count of session */ + sess = (SSL_SESSION *) cachemgr_ssess_get(id, idlen); + +#ifdef DEBUG_SESSION_CACHE + if (sess) { + log_dbg_print_free(ssl_session_to_str(sess)); + } +#endif /* DEBUG_SESSION_CACHE */ + + log_dbg_printf("SSL session cache: %s\n", sess ? "HIT" : "MISS"); + return sess; +} + +/* + * Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX. + */ +static void +pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts) +{ + SSL_CTX_set_options(sslctx, SSL_OP_ALL); +#ifdef SSL_OP_TLS_ROLLBACK_BUG + SSL_CTX_set_options(sslctx, SSL_OP_TLS_ROLLBACK_BUG); +#endif /* SSL_OP_TLS_ROLLBACK_BUG */ +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + SSL_CTX_set_options(sslctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); +#endif /* SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + SSL_CTX_set_options(sslctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); +#endif /* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ +#ifdef SSL_OP_NO_TICKET + SSL_CTX_set_options(sslctx, SSL_OP_NO_TICKET); +#endif /* SSL_OP_NO_TICKET */ + +#ifdef SSL_OP_NO_SSLv2 +#ifdef HAVE_SSLV2 + if (opts->no_ssl2) { +#endif /* HAVE_SSLV2 */ + SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2); +#ifdef HAVE_SSLV2 + } +#endif /* HAVE_SSLV2 */ +#endif /* !SSL_OP_NO_SSLv2 */ +#ifdef HAVE_SSLV3 + if (opts->no_ssl3) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3); + } +#endif /* HAVE_SSLV3 */ +#ifdef HAVE_TLSV10 + if (opts->no_tls10) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1); + } +#endif /* HAVE_TLSV10 */ +#ifdef HAVE_TLSV11 + if (opts->no_tls11) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_1); + } +#endif /* HAVE_TLSV11 */ +#ifdef HAVE_TLSV12 + if (opts->no_tls12) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2); + } +#endif /* HAVE_TLSV12 */ + +#ifdef SSL_OP_NO_COMPRESSION + if (!opts->sslcomp) + { + SSL_CTX_set_options(sslctx, SSL_OP_NO_COMPRESSION); + } +#endif /* SSL_OP_NO_COMPRESSION */ + + SSL_CTX_set_cipher_list(sslctx, opts->ciphers); +} + +/* + * Create and set up a new SSL_CTX instance for terminating SSL. + * Set up all the necessary callbacks, the certificate, the cert chain and key. + */ +static SSL_CTX * downstream_sslctx_create( + pxy_conn_ctx_t * ctx, X509 * crt, STACK_OF(X509) * chain, + EVP_PKEY * key, struct tfe_config* opts, void* cb_arg) +{ + SSL_CTX * sslctx = SSL_CTX_new(opts->sslmethod()); + if (!sslctx) + return NULL; + + pxy_sslctx_setoptions(sslctx, opts); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (opts->sslversion) { + if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || + SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { + SSL_CTX_free(sslctx); + return NULL; + } + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + + SSL_CTX_sess_set_new_cb(sslctx, pxy_ossl_sessnew_cb); + SSL_CTX_sess_set_remove_cb(sslctx, pxy_ossl_sessremove_cb); + SSL_CTX_sess_set_get_cb(sslctx, pxy_ossl_sessget_cb); + SSL_CTX_set_session_cache_mode(sslctx, SSL_SESS_CACHE_SERVER | + SSL_SESS_CACHE_NO_INTERNAL); +#ifdef USE_SSL_SESSION_ID_CONTEXT + SSL_CTX_set_session_id_context(sslctx, (void *)(&ssl_session_context), + sizeof(ssl_session_context)); +#endif /* USE_SSL_SESSION_ID_CONTEXT */ +#ifndef OPENSSL_NO_TLSEXT + SSL_CTX_set_tlsext_servername_callback(sslctx, pxy_ossl_servername_cb); + SSL_CTX_set_tlsext_servername_arg(sslctx, ctx); +#endif /* !OPENSSL_NO_TLSEXT */ +#ifndef OPENSSL_NO_DH + if (ctx->opts->dh) + { + SSL_CTX_set_tmp_dh(sslctx, ctx->opts->dh); + } + else + { + SSL_CTX_set_tmp_dh_callback(sslctx, ssl_tmp_dh_callback); + } +#endif /* !OPENSSL_NO_DH */ +#ifndef OPENSSL_NO_ECDH + if (ctx->opts->ecdhcurve) + { + EC_KEY * ecdh = ssl_ec_by_name(ctx->opts->ecdhcurve); + SSL_CTX_set_tmp_ecdh(sslctx, ecdh); + EC_KEY_free(ecdh); + } + else + { + EC_KEY * ecdh = ssl_ec_by_name(NULL); + SSL_CTX_set_tmp_ecdh(sslctx, ecdh); + EC_KEY_free(ecdh); + } +#endif /* !OPENSSL_NO_ECDH */ + SSL_CTX_use_certificate(sslctx, crt); + SSL_CTX_use_PrivateKey(sslctx, key); + for (int i = 0; i < sk_X509_num(chain); i++) + { + X509 * c = sk_X509_value(chain, i); + ssl_x509_refcount_inc(c); /* next call consumes a reference */ + SSL_CTX_add_extra_chain_cert(sslctx, c); + } + +#ifdef DEBUG_SESSION_CACHE + if (OPTS_DEBUG(ctx->opts)) { + int mode = SSL_CTX_get_session_cache_mode(sslctx); + log_dbg_printf("SSL session cache mode: %08x\n", mode); + if (mode == SSL_SESS_CACHE_OFF) + log_dbg_printf("SSL_SESS_CACHE_OFF\n"); + if (mode & SSL_SESS_CACHE_CLIENT) + log_dbg_printf("SSL_SESS_CACHE_CLIENT\n"); + if (mode & SSL_SESS_CACHE_SERVER) + log_dbg_printf("SSL_SESS_CACHE_SERVER\n"); + if (mode & SSL_SESS_CACHE_NO_AUTO_CLEAR) + log_dbg_printf("SSL_SESS_CACHE_NO_AUTO_CLEAR\n"); + if (mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) + log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_LOOKUP\n"); + if (mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) + log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_STORE\n"); + } +#endif /* DEBUG_SESSION_CACHE */ + + return sslctx; +} + +static int +pxy_srccert_write_to_gendir(pxy_conn_ctx_t * ctx, X509 * crt, int is_orig) +{ + char * fn; + int rv; + + if (!ctx->origcrtfpr) + return -1; + if (is_orig) + { + rv = asprintf(&fn, "%s/%s.crt", ctx->opts->certgendir, + ctx->origcrtfpr); + } + else + { + if (!ctx->usedcrtfpr) + return -1; + rv = asprintf(&fn, "%s/%s-%s.crt", ctx->opts->certgendir, + ctx->origcrtfpr, ctx->usedcrtfpr); + } + if (rv == -1) + { + ctx->enomem = 1; + return -1; + } + + free(fn); + return rv; +} + +static void pxy_srccert_write(pxy_conn_ctx_t * ctx) +{ + if (ctx->opts->certgen_writeall || ctx->generated_cert) + { + if (pxy_srccert_write_to_gendir(ctx, + SSL_get_certificate(ctx->src.ssl), 0) == -1) + { + log_err_printf("Failed to write used certificate\n"); + } + } + if (ctx->opts->certgen_writeall) + { + if (pxy_srccert_write_to_gendir(ctx, ctx->origcrt, 1) == -1) + { + log_err_printf("Failed to write orig certificate\n"); + } + } +} + +/* + * Create new SSL context for the incoming connection, based on the original + * destination SSL certificate. + * Returns NULL if no suitable certificate could be found. + */ + + +static SSL * downstream_ssl_create(cert_t * cert, struct tfe_config* opts) +{ + + assert(cert!=NULL); + SSL_CTX * sslctx = downstream_sslctx_create(ctx, cert->crt, cert->chain, + cert->key); + + if (!sslctx) + { + return NULL; + } + SSL * ssl = SSL_new(sslctx); + SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ + if (!ssl) + { + return NULL; + } +#ifdef SSL_MODE_RELEASE_BUFFERS + /* lower memory footprint for idle connections */ + SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); +#endif /* SSL_MODE_RELEASE_BUFFERS */ + return ssl; +} + +/* + * OpenSSL servername callback, called when OpenSSL receives a servername + * TLS extension in the clientHello. Must switch to a new SSL_CTX with + * a different certificate if we want to replace the server cert here. + * We generate a new certificate if the current one does not match the + * supplied servername. This should only happen if the original destination + * server supplies a certificate which does not match the server name we + * indicate to it. + */ +static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg) +{ + pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; + const char * sn; + X509 * sslcrt; + + if (!(sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) + return SSL_TLSEXT_ERR_NOACK; + + if (!ctx->sni) + { + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Warning: SNI parser yielded no " + "hostname, copying OpenSSL one: " + "[NULL] != [%s]\n", sn); + } + ctx->sni = strdup(sn); + if (!ctx->sni) + { + ctx->enomem = 1; + return SSL_TLSEXT_ERR_NOACK; + } + } + if (OPTS_DEBUG(ctx->opts)) + { + if (strcmp(sn, ctx->sni) != 0) + { + /* + * This may happen if the client resumes a session, but + * uses a different SNI hostname when resuming than it + * used when the session was created. OpenSSL + * correctly ignores the SNI in the ClientHello in this + * case, but since we have already sent the SNI onwards + * to the original destination, there is no way back. + * We log an error and hope this never happens. + */ + log_dbg_printf("Warning: SNI parser yielded different " + "hostname than OpenSSL callback for " + "the same ClientHello message: " + "[%s] != [%s]\n", ctx->sni, sn); + } + } + + /* generate a new certificate with sn as additional altSubjectName + * and replace it both in the current SSL ctx and in the cert cache */ + if (!ctx->immutable_cert && + !ssl_x509_names_match((sslcrt = SSL_get_certificate(ssl)), sn)) + { + X509 * newcrt; + SSL_CTX * newsslctx; + + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Certificate cache: UPDATE " + "(SNI mismatch)\n"); + } + newcrt = ssl_x509_forge(ctx->opts->cacrt, ctx->opts->cakey, + sslcrt, ctx->opts->key, + sn, ctx->opts->crlurl); + if (!newcrt) + { + ctx->enomem = 1; + return SSL_TLSEXT_ERR_NOACK; + } + cachemgr_fkcrt_set(ctx->origcrt, newcrt); + ctx->generated_cert = 1; + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("===> Updated forged server " + "certificate:\n"); + pxy_debug_crt(newcrt); + } + if (WANT_CONNECT_LOG(ctx)) + { + if (ctx->ssl_names) + { + free(ctx->ssl_names); + } + ctx->ssl_names = ssl_x509_names_to_str(newcrt); + if (!ctx->ssl_names) + { + ctx->enomem = 1; + } + } + if (WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) + { + if (ctx->usedcrtfpr) + { + free(ctx->usedcrtfpr); + } + ctx->usedcrtfpr = ssl_x509_fingerprint(newcrt, 0); + if (!ctx->usedcrtfpr) + { + ctx->enomem = 1; + } + } + + newsslctx = downstream_sslctx_create(ctx, newcrt, ctx->opts->chain, + ctx->opts->key); + if (!newsslctx) + { + X509_free(newcrt); + ctx->enomem = 1; + return SSL_TLSEXT_ERR_NOACK; + } + SSL_set_SSL_CTX(ssl, newsslctx); /* decr's old incr new refc */ + SSL_CTX_free(newsslctx); + X509_free(newcrt); + } + else if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Certificate cache: KEEP (SNI match or " + "target mode)\n"); + } + + return SSL_TLSEXT_ERR_OK; +} + +/* + * Create new SSL context for outgoing connections to the original destination. + * If hostname sni is provided, use it for Server Name Indication. + */ +static SSL* upstream_ssl_create(tfe_config*opts, const char* sni, MESA_htable_handle* dsess_cache) +{ + SSL_CTX* sslctx=NULL; + SSL* ssl=NULL; + SSL_SESSION* sess=NULL; + + sslctx = SSL_CTX_new(opts->sslmethod()); + + pxy_sslctx_setoptions(sslctx, opts); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (opts->sslversion) { + if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || + SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { + SSL_CTX_free(sslctx); + return NULL; + } + } +#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + + SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, NULL); + + ssl = SSL_new(sslctx); + SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ + if (!ssl) + { + return NULL; + } +#ifndef OPENSSL_NO_TLSEXT + if (sni) + { + SSL_set_tlsext_host_name(ssl, sni); + } +#endif /* !OPENSSL_NO_TLSEXT */ + +#ifdef SSL_MODE_RELEASE_BUFFERS + /* lower memory footprint for idle connections */ + SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); +#endif /* SSL_MODE_RELEASE_BUFFERS */ + + //Todo: Refactor below session resumption. + /* session resuming based on remote endpoint address and port */ + sess = (SSL_SESSION *) cachemgr_dsess_get((struct sockaddr *) &ctx->addr, + ctx->addrlen, ctx->sni); /* new sess inst */ + if (sess) + { + if (OPTS_DEBUG(opts)) + { + log_dbg_printf("Attempt reuse dst SSL session\n"); + } + SSL_set_session(ssl, sess); /* increments sess refcount */ + SSL_SESSION_free(sess); + } + + return ssl; +} +/* +struct ssl_upstream* ssl_upstream_create(tfe_config*opts, const char* sni) +{ + struct ssl_upstream* upstream=NULL; + SSL* ssl=NULL; + ssl=upstream_ssl_create(opts,sni); + if(ssl==NULL) + { + return NULL; + } + upstream=ALLOC(struct ssl_upstream,1); + upstream->ssl=ssl; + return upstream; +}*/ +void ssl_upstream_free(struct ssl_upstream* p) +{ + X509_free(p->orig_cert); +} +struct ssl_downstream* ssl_downstream_create(void) +{ + struct ssl_downstream* p=NULL; + p=ALLOC(struct ssl_downstream, 1); + return p; +} +void ssl_downstream_free(struct ssl_downstream* p) +{ + free(p->sni); + p->sni=NULL; +// X509_free(p->fake_cert); +} + diff --git a/src/ssl_stream.h b/src/ssl_stream.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/ssl_stream.h @@ -0,0 +1 @@ + diff --git a/src/tfe_main.cpp b/src/tfe_main.cpp new file mode 100644 index 0000000..ecaf95c --- /dev/null +++ b/src/tfe_main.cpp @@ -0,0 +1,41 @@ +/*- +* Tango Frontend Engine (TFE) 3a +* Part of Tango Security Gateway +* +* Copyright (c) 2018-2023, MESA Lab, https://www.mesalab.cn +* All rights reserved. +*/ +#include "proxy.h" +#include "tfe_util.h" +#include "MESA_handle_logger.h" +#include "MESA_prof_load.h" +#include "wired_cfg.h" +#include +#include +struct tfe_instance* g_tfe_instance=NULL; +struct tfe_config * g_tfe_cfg=NULL; +const char* module_name="TFE"; +int main(int argc, char *argv[]) +{ + const char* main_profile="./conf/tfe_main.conf"; + + tfe_proxy *proxy=NULL; + void* wcfg_handle=NULL; + + wcfg_handle=wired_cfg_create(module_name, main_profile); + wired_cfg_init(wcfg_handle); + wired_cfg_destroy(wcfg_handle); + g_tfe_cfg=ALLOC(struct tfe_config, 1); + MESA_load_profile_uint_def(main_profile, "SYSTEM", "thread_num",&(g_tfe_cfg->thread_num),1); + MESA_load_profile_uint_def(main_profile, "SYSTEM", "use_cert_store",&(g_tfe_cfg->use_cert_store),0); + //TODO: Initiate Local Cert Cache, Decryption Mirror, Field Stat, Logger and etc. + //NOTICE: Maat, Cert Store,Tango Cache are initiated in bussiness plugin. + g_tfe_instance=ALLOC(struct tfe_instance,1); + proxy=tfe_proxy_new(g_tfe_cfg); + + //TODO: Drop privs, chroot + tfe_proxy_run(proxy); + proxy_free(proxy); + +} + diff --git a/src/tfe_proxy.cpp b/src/tfe_proxy.cpp new file mode 100644 index 0000000..2d43ad6 --- /dev/null +++ b/src/tfe_proxy.cpp @@ -0,0 +1,404 @@ + +#include "proxy.h" +#include "opts.h" +#include "attrib.h" +#include "tfe_util.h" +#include "tfe_stream.h" +#include "tfe_stream_internal.h" +#include "MESA_handle_logger.h" +#include "io_module_kni.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Proxy engine, built around libevent 2.x. + */ + +#define TFE_BACKLOG_DEFAULT 20 +const char* module_name_pxy="TFE_PXY"; +extern struct tfe_instance* g_tfe_instance; + +__thread int __currect_thread_id; + +struct tfe_thread_manager_ctx +{ + tfe_config *opts; + unsigned int nr_thread; + + tfe_thread_ctx* thr_ctx; +}; + +void free_thread_manager(struct tfe_thread_manager_ctx* ctx) +{ + free(ctx->thr_ctx); + ctx->thr_ctx=NULL; + free(ctx); +} + +static void __dummy_event_handler( + UNUSED evutil_socket_t fd, UNUSED short what, + UNUSED void *arg) +{ + /* do nothing */ +} + +/* + * Thread entry point; runs the event loop of the event base. + * Does not exit until the libevent loop is broken explicitly. + */ +static void *__tfe_thrmgr_thread_entry(void *arg) +{ + struct tfe_thread_ctx *ctx = (struct tfe_thread_ctx *) arg; + struct timeval timer_delay = {60, 0}; + + struct event *ev; + ev = event_new(ctx->evbase, -1, EV_PERSIST, __dummy_event_handler, NULL); + + if (!ev) return NULL; + + evtimer_add(ev, &timer_delay); + ctx->running = 1; + + __currect_thread_id = ctx->thread_id; + MESA_handle_runtime_log(g_tfe_instance->main_logger, RLOG_LV_FATAL, module_name_pxy,"EventThread %d is running...", __currect_thread_id); + + event_base_dispatch(ctx->evbase); + event_free(ev); + return NULL; +} + +int tfe_thread_manager_run(struct tfe_thread_manager_ctx *ctx) +{ + unsigned int thread_id; + + for (thread_id = 0; thread_id < ctx->nr_thread; thread_id++) + { + if (pthread_create(&(ctx->thr_ctx[thread_id].thr), NULL, + __tfe_thrmgr_thread_entry, &(ctx->thr_ctx[thread_id]))) + { + MESA_handle_runtime_log(g_tfe_instance->main_logger, RLOG_LV_FATAL, module_name_pxy, "pthread_create failed."); + } + + while (!ctx->thr_ctx[thread_id].running) + { + sched_yield(); + } + } + MESA_handle_runtime_log(g_tfe_instance->main_logger, RLOG_LV_INFO, module_name_pxy,"Started %d connection handling threads\n", ctx->nr_thread); + return 0; +} + + +static int signals[] = {SIGTERM, SIGQUIT, SIGHUP, SIGINT, SIGPIPE, SIGUSR1}; + +struct tfe_proxy +{ + char name[32]; + struct event_base * evbase; + struct event * sev[sizeof(signals) / sizeof(int)]; + struct event * gcev; + struct proxy_listener_ctx * lctx; + struct tfe_config * opts; + void* main_logger; + unsigned int thread_num; + struct tfe_thread_ctx* work_threads; + void* io_mod; + cert_mgr* cert_mgr; + struct sess_cache* dsess_cache; + struct sess_cache* ssess_cache; +}; + +/* + * Signal handler for SIGTERM, SIGQUIT, SIGINT, SIGHUP, SIGPIPE and SIGUSR1. + */ +static void +proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void * arg) +{ + tfe_proxy * ctx = (tfe_proxy *) arg; + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Received signal %i\n", fd); + } + + switch (fd) + { + case SIGTERM: + case SIGQUIT: + case SIGINT: + case SIGHUP: proxy_loopbreak(ctx); + break; + +#if 0 + case SIGUSR1: + if (log_reopen() == -1) { + log_err_printf("Warning: Failed to reopen logs\n"); + } else { + log_dbg_printf("Reopened log files\n"); + } + break; +#endif + + case SIGPIPE: log_err_printf("Warning: Received SIGPIPE; ignoring.\n"); + break; + default: log_err_printf("Warning: Received unexpected signal %i\n", fd); + break; + } +} + +/* + * Garbage collection handler. + */ +static void +proxy_gc_cb(UNUSED evutil_socket_t fd, UNUSED short what, void * arg) +{ + tfe_proxy * ctx = (tfe_proxy *) arg; + + if (OPTS_DEBUG(ctx->opts)) + log_dbg_printf("Garbage collecting caches started.\n"); + +// cachemgr_gc(); + + if (OPTS_DEBUG(ctx->opts)) + log_dbg_printf("Garbage collecting caches done.\n"); +} +int select_work_thread(struct tfe_proxy *pxy) +{ + + int min_thread_id = 0; + size_t min_load = pxy->work_threads[min_thread_id]->load; + + for (unsigned thread_id = 1; thread_id < pxy->thread_num; thread_id++) + { + if (min_load > pxy->work_threads[thread_id]->load) + { + min_load = pxy->work_threads[thread_id]->load; + min_thread_id = thread_id; + } + } + pxy->work_threads[min_thread_id]->load++; + return min_thread_id; +} +/* + * Callback for accept events on the socket listener bufferevent. + */ +static void io_mod_accept_cb( evutil_socket_t upstream_fd, evutil_socket_t downstream_fd, enum tfe_session_proto session_type, + struct sockaddr * peeraddr, int peeraddrlen, void * arg) +{ + struct tfe_proxy* pxy=(struct tfe_proxy* )arg; + struct tfe_stream_private* stream=NULL; + int tid=-1; + tid=select_work_thread(pxy); + stream=tfe_stream_create(upstream_fd, downstream_fd, session_type,peeraddr, peeraddrlen, pxy->work_threads+tid); + tfe_stream_setup(stream); + return; +} + +/* + * Set up the core event loop. + * Socket clisock is the privsep client socket used for binding to ports. + * Returns ctx on success, or NULL on error. + */ +struct tfe_proxy * tfe_proxy_new(tfe_config * opts) +{ + struct tfe_proxy * proxy=NULL; + size_t i = 0; + int ret=0; + + struct timeval gc_delay = {60, 0}; + + /* adds locking, only required if accessed from separate threads */ + evthread_use_pthreads(); + + if (OPTS_DEBUG(opts)) + { + event_enable_debug_mode(); + } + + proxy = ALLOC(struct tfe_proxy,1); + proxy->opts = opts; + proxy->evbase = event_base_new(); + if (!proxy->evbase) + { + log_err_printf("Error getting event base\n"); + goto error_out; + } + + if (OPTS_DEBUG(opts)) + { + proxy_debug_base(proxy->evbase); + } + switch(opts->input_io_module) + { + case IO_MOD_KNI: + strcpy(proxy->name,"KNI_PXY"); + proxy->io_mod=io_kni_init(opts->kni_path,proxy->evbase); + assert(ret>=0); + break; + case IO_MOD_TPROXY: + default: + assert(0); + break; + } + proxy->thread_num=opts->thread_num; + proxy->cert_mgr=cert_manager_init(); + proxy->dsess_cache=session_cache_init(); + proxy->ssess_cache=session_cache_init(); + + proxy->work_threads=ALLOC(struct tfe_thread_ctx, proxy->thread_num); + for(i=0;ithread_num;i++) + { + proxy->work_threads[i].thread_id=i; + proxy->work_threads[i].evbase = event_base_new(); + proxy->work_threads[i].opts=opts; + proxy->work_threads[i].cert_mgr=proxy->cert_mgr;//reference + proxy->work_threads[i].dsess_cache=proxy->dsess_cache; + proxy->work_threads[i].ssess_cache=proxy->ssess_cache; + + } + //Todo: Not handle signal if have mutliple proxy instance. + for (i = 0; i < (sizeof(signals) / sizeof(int)); i++) + { + proxy->sev[i] = evsignal_new(proxy->evbase, signals[i], proxy_signal_cb, proxy); + if (!proxy->sev[i]) + goto error_out; + evsignal_add(proxy->sev[i], NULL); + } + + proxy->gcev = event_new(proxy->evbase, -1, EV_PERSIST, proxy_gc_cb, proxy); + if (!proxy->gcev) + goto error_out; + + evtimer_add(proxy->gcev, &gc_delay); + return proxy; +error_out: + if (proxy->gcev) + { + event_free(proxy->gcev); + } + + for (size_t i = 0; i < (sizeof(proxy->sev) / sizeof(proxy->sev[0])); i++) + { + if (proxy->sev[i]) + { + event_free(proxy->sev[i]); + } + } + + if (proxy->lctx) + { + proxy_listener_ctx_free(proxy->lctx); + } + for(i=0;ithread_num;i++) + { + proxy->work_threads[i].thread_id=i; + event_base_free(proxy->work_threads[i].evbase); + } + + event_base_free(proxy->evbase); + + free(proxy); + return NULL; +} + +/* + * Run the event loop. Returns when the event loop is cancelled by a signal + * or on failure. + */ +void tfe_proxy_run(struct tfe_proxy * proxy) +{ + if (proxy->opts->detach) + { + event_reinit(proxy->evbase); + } +#ifndef PURIFY + if (OPTS_DEBUG(proxy->opts)) + { + event_base_dump_events(proxy->evbase, stderr); + } +#endif /* PURIFY */ + unsigned int thread_id; + + for (thread_id = 0; thread_id < proxy->thread_num; thread_id++) + { + if (pthread_create(&(proxy->work_threads[thread_id].thr), NULL, + __tfe_thrmgr_thread_entry, &(proxy->work_threads[thread_id]))) + { + MESA_handle_runtime_log(proxy->main_logger, RLOG_LV_FATAL, proxy->name, "pthread_create failed."); + } + + while (!proxy->work_threads[thread_id].running) + { + sched_yield(); + } + } + MESA_handle_runtime_log(proxy->main_logger, RLOG_LV_INFO, proxy->name,"Started %d connection handling threads\n", proxy->nr_thread); + if (OPTS_DEBUG(proxy->opts)) + { + log_dbg_printf("Starting main event loop.\n"); + } + event_base_dispatch(proxy->evbase); + if (OPTS_DEBUG(proxy->opts)) + { + log_dbg_printf("Main event loop stopped.\n"); + } +} + +/* + * Break the loop of the proxy, causing the tfe_proxy_run to return. + */ +void +proxy_loopbreak(tfe_proxy * ctx) +{ + event_base_loopbreak(ctx->evbase); +} + +/* + * Free the proxy data structures. + */ +void +proxy_free(tfe_proxy * ctx) +{ + if (ctx->gcev) + { + event_free(ctx->gcev); + } + if (ctx->lctx) + { + proxy_listener_ctx_free(ctx->lctx); + } + for (size_t i = 0; i < (sizeof(ctx->sev) / sizeof(ctx->sev[0])); i++) + { + if (ctx->sev[i]) + { + event_free(ctx->sev[i]); + } + } + if (ctx->thrmgr) + { + free_thread_manager(ctx->thrmgr); + } + if (ctx->evbase) + { + event_base_free(ctx->evbase); + } + free(ctx); +} + +/* vim: set noet ft=c: */ diff --git a/src/tfe_stream.cpp b/src/tfe_stream.cpp new file mode 100644 index 0000000..abf0723 --- /dev/null +++ b/src/tfe_stream.cpp @@ -0,0 +1,1165 @@ + +#include "tfe_stream.h" +#include "tfe_util.h" +#include "opts.h" +#include "attrib.h" + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define STREAM_EVBASE(s) ((s)->thrmgr_ref->evbase) +/* + * Maximum size of data to buffer per connection direction before + * temporarily stopping to read data from the other end. + */ +#define OUTBUF_LIMIT (1024*1024) + +/* + * Print helper for logging code. + */ +#define STRORDASH(x) (((x)&&*(x))?(x):"-") + +/* + * Context used for all server http_sessions_. + */ +#ifdef USE_SSL_SESSION_ID_CONTEXT +static unsigned long ssl_session_context = 0x31415926; +#endif /* USE_SSL_SESSION_ID_CONTEXT */ + +#define WANT_CONNECT_LOG(ctx) ((ctx)->opts->connectlog||!(ctx)->opts->detach) +#define WANT_CONTENT_LOG(ctx) ((ctx)->opts->contentlog&&!(ctx)->passthrough) + +static pxy_conn_ctx_t * +pxy_conn_ctx_new(proxyspec * spec, tfe_config * opts, tfe_thread_manager_ctx * thrmgr, + evutil_socket_t fd_downstream, evutil_socket_t fd_upstream) +MALLOC NONNULL(1, 2, 3); + +static tfe_stream * +pxy_conn_ctx_new(proxyspec * spec, tfe_config * opts, tfe_thread_manager_ctx * thrmgr, + evutil_socket_t fd_downstream, evutil_socket_t fd_upstream) +{ + pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) malloc(sizeof(pxy_conn_ctx_t)); + if (!ctx) return NULL; + + memset(ctx, 0, sizeof(pxy_conn_ctx_t)); + ctx->spec = spec; + ctx->opts = opts; + ctx->clienthello_search = spec->upgrade; + ctx->fd = fd_downstream; + ctx->peer_fd = fd_upstream; + + ctx->thridx = tfe_thread_manager_attach(thrmgr, &ctx->evbase, &ctx->dnsbase); + ctx->thrmgr = thrmgr; + + CLOG(DEBUG, "conntrace") << string_format("ctx = %p created.", ctx); + + TFE_STAT(TFE_STAT_CONN_CTX_NEW); + return ctx; +} + + +/* forward declaration of libevent callbacks */ +static void pxy_bev_readcb(struct bufferevent *, void *); +static void pxy_bev_writecb(struct bufferevent *, void *); +static void pxy_bev_eventcb(struct bufferevent *, short, void *); +static void stream_fd_readcb(evutil_socket_t, short, void *); + +/* + * Free bufferenvent and close underlying socket properly. + * For OpenSSL bufferevents, this will shutdown the SSL connection. + */ +static void +bufferevent_free_and_close_fd(struct bufferevent * bev, pxy_conn_ctx_t * ctx) +{ + evutil_socket_t fd = bufferevent_getfd(bev); + SSL * ssl = NULL; + + if ((ctx->spec->ssl || ctx->clienthello_found) && !ctx->passthrough) + { + ssl = bufferevent_openssl_get_ssl(bev); /* does not inc refc */ + } + + CLOG(DEBUG, "conntrace") << string_format("%p %p free_and_close_fd", ctx, bev); + bufferevent_free(bev); /* does not free SSL unless the option + BEV_OPT_CLOSE_ON_FREE was set */ + if (ssl) + { + pxy_ssl_shutdown(ctx->opts, ctx->evbase, ssl, fd); + } + else + { + evutil_closesocket(fd); + CLOG(DEBUG, "conntrace") << string_format("%p %p fd %d closed", ctx, bev, fd); + } + + return; +} + +/* + * Set up a bufferevent structure for either a dst or src connection, + * optionally with or without SSL. Sets all callbacks, enables read + * and write events, but does not call bufferevent_socket_connect(). + * + * For dst connections, pass -1 as fd. Pass a pointer to an initialized + * SSL struct as ssl if the connection should use SSL. + * + * Returns pointer to initialized bufferevent structure, as returned + * by bufferevent_socket_new() or bufferevent_openssl_socket_new(). + */ +static struct bufferevent * stream_bufferevent_new(enum tfe_conn_dir direction,enum tfe_session_proto session_type, void* session_ctx, evutil_socket_t fd, struct event_base* evbase, void* cb_para) +{ +// pxy_conn_ctx_t * ctx, evutil_socket_t fd, +// SSL * ssl, bool is_upstream = false + + struct bufferevent * bev = NULL; + struct tfe_stream_private* _stream, enum tfe_conn_dir direction; + /* 上行,SSL的状态全部为CONNECTING */ + switch(_stream->session_type) + { + case SESSION_PROTO_SSL: + SSL* ssl=(SSL*)session_ctx; + bev = bufferevent_openssl_socket_new(evbase, fd, ssl, ((direction == CONN_DIR_UPSTREAM) ? BUFFEREVENT_SSL_CONNECTING + : BUFFEREVENT_SSL_ACCEPTING), BEV_OPT_DEFER_CALLBACKS); + /* Prevent unclean (dirty) shutdowns to cause error + * events on the SSL socket bufferevent. */ + bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); + break; + default: + bev = bufferevent_socket_new(evbase, fd, BEV_OPT_DEFER_CALLBACKS); + break; + } + bufferevent_setcb(bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, cb_para); + bufferevent_enable(bev, EV_READ | EV_WRITE); + return bev; +} + +static int pxy_ocsp_is_valid_uri(const char * uri, pxy_conn_ctx_t * ctx) +{ + char * buf_url; + size_t sz_url; + char * buf_b64; + size_t sz_b64; + unsigned char * buf_asn1; + size_t sz_asn1; + int ret; + + buf_url = (char *) strrchr(uri, '/'); + if (!buf_url) + return 0; + buf_url++; + + /* + * Do some quick checks to avoid unnecessary buffer allocations and + * decoding URL, Base64 and ASN.1: + * - OCSP requests begin with a SEQUENCE (0x30), so the first Base64 + * byte is 'M' or, unlikely but legal, the URL encoding thereof. + * - There should be no query string in OCSP GET requests. + * - Encoded OCSP request ASN.1 blobs are longer than 32 bytes. + */ + if (buf_url[0] != 'M' && buf_url[0] != '%') + return 0; + if (strchr(uri, '?')) + return 0; + sz_url = strlen(buf_url); + if (sz_url < 32) + return 0; + buf_b64 = url_dec(buf_url, sz_url, &sz_b64); + if (!buf_b64) + { + ctx->enomem = 1; + return 0; + } + buf_asn1 = base64_dec(buf_b64, sz_b64, &sz_asn1); + if (!buf_asn1) + { + ctx->enomem = 1; + free(buf_b64); + return 0; + } + ret = ssl_is_ocspreq(buf_asn1, sz_asn1); + free(buf_asn1); + free(buf_b64); + return ret; +} + +/* + * Called after a request header was completely read. + * If the request is an OCSP request, deny the request by sending an + * OCSP response of type tryLater and close the connection to the server. + * + * Reference: + * RFC 2560: X.509 Internet PKI Online Certificate Status Protocol (OCSP) + */ +static void pxy_ocsp_deny(pxy_conn_ctx_t * ctx) +{ + struct evbuffer * inbuf, * outbuf; + static const char ocspresp[] = + "HTTP/1.0 200 OK\r\n" + "Content-Type: application/ocsp-response\r\n" + "Content-Length: 5\r\n" + "Connection: close\r\n" + "\r\n" + "\x30\x03" /* OCSPResponse: SEQUENCE */ + "\x0a\x01" /* OCSPResponseStatus: ENUMERATED */ + "\x03"; /* tryLater (3) */ + + if (!ctx->http_method) + return; + if (!strncasecmp(ctx->http_method, "GET", 3) && + pxy_ocsp_is_valid_uri(ctx->http_uri, ctx)) + goto deny; + if (!strncasecmp(ctx->http_method, "POST", 4) && + ctx->http_content_type && + !strncasecmp(ctx->http_content_type, + "application/ocsp-request", 24)) + goto deny; + return; + +deny: + inbuf = bufferevent_get_input(ctx->src.bev); + outbuf = bufferevent_get_output(ctx->src.bev); + + if (evbuffer_get_length(inbuf) > 0) + { + evbuffer_drain(inbuf, evbuffer_get_length(inbuf)); + } + + bufferevent_free_and_close_fd(ctx->dst.bev, ctx); + ctx->dst.bev = NULL; + ctx->dst.closed = 1; + evbuffer_add_printf(outbuf, ocspresp); + ctx->ocsp_denied = 1; +} + +/* + * Peek into pending data to see if it is an SSL/TLS ClientHello, and if so, + * upgrade the connection from plain TCP to SSL/TLS. + * + * Return 1 if ClientHello was found and connection was upgraded to SSL/TLS, + * 0 otherwise. + * + * WARNING: This is experimental code and will need to be improved. + * + * TODO - enable search and skip bytes before ClientHello in case it does not + * start at offset 0 (i.e. chello > vec_out[0].iov_base) + * TODO - peek into more than just the current segment + * TODO - add retry mechanism for short truncated ClientHello, possibly generic + */ +int +pxy_conn_autossl_peek_and_upgrade(pxy_conn_ctx_t * ctx) +{ + struct evbuffer * inbuf; + struct evbuffer_iovec vec_out[1]; + const unsigned char * chello; + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Checking for a client hello\n"); + } + /* peek the buffer */ + inbuf = bufferevent_get_input(ctx->src.bev); + if (evbuffer_peek(inbuf, 1024, 0, vec_out, 1)) + { + if (ssl_tls_clienthello_parse((const unsigned char *) vec_out[0].iov_base, + vec_out[0].iov_len, 0, &chello, &ctx->sni) == 0) + { + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Peek found ClientHello\n"); + } + ctx->dst.ssl = upstream_ssl_create(ctx); + if (!ctx->dst.ssl) + { + log_err_printf("Error creating SSL for " + "upgrade\n"); + return 0; + } + ctx->dst.bev = bufferevent_openssl_filter_new( + ctx->evbase, ctx->dst.bev, ctx->dst.ssl, + BUFFEREVENT_SSL_CONNECTING, + BEV_OPT_DEFER_CALLBACKS); + if (!ctx->dst.bev) + { + return 0; + } + bufferevent_setcb(ctx->dst.bev, pxy_bev_readcb, + pxy_bev_writecb, pxy_bev_eventcb, + ctx); + bufferevent_enable(ctx->dst.bev, EV_READ | EV_WRITE); + if (OPTS_DEBUG(ctx->opts)) + { + log_err_printf("Replaced dst bufferevent, new " + "one is %p\n", + (void *) ctx->dst.bev); + } + ctx->clienthello_search = 0; + ctx->clienthello_found = 1; + return 1; + } + else + { + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Peek found no ClientHello\n"); + } + return 0; + } + } + return 0; +} + +static void +pxy_conn_terminate_free(pxy_conn_ctx_t * ctx, int is_requestor) +{ + log_err_printf("Terminating connection%s!\n", + ctx->enomem ? " (out of memory)" : ""); + + if (ctx->dst.bev && !ctx->dst.closed) + { + bufferevent_free_and_close_fd(ctx->dst.bev, ctx); + ctx->dst.bev = NULL; + } + + if (ctx->src.bev && !ctx->src.closed) + { + bufferevent_free_and_close_fd(ctx->src.bev, ctx); + ctx->src.bev = NULL; + } + + tfe_stream_free(ctx, is_requestor); +} + +extern int pxy_http_read_cb(pxy_conn_ctx_t * ctx, struct bufferevent * bev); + +void * __http1_connection_ctx_setup(pxy_conn_ctx_t * ctx) +{ + const auto & __source_address = ctx->spec->listen_addr; + const auto & __dest_address = ctx->spec->connect_addr; + + auto __http1_connection_ctx = std::make_unique( + ctx->src.bev, ctx->dst.bev, __source_address, __dest_address); + + auto __http_scan_ctx = std::make_shared(g_tfe_instance, g_tfe_config); + + __http_scan_ctx->handlerConnectionCreate(*__http1_connection_ctx); + __http_scan_ctx->handlerConnectionClose(*__http1_connection_ctx); + + return __http1_connection_ctx.release(); +} + +/* + * Callback for read events on the up- and downstream connection bufferevents. + * Called when there is data ready in the input evbuffer. + */ + +static void pxy_bev_readcb(struct bufferevent * bev, void * arg) +{ +#if 0 + TIMED_FUNC(timerObj); +#endif + + pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; + pxy_conn_desc_t * conn_this = (bev == ctx->src.bev) ? &ctx->src : &ctx->dst; + pxy_conn_desc_t * conn_other = (bev == ctx->src.bev) ? &ctx->dst : &ctx->src; + +#if 0 + CLOG(DEBUG, "conntrace") << "ctx = " << ctx << "direction = " << (bev == ctx->src.bev); + CLOG(DEBUG, "conntrace") << "inbuf length: " << evbuffer_get_length(bufferevent_get_input(bev)); + CLOG(DEBUG, "conntrace") << "outbuf length: " << evbuffer_get_length(bufferevent_get_output(conn_other->bev)); +#endif + + if (!ctx->connected) + { + log_err_printf("readcb called when other end not connected - aborting.\n"); + return; + } + + if (ctx->clienthello_search && pxy_conn_autossl_peek_and_upgrade(ctx)) + { + return; + } + + struct evbuffer * inbuf = bufferevent_get_input(bev); + struct evbuffer * outbuf = bufferevent_get_output(conn_other->bev); + + if (conn_other->closed) + { + log_dbg_printf("Warning: Drained %zu bytes (conn closed)\n", evbuffer_get_length(inbuf)); + evbuffer_drain(inbuf, evbuffer_get_length(inbuf)); + return; + } + + if (ctx->spec->http && ctx->protocol_conn_ctx == nullptr) + { + ctx->protocol_conn_ctx = __http1_connection_ctx_setup(ctx); + } + + auto * http1_connection = reinterpret_cast(ctx->protocol_conn_ctx); + + if (ctx->spec->http && (bev == ctx->src.bev) && !ctx->passthrough) + { + http1_connection->on_connection_read_request(ctx, conn_this, conn_other); + } + + if (ctx->spec->http && (bev == ctx->dst.bev) && !ctx->passthrough) + { + http1_connection->on_connection_read_response(ctx, conn_this, conn_other); + } + + if (ctx->spec->http && http1_connection->NeedToClose()) + { + pxy_conn_terminate_free(ctx, (bev == ctx->src.bev)); + return; + } + + /* out of memory condition? */ + if (ctx->enomem) + { + pxy_conn_terminate_free(ctx, (bev == ctx->src.bev)); + return; + } + + /* no data left after parsing headers? */ + if (evbuffer_get_length(inbuf) == 0) + return; + + if (ctx->passthrough) + { + evbuffer_add_buffer(outbuf, inbuf); + } + else if (evbuffer_get_length(inbuf) != 0) + { + bufferevent_trigger(bev, EV_READ, BEV_OPT_DEFER_CALLBACKS); + } + + if (evbuffer_get_length(outbuf) >= OUTBUF_LIMIT) + { + CLOG(DEBUG, "conntrace") << string_format("ctx = %p, exceed output limit, disable read", ctx); + + /* temporarily disable data source; + * set an appropriate watermark. */ + bufferevent_setwatermark(conn_other->bev, EV_WRITE, OUTBUF_LIMIT / 2, OUTBUF_LIMIT); + bufferevent_disable(bev, EV_READ); + } + + return; +} + +/* + * Callback for write events on the up- and downstream connection bufferevents. + * Called when either all data from the output evbuffer has been written, + * or if the outbuf is only half full again after having been full. + */ +static void pxy_bev_writecb(struct bufferevent * bev, void * arg) +{ + pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; + pxy_conn_desc_t * other = (bev == ctx->src.bev) ? &ctx->dst : &ctx->src; + + struct evbuffer * outbuf = bufferevent_get_output(bev); + +#if 0 + CLOG(DEBUG, "conntrace") << "ctx = " << ctx << "direction = " << (bev == ctx->src.bev); + CLOG(DEBUG, "conntrace") << "length: " << evbuffer_get_length(outbuf); +#endif + + if (other->closed) + { + struct evbuffer * outbuf = bufferevent_get_output(bev); + if (evbuffer_get_length(outbuf) == 0) + { + /* finished writing and other end is closed; + * close this end too and clean up memory */ + bufferevent_free_and_close_fd(bev, ctx); + tfe_stream_free(ctx, (bev == ctx->dst.bev)); + } + return; + } + + if (other->bev && !(bufferevent_get_enabled(other->bev) & EV_READ)) + { + /* data source temporarily disabled; + * re-enable and reset watermark to 0. */ + bufferevent_setwatermark(bev, EV_WRITE, 0, 0); + bufferevent_enable(other->bev, EV_READ); + } +} + +/* + * Callback for meta events on the up- and downstream connection bufferevents. + * Called when EOF has been reached, a connection has been made, and on errors. + */ +static void pxy_bev_eventcb(struct bufferevent * bev, short events, void * arg) +{ + pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; + pxy_conn_desc_t * this_conn = (bev == ctx->src.bev) ? &ctx->src : &ctx->dst; + pxy_conn_desc_t * other_conn = (bev == ctx->src.bev) ? &ctx->dst : &ctx->src; + + int is_requestor = (bev == ctx->src.bev); + + CLOG(DEBUG, "conntrace") << string_format("%p %p eventcb %s %s%s%s%s", arg, (void *) bev, + (bev == ctx->src.bev) ? "src" : "dst", + events & BEV_EVENT_CONNECTED ? "connected" : "", + events & BEV_EVENT_ERROR ? "error" : "", + events & BEV_EVENT_TIMEOUT ? "timeout" : "", + events & BEV_EVENT_EOF ? "eof" : ""); + + if (events & BEV_EVENT_CONNECTED) + { + if (bev != ctx->dst.bev) + { + LOG(DEBUG) << string_format("ctx = %p, src buffer event connected: ignoring event", ctx); + goto connected; + } + + /* dst has connected */ + ctx->connected = 1; + + /* wrap client-side socket in an eventbuffer */ + if ((ctx->spec->ssl || ctx->clienthello_found) && + !ctx->passthrough) + { + ctx->src.ssl = downstream_ssl_create(ctx, this_conn->ssl); + if (!ctx->src.ssl) + { + bufferevent_free_and_close_fd(bev, ctx); + ctx->dst.bev = NULL; + ctx->dst.ssl = NULL; + if (ctx->opts->passthrough && !ctx->enomem) + { + ctx->passthrough = 1; + ctx->connected = 0; + log_dbg_printf("No cert found; " + "falling back " + "to passthrough\n"); + stream_fd_readcb(ctx->fd, 0, ctx); + return; + } + evutil_closesocket(ctx->fd); + tfe_stream_free(ctx, 1); + return; + } + } + + if (ctx->clienthello_found) + { + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("Completing autossl upgrade\n"); + } + + ctx->src.bev = bufferevent_openssl_filter_new(ctx->evbase, ctx->src.bev, ctx->src.ssl, + BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_DEFER_CALLBACKS); + + if (ctx->src.bev) + { + bufferevent_setcb(ctx->src.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx); + bufferevent_enable(ctx->src.bev, EV_READ | EV_WRITE); + } + } + else + { + ctx->src.bev = stream_bufferevent_new(ctx, ctx->fd, ctx->src.ssl); + } + + if (!ctx->src.bev) + { + if (ctx->src.ssl) + { + SSL_free(ctx->src.ssl); + ctx->src.ssl = NULL; + } + bufferevent_free_and_close_fd(bev, ctx); + evutil_closesocket(ctx->fd); + tfe_stream_free(ctx, 1); + return; + } + + /* prepare logging, part 2 */ + if (WANT_CONNECT_LOG(ctx) || WANT_CONTENT_LOG(ctx)) + { + if (sys_sockaddr_str((struct sockaddr *) + &ctx->addr, ctx->addrlen, + &ctx->dsthost_str, + &ctx->dstport_str) != 0) + { + ctx->enomem = 1; + pxy_conn_terminate_free(ctx, 1); + return; + } + + } + +connected: + /* log connection if we don't analyze any headers */ + if ((!this_conn->ssl || (bev == ctx->src.bev)) && + (!ctx->spec->http || ctx->passthrough) && + WANT_CONNECT_LOG(ctx)) + { + pxy_log_connect_nonhttp(ctx); + } + + if (this_conn->ssl) + { + /* write SSL certificates to gendir */ + if ((bev == ctx->src.bev) && ctx->opts->certgendir) + { + pxy_srccert_write(ctx); + } + + /* log master key */ + if (ctx->opts->masterkeylog) + { + char * keystr; + keystr = ssl_ssl_masterkey_to_str(this_conn->ssl); + if ((keystr == NULL)) + { + if (errno == ENOMEM) ctx->enomem = 1; + pxy_conn_terminate_free(ctx, 1); + return; + } + } + } + + if (this_conn->ssl) + { + char * keystr; + /* for SSL, we get two connect events */ + CLOG(DEBUG, "conntrace") << string_format("SSL connected %s [%s]:%s %s %s", + bev == ctx->dst.bev ? "to" : "from", + bev == ctx->dst.bev ? ctx->dsthost_str : ctx->srchost_str, + bev == ctx->dst.bev ? ctx->dstport_str : ctx->srcport_str, + SSL_get_version(this_conn->ssl), SSL_get_cipher(this_conn->ssl), this_conn); + + keystr = ssl_ssl_masterkey_to_str(this_conn->ssl); + if (keystr) + { + log_dbg_print_free(keystr); + } + } + else + { + /* for TCP, we get only a dst connect event, + * since src was already connected from the + * beginning; mirror SSL debug output anyway + * in order not to confuse anyone who might be + * looking closely at the output */ + CLOG(DEBUG, "conntrace") << string_format("TCP connected to [%s]:%s", ctx->dsthost_str, ctx->dstport_str); + CLOG(DEBUG, "conntrace") << string_format("TCP connected from [%s]:%s", ctx->srchost_str, ctx->srcport_str); + } + + return; + } + + if (events & BEV_EVENT_ERROR) + { + unsigned long sslerr; + int have_sslerr = 0; + + /* Can happen for socket errs, ssl errs; + * may happen for unclean ssl socket shutdowns. */ + sslerr = bufferevent_get_openssl_error(bev); + if (sslerr) + have_sslerr = 1; + if (!errno && !sslerr) + { +#if LIBEVENT_VERSION_NUMBER >= 0x02010000 + /* We have disabled notification for unclean shutdowns + * so this should not happen; log a warning. */ + log_err_printf("Warning: Spurious error from " + "bufferevent (errno=0,sslerr=0)\n"); +#else /* LIBEVENT_VERSION_NUMBER < 0x02010000 */ + /* Older versions of libevent will report these. */ + if (OPTS_DEBUG(ctx->opts)) { + log_dbg_printf("Unclean SSL shutdown.\n"); + } +#endif /* LIBEVENT_VERSION_NUMBER < 0x02010000 */ + } + else if (ERR_GET_REASON(sslerr) == + SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE) + { + /* these can happen due to client cert auth, + * only log error if debugging is activated */ + log_dbg_printf("Error from %s bufferevent: " + "%i:%s %lu:%i:%s:%i:%s:%i:%s\n", + (bev == ctx->src.bev) ? "src" : "dst", + errno, + errno ? strerror(errno) : "-", + sslerr, + ERR_GET_REASON(sslerr), + sslerr ? + ERR_reason_error_string(sslerr) : "-", + ERR_GET_LIB(sslerr), + sslerr ? + ERR_lib_error_string(sslerr) : "-", + ERR_GET_FUNC(sslerr), + sslerr ? + ERR_func_error_string(sslerr) : "-"); + while ((sslerr = bufferevent_get_openssl_error(bev))) + { + log_dbg_printf("Additional SSL error: " + "%lu:%i:%s:%i:%s:%i:%s\n", + sslerr, + ERR_GET_REASON(sslerr), + ERR_reason_error_string(sslerr), + ERR_GET_LIB(sslerr), + ERR_lib_error_string(sslerr), + ERR_GET_FUNC(sslerr), + ERR_func_error_string(sslerr)); + } + } + else + { + /* real errors */ + log_err_printf("Error from %s bufferevent: " + "%i:%s %lu:%i:%s:%i:%s:%i:%s\n", + (bev == ctx->src.bev) ? "src" : "dst", + errno, + errno ? strerror(errno) : "-", + sslerr, + ERR_GET_REASON(sslerr), + sslerr ? + ERR_reason_error_string(sslerr) : "-", + ERR_GET_LIB(sslerr), + sslerr ? + ERR_lib_error_string(sslerr) : "-", + ERR_GET_FUNC(sslerr), + sslerr ? + ERR_func_error_string(sslerr) : "-"); + while ((sslerr = bufferevent_get_openssl_error(bev))) + { + log_err_printf("Additional SSL error: " + "%lu:%i:%s:%i:%s:%i:%s\n", + sslerr, + ERR_GET_REASON(sslerr), + ERR_reason_error_string(sslerr), + ERR_GET_LIB(sslerr), + ERR_lib_error_string(sslerr), + ERR_GET_FUNC(sslerr), + ERR_func_error_string(sslerr)); + } + } + + if (!ctx->connected) + { + /* the callout to the original destination failed, + * e.g. because it asked for client cert auth, so + * close the accepted socket and clean up */ + if (bev == ctx->dst.bev && ctx->dst.ssl && + ctx->opts->passthrough && have_sslerr) + { + /* ssl callout failed, fall back to plain + * TCP passthrough of SSL connection */ + bufferevent_free_and_close_fd(bev, ctx); + ctx->dst.bev = NULL; + ctx->dst.ssl = NULL; + ctx->passthrough = 1; + log_dbg_printf("SSL dst connection failed; fal" + "ling back to passthrough\n"); + stream_fd_readcb(ctx->fd, 0, ctx); + return; + } + evutil_closesocket(ctx->fd); + other_conn->closed = 1; + } + else if (!other_conn->closed) + { + /* if the other end is still open and doesn't have data + * to send, close it, otherwise its writecb will close + * it after writing what's left in the output buffer */ + struct evbuffer * outbuf; + outbuf = bufferevent_get_output(other_conn->bev); + if (evbuffer_get_length(outbuf) == 0) + { + bufferevent_free_and_close_fd(other_conn->bev, ctx); + other_conn->bev = NULL; + other_conn->closed = 1; + } + } + goto leave; + } + + if (events & BEV_EVENT_EOF) + { +#ifdef DEBUG_PROXY + if (OPTS_DEBUG(ctx->opts)) { + log_dbg_printf("evbuffer size at EOF: " + "i:%zu o:%zu i:%zu o:%zu\n", + evbuffer_get_length( + bufferevent_get_input(bev)), + evbuffer_get_length( + bufferevent_get_output(bev)), + evbuffer_get_length( + other->closed ? 0 : + bufferevent_get_input(other->bev)), + evbuffer_get_length( + other->closed ? 0 : + bufferevent_get_output(other->bev)) + ); + } +#endif /* DEBUG_PROXY */ + if (!ctx->connected) + { + log_dbg_printf("EOF on outbound connection before " + "connection establishment\n"); + evutil_closesocket(ctx->fd); + other_conn->closed = 1; + } + else if (!other_conn->closed) + { + /* if there is data pending in the closed connection, + * handle it here, otherwise it will be lost. */ + if (evbuffer_get_length(bufferevent_get_input(bev))) + { + pxy_bev_readcb(bev, ctx); + } + /* if the other end is still open and doesn't + * have data to send, close it, otherwise its + * writecb will close it after writing what's + * left in the output buffer. */ + if (evbuffer_get_length(bufferevent_get_output(other_conn->bev)) == 0) + { + bufferevent_free_and_close_fd(other_conn->bev, ctx); + other_conn->bev = NULL; + other_conn->closed = 1; + } + } + goto leave; + } + + log_err_printf("Unknown bufferevent 0x%02X\n", (int) events); + return; + +leave: + /* we only get a single disconnect event here for both connections */ + if (OPTS_DEBUG(ctx->opts)) + { + log_dbg_printf("%s disconnected to [%s]:%s\n", + this_conn->ssl ? "SSL" : "TCP", + ctx->dsthost_str, ctx->dstport_str); + log_dbg_printf("%s disconnected from [%s]:%s\n", + this_conn->ssl ? "SSL" : "TCP", + ctx->srchost_str, ctx->srcport_str); + } + + this_conn->closed = 1; + bufferevent_free_and_close_fd(bev, ctx); + this_conn->bev = NULL; + if (other_conn->closed) + { + tfe_stream_free(ctx, is_requestor); + } +} + +/* + * Complete the connection. This gets called after finding out where to + * connect to. + */ +static void tfe_stream_connect(struct tfe_stream_private* _stream, enum tfe_conn_dir direction) +{ + struct tfe_stream* conn_pub=&(_stream->head); + struct event_base* evbase=STREAM_EVBASE(_stream); + /* create server-side socket and eventbuffer */ + switch(_stream->session_type) + { + case SESSION_PROTO_SSL: + if(!_stream->passthrough) + { + break; + } + _stream->ssl_upstream=ssl_upstream_create(_stream->thrmgr_ref->opts, _stream->ssl_downstream->sni); + _stream->head.upstream.bev = stream_bufferevent_new(SESSION_PROTO_SSL,CONN_DIR_UPSTREAM, _stream->ssl_upstream->ssl, _stream->fd_upstream, evbase, _stream); + break; + default: + stream_bufferevent_new(SESSION_PROTO_PLAIN, NULL, _stream->fd_upstream, evbase, _stream); + break; + } + + pxy_conn_ctx_t * ctx; + + /* 对于明文上游连接,直接出发CONNECT事件 + * 密文的上游连接,需要等SSL连接建立后,由libevent触发 + */ + if (ctx->peer_fd == 0) + { + bufferevent_socket_connect(ctx->dst.bev, (struct sockaddr *) &ctx->addr, ctx->addrlen); + } + if (ctx->peer_fd > 0 && ctx->spec->ssl == 0) + { + bufferevent_trigger_event(ctx->dst.bev, BEV_EVENT_CONNECTED, 0); + } + + return; + +__close_connection_with_ssl: + if (ctx->dst.ssl != nullptr) + { + SSL_free(ctx->dst.ssl); + ctx->dst.ssl = nullptr; + } + +__close_connection: + if (ctx->fd > 0) evutil_closesocket(ctx->fd); + if (ctx->peer_fd > 0) evutil_closesocket(ctx->peer_fd); + tfe_stream_free(ctx, 1); + + CLOG(DEBUG, "conntrace") << string_format("ctx = %p, fd = %d, peer fd = %d closed.", ctx, ctx->fd, ctx->peer_fd); + return; +} + +/* + * The SNI hostname has been resolved. Fill the first resolved address into + * the context and continue connecting. + */ +static void pxy_sni_resolve_cb(int errcode, struct evutil_addrinfo * ai, void * arg) +{ + pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; + + if (errcode) + { + log_err_printf("Cannot resolve SNI hostname '%s': %s\n", + ctx->sni, evutil_gai_strerror(errcode)); + evutil_closesocket(ctx->fd); + tfe_stream_free(ctx, 1); + return; + } + + memcpy(&ctx->addr, ai->ai_addr, ai->ai_addrlen); + ctx->addrlen = ai->ai_addrlen; + evutil_freeaddrinfo(ai); + tfe_stream_connect(ctx); +} + +/* + * The src fd is readable. This is used to sneak-preview the SNI on SSL + * connections. If ctx->ev is NULL, it was called manually for a non-SSL + * connection. If ctx->passthrough is set, it was called a second time + * after the first ssl callout failed because of client cert auth. + */ +static void stream_fd_readcb(evutil_socket_t fd, short what, void * arg) +{ + struct tfe_stream_private* stream = (struct tfe_stream_private*)arg; + unsigned char buf[1024]; + ssize_t n=0; + const unsigned char * chello=NULL; + int rv=0; + struct tfe_stats *p_stat=&(stream->thrmgr_ref->stat); + struct ssl_downstream* ssl_down=stream->ssl_downstream; + struct tfe_stream* pub=&(stream->head); + + /* for SSL, peek ClientHello and parse SNI from it */ + if (stream->session_type==SESSION_PROTO_SSL && !stream->passthrough) /*&& ctx->ev*/ + { + n = recv(fd, buf, sizeof(buf), MSG_PEEK); + if (n == -1) + { + log_err_printf("Error peeking on fd, aborting connection\n"); + goto __close_connection_fd; + } + + if (n == 0) + { + return; + } + + rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &ssl_down->sni); + if ((rv == 1) && !chello) + { + log_err_printf("Peeking did not yield a (truncated) ClientHello message, aborting connection\n"); + goto __close_connection_fd; + } + + if ((rv == 1) && chello && (ssl_down->sni_peek_retries++ < 50)) + { + /* ssl_tls_clienthello_parse indicates that we + * should retry later when we have more data, and we + * haven't reached the maximum retry count yet. + * Reschedule this event as timeout-only event in + * order to prevent busy looping over the read event. + * Because we only peeked at the pending bytes and + * never actually read them, fd is still ready for + * reading now. We use 25 * 0.2 s = 5 s timeout. */ + struct timeval retry_delay = {0, 100}; + + event_free(ssl_down->ev); + ssl_down->ev = event_new(stream->thrmgr_ref->evbase, fd, 0, stream_fd_readcb, stream); + if (!ssl_down->ev) + { + log_err_printf("Error creating retry event, aborting connection\n"); + goto __close_connection_fd; + } + + event_add(ssl_down->ev, &retry_delay); + return; + } + event_free(ssl_down->ev); + ssl_down->ev = NULL; + } + tfe_stream_connect(stream); + return; + +__close_connection_fd: + p_stat->value[SNI_PEAK_FAIL]++; + if (stream->fd_downstream > 0) + { + evutil_closesocket(stream->fd_downstream); + } + + if (stream->fd_upstream > 0) + { + evutil_closesocket(private->fd_upstream); + } + + tfe_stream_free(stream, 1); + return; +} + +static void tfe_stream_free(struct tfe_stream_private* stream) +{ + pxy_conn_ctx_t * ctx; + int by_requestor; + struct tfe_thread_ctx* thread=stream->thrmgr_ref; + thread->load--; + switch (stream->session_type) + { + case SESSION_PROTO_SSL: + ssl_upstream_free(stream->ssl_upstream); + ssl_downstream_free(stream->ssl_downstream); + thread->stat.value[SSL_NUM]--; + break; + default: + break; + } + free(stream); + thread->stat.value[STREAM_NUM]--; + return; +} +void ssl_get_cert_on_succ(void * result, void * user) +{ + cert_t* cert=(cert_t*)result; + struct tfe_stream_private* _stream=(struct tfe_stream_private*)user; + _stream->ssl_downstream->ssl=downstream_ssl_create(_stream, ); + cert_free(cert); + + + future_destroy(_stream->ssl_downstream->future_get_cert); + _stream->ssl_downstream->future_get_cert=NULL; + return; +} +void ssl_get_cert_on_fail(enum e_future_error err, const char * what, void * user) +{ + //todo + assert(0); +} + +void ssl_conn_origin_on_succ(void * result, void * user) +{ + struct bufferevent *bev=(struct bufferevent *)result; + struct tfe_stream_private* _stream=(struct tfe_stream_private*)user; + + _stream->head.upstream.bev=bev; + _stream->ssl_upstream->ssl=bufferevent_openssl_get_ssl(bev); /* does not inc refc */ + _stream->ssl_upstream->orig_cert=SSL_get_peer_certificate(_stream->ssl_upstream->ssl); + + session_cache_set(_stream->thrmgr_ref->dsess_cache, struct sockaddr * addr, addrlen,_stream->ssl_downstream->sni, SSL_get0_session(_stream->ssl_upstream->ssl)); + + _stream->ssl_downstream->future_get_cert=future_create(ssl_get_cert_on_succ, ssl_get_cert_on_fail, _stream); + cert_mgr_async_get(_stream->thrmgr_ref->cert_mgr, _stream->ssl_downstream->sni, _stream->ssl_downstream->keyring_id,_stream->ssl_upstream->orig_cert); + + future_destroy(_stream->ssl_upstream->conn_ssl_srv); + _stream->ssl_upstream->conn_ssl_srv=NULL; + return; +} +void ssl_conn_origin_on_fail(enum e_future_error err, const char * what, void * user) +{ + //todo + assert(0); +} + + +void peek_sni_on_succ(void* result, void* user) +{ + struct tfe_stream_private* _stream=(struct tfe_stream_private*)user; + assert(_stream->session_type==SESSION_PROTO_SSL); + _stream->ssl_downstream->sni=tfe_strdup((const char *)result); + + future_destroy(ssl_downstream->future_sni_peek); + ssl_downstream->future_sni_peek=NULL; + + _stream->ssl_upstream=ALLOC(struct ssl_upstream,1); + _stream->ssl_upstream->conn_ssl_srv=future_create(ssl_conn_origin_on_succ, ssl_conn_origin_on_fail, _stream); + ssl_async_connect_origin(_stream->ssl_upstream->conn_ssl_srv, _stream->fd_upstream, _stream->ssl_downstream->sni, _stream->thrmgr_ref->evbase, _stream->thrmgr_ref->opts); + +} +void peek_sni_on_fail(enum e_future_error err, const char * what, void * user) +{ + //todo + assert(0); +} + + +/* + * Callback for accept events on the socket listener bufferevent. + * Called when a new incoming connection has been accepted. + * Initiates the connection to the server. The incoming connection + * from the client is not being activated until we have a successful + * connection to the server, because we need the server's certificate + * in order to set up the SSL session to the client. + * For consistency, plain TCP works the same way, even if we could + * start reading from the client while waiting on the connection to + * the server to connect. + */ +void tfe_stream_setup(struct tfe_stream_private* _stream) +{ + struct future* f_sni=NULL; + tfe_thread_ctx* thread=_stream->thrmgr_ref; + switch (_stream->session_type) + { + case SESSION_PROTO_SSL: + // for SSL, defer dst connection setup to initial_readcb + _stream->ssl_downstream=ssl_downstream_create(); + _stream->async_future=future_create(peek_sni_on_succ, peek_sni_on_fail, _stream); + ssl_async_peek_sni(_stream->ssl_downstream->future_sni_peek, _stream->fd_downstream, _stream->thrmgr_ref->evbase); + thread->stat.value[SSL_NUM]++; + break + default: + stream_fd_readcb(_stream->fd_downstream, 0, _stream); + break; + } + return; +} + +struct tfe_stream_private* tfe_stream_create(evutil_socket_t fd_downstream, evutil_socket_t fd_upstream, + struct sockaddr * peeraddr, int peeraddrlen, + enum tfe_session_proto session_type, tfe_thread_ctx* thread) +{ + struct tfe_stream_private* conn_private=NULL; + struct tfe_stream* conn_public=NULL; + conn_private= ALLOC(struct tfe_stream_private, 1); + conn_private->session_type=session_type; + conn_private->fd_downstream=fd_downstream; + conn_private->fd_upstream=fd_upstream; + conn_private->thrmgr_ref=thread; + conn_public=&(conn_private->head); + addr_sock2layer(conn_public->downstream.addr,peeraddr,peeraddrlen); + thread->stat.value[STREAM_NUM]++; + return; +} + +/* vim: set noet ft=c: */ diff --git a/src/tfe_stream_internal.h b/src/tfe_stream_internal.h new file mode 100644 index 0000000..4c307c3 --- /dev/null +++ b/src/tfe_stream_internal.h @@ -0,0 +1,66 @@ +#include "tfe_stream.h" +#include "ssl_stream.h" +#include "tfe_stat.h" +struct tfe_thread_ctx +{ + pthread_t thr; + int thread_id; + size_t load; + struct event_base *evbase; + unsigned char running; + struct tfe_stats stat; + tfe_config* opts; + cert_mgr cert_mgr; + struct sess_cache* dsess_cache; + struct sess_cache* ssess_cache; + +}; + + +//Downstream: comunication form client to proxy +//Upstream: communication form proxy to server +struct ssl_downstream +{ + /* server name indicated by client in SNI TLS extension */ + char *sni; + SSL *ssl; + X509* fake_cert_ref;//? + int keyring_id; + struct future* future_sni_peek; + struct future* future_get_cert; + +}; + +struct ssl_upstream +{ + X509 *orig_cert; + SSL *ssl; + struct future* conn_ssl_srv; +}; + +struct tfe_stream_private +{ + struct tfe_stream head; + enum tfe_session_proto session_type; + + union + { + struct ssl_downstream* ssl_downstream; + void* raw_downstream; + }; + union + { + struct ssl_upstream* ssl_upstream; + void* raw_upstream; + }; + enum tfe_app_proto app_type; + + + unsigned char passthrough; /* 1 if SSL passthrough is active */ + + evutil_socket_t fd_downstream, fd_upstream; + + struct tfe_thread_ctx* thrmgr_ref; + future* async_future; +}; + diff --git a/src/tfe_util.cpp b/src/tfe_util.cpp new file mode 100644 index 0000000..884a359 --- /dev/null +++ b/src/tfe_util.cpp @@ -0,0 +1,23 @@ +#include "tfe_util.h" +int addr_sock2layer(struct sockaddr * sock_addr, int sockaddrlen, struct layer_addr* layer_addr) +{ + return 0; +} +int addr_layer2sock(struct layer_addr* layer_addr,struct sockaddr * sock_addr) +{ + int sockaddrlen=-1; + return sockaddrlen; +} +//functioned as strdup, for dictator compatible. +char* tfe_strdup(const char* s) +{ + char*d=NULL; + if(s==NULL) + { + return NULL; + } + d=(char*)malloc(strlen(s)+1); + memcpy(d,s,strlen(s)+1); + return d; +} +