From: Cheoleun Moon Date: Tue, 16 Feb 2021 04:48:48 +0000 (+0900) Subject: Merge tag 'upstream/3.2.3' into tizen X-Git-Tag: submit/tizen/20210216.094343^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9f53bae9cd55cd02503656bf9f7ac816ba0b95f2;p=platform%2Fupstream%2Flibwebsockets.git Merge tag 'upstream/3.2.3' into tizen Change-Id: I538a1ab537c64150323a469f40078bf001a42c33 --- 9f53bae9cd55cd02503656bf9f7ac816ba0b95f2 diff --cc lib/core-net/private-lib-core-net.h index 16ca46cf,00000000..6894c882 mode 100644,000000..100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@@ -1,1166 -1,0 +1,1168 @@@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#if !defined(__LWS_CORE_NET_PRIVATE_H__) +#define __LWS_CORE_NET_PRIVATE_H__ + +#if !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200112L +#endif + +#include "private-lib-roles.h" + +#ifdef LWS_WITH_IPV6 +#if defined(WIN32) || defined(_WIN32) +#include +#else +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * All lws_tls...() functions must return this type, converting the + * native backend result and doing the extra work to determine which one + * as needed. + * + * Native TLS backend return codes are NOT ALLOWED outside the backend. + * + * Non-SSL mode also uses these types. + */ +enum lws_ssl_capable_status { + LWS_SSL_CAPABLE_ERROR = -1, /* it failed */ + LWS_SSL_CAPABLE_DONE = 0, /* it succeeded */ + LWS_SSL_CAPABLE_MORE_SERVICE_READ = -2, /* retry WANT_READ */ + LWS_SSL_CAPABLE_MORE_SERVICE_WRITE = -3, /* retry WANT_WRITE */ + LWS_SSL_CAPABLE_MORE_SERVICE = -4, /* general retry */ +}; + + +/* + * + * ------ roles ------ + * + */ + +/* null-terminated array of pointers to roles lws built with */ +extern const struct lws_role_ops *available_roles[]; + +#define LWS_FOR_EVERY_AVAILABLE_ROLE_START(xx) { \ + const struct lws_role_ops **ppxx = available_roles; \ + while (*ppxx) { \ + const struct lws_role_ops *xx = *ppxx++; + +#define LWS_FOR_EVERY_AVAILABLE_ROLE_END }} + +/* + * + * ------ event_loop ops ------ + * + */ + +/* enums of socks version */ +enum socks_version { + SOCKS_VERSION_4 = 4, + SOCKS_VERSION_5 = 5 +}; + +/* enums of subnegotiation version */ +enum socks_subnegotiation_version { + SOCKS_SUBNEGOTIATION_VERSION_1 = 1, +}; + +/* enums of socks commands */ +enum socks_command { + SOCKS_COMMAND_CONNECT = 1, + SOCKS_COMMAND_BIND = 2, + SOCKS_COMMAND_UDP_ASSOCIATE = 3 +}; + +/* enums of socks address type */ +enum socks_atyp { + SOCKS_ATYP_IPV4 = 1, + SOCKS_ATYP_DOMAINNAME = 3, + SOCKS_ATYP_IPV6 = 4 +}; + +/* enums of socks authentication methods */ +enum socks_auth_method { + SOCKS_AUTH_NO_AUTH = 0, + SOCKS_AUTH_GSSAPI = 1, + SOCKS_AUTH_USERNAME_PASSWORD = 2 +}; + +/* enums of subnegotiation status */ +enum socks_subnegotiation_status { + SOCKS_SUBNEGOTIATION_STATUS_SUCCESS = 0, +}; + +/* enums of socks request reply */ +enum socks_request_reply { + SOCKS_REQUEST_REPLY_SUCCESS = 0, + SOCKS_REQUEST_REPLY_FAILURE_GENERAL = 1, + SOCKS_REQUEST_REPLY_CONNECTION_NOT_ALLOWED = 2, + SOCKS_REQUEST_REPLY_NETWORK_UNREACHABLE = 3, + SOCKS_REQUEST_REPLY_HOST_UNREACHABLE = 4, + SOCKS_REQUEST_REPLY_CONNECTION_REFUSED = 5, + SOCKS_REQUEST_REPLY_TTL_EXPIRED = 6, + SOCKS_REQUEST_REPLY_COMMAND_NOT_SUPPORTED = 7, + SOCKS_REQUEST_REPLY_ATYP_NOT_SUPPORTED = 8 +}; + +/* enums used to generate socks messages */ +enum socks_msg_type { + /* greeting */ + SOCKS_MSG_GREETING, + /* credential, user name and password */ + SOCKS_MSG_USERNAME_PASSWORD, + /* connect command */ + SOCKS_MSG_CONNECT +}; + +enum { + LWS_RXFLOW_ALLOW = (1 << 0), + LWS_RXFLOW_PENDING_CHANGE = (1 << 1), +}; + +enum lws_parser_return { + LPR_OK = 0, + LPR_FAIL = -1, + LPR_DO_FALLBACK = 2, + LPR_FORBIDDEN = -2 +}; + +enum pmd_return { + PMDR_UNKNOWN, + PMDR_DID_NOTHING, + PMDR_HAS_PENDING, + PMDR_EMPTY_NONFINAL, + PMDR_EMPTY_FINAL, + + PMDR_FAILED = -1 +}; + +typedef union { +#ifdef LWS_WITH_IPV6 + struct sockaddr_in6 sa6; +#endif + struct sockaddr_in sa4; +} sockaddr46; + + +#if defined(LWS_WITH_PEER_LIMITS) +struct lws_peer { + struct lws_peer *next; + struct lws_peer *peer_wait_list; + + time_t time_created; + time_t time_closed_all; + + uint8_t addr[32]; + uint32_t hash; + uint32_t count_wsi; + uint32_t total_wsi; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct lws_peer_role_http http; +#endif + + uint8_t af; +}; +#endif + +enum { + LWS_EV_READ = (1 << 0), + LWS_EV_WRITE = (1 << 1), + LWS_EV_START = (1 << 2), + LWS_EV_STOP = (1 << 3), + + LWS_EV_PREPARE_DELETION = (1u << 31), +}; + +#ifdef LWS_WITH_IPV6 +#define LWS_IPV6_ENABLED(vh) \ + (!lws_check_opt(vh->context->options, LWS_SERVER_OPTION_DISABLE_IPV6) && \ + !lws_check_opt(vh->options, LWS_SERVER_OPTION_DISABLE_IPV6)) +#else +#define LWS_IPV6_ENABLED(context) (0) +#endif + +#ifdef LWS_WITH_UNIX_SOCK +#define LWS_UNIX_SOCK_ENABLED(vhost) \ + (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK) +#else +#define LWS_UNIX_SOCK_ENABLED(vhost) (0) +#endif + +enum uri_path_states { + URIPS_IDLE, + URIPS_SEEN_SLASH, + URIPS_SEEN_SLASH_DOT, + URIPS_SEEN_SLASH_DOT_DOT, +}; + +enum uri_esc_states { + URIES_IDLE, + URIES_SEEN_PERCENT, + URIES_SEEN_PERCENT_H1, +}; + + +#ifndef LWS_NO_CLIENT +struct client_info_stash { + char *address; + char *path; + char *host; + char *origin; + char *protocol; + char *method; + char *iface; + char *alpn; + void *opaque_user_data; /* not allocated or freed by lws */ +}; +#endif + +#define lws_wsi_is_udp(___wsi) (!!___wsi->udp) + +#define LWS_H2_FRAME_HEADER_LENGTH 9 + +int +__lws_sul_insert(lws_dll2_owner_t *own, lws_sorted_usec_list_t *sul, + lws_usec_t us); + +lws_usec_t - __lws_sul_check(lws_dll2_owner_t *own, lws_usec_t usnow); ++__lws_sul_service_ripe(lws_dll2_owner_t *own, lws_usec_t usnow); + +struct lws_timed_vh_protocol { + struct lws_timed_vh_protocol *next; + lws_sorted_usec_list_t sul; + const struct lws_protocols *protocol; + struct lws_vhost *vhost; /* only used for pending processing */ + int reason; + int tsi_req; +}; + +/* + * lws_dsh +*/ + +typedef struct lws_dsh_obj_head { + lws_dll2_owner_t owner; + int kind; +} lws_dsh_obj_head_t; + - typedef struct lws_dsh lws_dsh_t; - +typedef struct lws_dsh_obj { + lws_dll2_t list; /* must be first */ + lws_dsh_t *dsh; /* invalid when on free list */ + size_t size; /* invalid when on free list */ + size_t asize; +} lws_dsh_obj_t; + - typedef struct lws_dsh { ++struct lws_dsh { + lws_dll2_t list; + uint8_t *buf; + lws_dsh_obj_head_t *oha; /* array of object heads/kind */ + size_t buffer_size; + size_t locally_in_use; + size_t locally_free; + int count_kinds; + uint8_t being_destroyed; + /* + * Overallocations at create: + * + * - the buffer itself + * - the object heads array + */ - } lws_dsh_t; ++}; + +/* + * so we can have n connections being serviced simultaneously, + * these things need to be isolated per-thread. + */ + +struct lws_context_per_thread { +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock_stats; + struct lws_mutex_refcount mr; + pthread_t self; +#endif + struct lws_dll2_owner dll_buflist_owner; /* guys with pending rxflow */ + struct lws_dll2_owner seq_owner; /* list of lws_sequencer-s */ + + struct lws_dll2_owner pt_sul_owner; + +#if defined (LWS_WITH_SEQUENCER) + lws_sorted_usec_list_t sul_seq_heartbeat; +#endif +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + lws_sorted_usec_list_t sul_ah_lifecheck; +#endif +#if defined(LWS_WITH_TLS) && !defined(LWS_NO_SERVER) + lws_sorted_usec_list_t sul_tls; +#endif +#if defined(LWS_PLAT_UNIX) + lws_sorted_usec_list_t sul_plat; +#endif +#if defined(LWS_WITH_STATS) + uint64_t lws_stats[LWSSTATS_SIZE]; + int updated; + lws_sorted_usec_list_t sul_stats; +#endif +#if defined(LWS_WITH_PEER_LIMITS) + lws_sorted_usec_list_t sul_peer_limits; +#endif + +#if defined(LWS_WITH_TLS) + struct lws_pt_tls tls; +#endif + struct lws *fake_wsi; /* used for callbacks where there's no wsi */ + + struct lws_context *context; + + /* + * usable by anything in the service code, but only if the scope + * does not last longer than the service action (since next service + * of any socket can likewise use it and overwrite) + */ + unsigned char *serv_buf; + + struct lws_pollfd *fds; + volatile struct lws_foreign_thread_pollfd * volatile foreign_pfd_list; +#ifdef _WIN32 + WSAEVENT events; + CRITICAL_SECTION interrupt_lock; +#endif + lws_sockfd_type dummy_pipe_fds[2]; + struct lws *pipe_wsi; + + /* --- role based members --- */ + +#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) + struct lws_pt_role_ws ws; +#endif +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct lws_pt_role_http http; +#endif +#if defined(LWS_ROLE_DBUS) + struct lws_pt_role_dbus dbus; +#endif + + /* --- event library based members --- */ + +#if defined(LWS_WITH_LIBEV) + struct lws_pt_eventlibs_libev ev; +#endif +#if defined(LWS_WITH_LIBUV) + struct lws_pt_eventlibs_libuv uv; +#endif +#if defined(LWS_WITH_LIBEVENT) + struct lws_pt_eventlibs_libevent event; +#endif + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ + defined(LWS_WITH_LIBEVENT) + struct lws_signal_watcher w_sigint; +#endif + + /* --- */ + + unsigned long count_conns; + unsigned int fds_count; + + /* + * set to the Thread ID that's doing the service loop just before entry + * to poll indicates service thread likely idling in poll() + * volatile because other threads may check it as part of processing + * for pollfd event change. + */ + volatile int service_tid; + int service_tid_detected; + + volatile unsigned char inside_poll; + volatile unsigned char foreign_spinlock; + + unsigned char tid; + + unsigned char inside_service:1; + unsigned char event_loop_foreign:1; + unsigned char event_loop_destroy_processing_done:1; +#ifdef _WIN32 + unsigned char interrupt_requested:1; +#endif +}; + +struct lws_conn_stats { + unsigned long long rx, tx; + unsigned long h1_conn, h1_trans, h2_trans, ws_upg, h2_alpn, h2_subs, + h2_upg, rejected; +}; + +/* + * virtual host -related context information + * vhostwide SSL context + * vhostwide proxy + * + * hierarchy: + * + * context -> vhost -> wsi + * + * incoming connection non-SSL vhost binding: + * + * listen socket -> wsi -> select vhost after first headers + * + * incoming connection SSL vhost binding: + * + * SSL SNI -> wsi -> bind after SSL negotiation + */ + + +struct lws_vhost { +#if !defined(LWS_WITHOUT_CLIENT) + char proxy_basic_auth_token[128]; +#endif +#if LWS_MAX_SMP > 1 + pthread_mutex_t lock; + char close_flow_vs_tsi[LWS_MAX_SMP]; +#endif + +#if defined(LWS_ROLE_H2) + struct lws_vhost_role_h2 h2; +#endif +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct lws_vhost_role_http http; +#endif +#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS) + struct lws_vhost_role_ws ws; +#endif + +#if defined(LWS_WITH_SOCKS5) + char socks_proxy_address[128]; + char socks_user[96]; + char socks_password[96]; +#endif +#if defined(LWS_WITH_LIBEV) + struct lws_io_watcher w_accept; +#endif + struct lws_conn_stats conn_stats; + struct lws_context *context; + struct lws_vhost *vhost_next; + + struct lws *lserv_wsi; + const char *name; + const char *iface; + const char *listen_accept_role; + const char *listen_accept_protocol; + const char *unix_socket_perms; + + void (*finalize)(struct lws_vhost *vh, void *arg); + void *finalize_arg; + +#if !defined(LWS_WITH_ESP32) && !defined(OPTEE_TA) && !defined(WIN32) + int bind_iface; +#endif + const struct lws_protocols *protocols; + void **protocol_vh_privs; + const struct lws_protocol_vhost_options *pvo; + const struct lws_protocol_vhost_options *headers; + struct lws_dll2_owner *same_vh_protocol_owner; + struct lws_vhost *no_listener_vhost_list; + struct lws_dll2_owner abstract_instances_owner; + +#if !defined(LWS_NO_CLIENT) + struct lws_dll2_owner dll_cli_active_conns_owner; +#endif + +#if defined(LWS_WITH_TLS) + struct lws_vhost_tls tls; +#endif + + struct lws_timed_vh_protocol *timed_vh_protocol_list; + void *user; + + int listen_port; + +#if defined(LWS_WITH_SOCKS5) + unsigned int socks_proxy_port; +#endif + unsigned int options; + int count_protocols; + int ka_time; + int ka_probes; + int ka_interval; + int keepalive_timeout; + int timeout_secs_ah_idle; + + int count_bound_wsi; + +#ifdef LWS_WITH_ACCESS_LOG + int log_fd; +#endif + + unsigned int allocated_vhost_protocols:1; + unsigned int created_vhost_protocols:1; + unsigned int being_destroyed:1; + + unsigned char default_protocol_index; + unsigned char raw_protocol_index; +}; + +void +__lws_vhost_destroy2(struct lws_vhost *vh); + +struct lws { + /* structs */ + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + struct _lws_http_mode_related http; +#endif +#if defined(LWS_ROLE_H2) + struct _lws_h2_related h2; +#endif +#if defined(LWS_ROLE_WS) + struct _lws_websocket_related *ws; /* allocated if we upgrade to ws */ + lws_sorted_usec_list_t sul_ping; +#endif +#if defined(LWS_ROLE_DBUS) + struct _lws_dbus_mode_related dbus; +#endif + + + const struct lws_role_ops *role_ops; + lws_wsi_state_t wsistate; + lws_wsi_state_t wsistate_pre_close; + + /* lifetime members */ + +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || \ + defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_read; +#endif +#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBEVENT) + struct lws_io_watcher w_write; +#endif + + lws_sorted_usec_list_t sul_timeout; + lws_sorted_usec_list_t sul_hrtimer; + + /* pointers */ + + struct lws_context *context; + struct lws_vhost *vhost; + struct lws *parent; /* points to parent, if any */ + struct lws *child_list; /* points to first child */ + struct lws *sibling_list; /* subsequent children at same level */ + + const struct lws_protocols *protocol; + struct lws_dll2 same_vh_protocol; + + lws_seq_t *seq; /* associated sequencer if any */ + + struct lws_dll2 dll_buflist; /* guys with pending rxflow */ + +#if defined(LWS_WITH_THREADPOOL) + struct lws_threadpool_task *tp_task; +#endif + +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer *peer; +#endif + + struct lws_udp *udp; +#ifndef LWS_NO_CLIENT + struct client_info_stash *stash; + char *cli_hostname_copy; + struct lws_dll2 dll_cli_active_conns; + struct lws_dll2_owner dll2_cli_txn_queue_owner; + struct lws_dll2 dll2_cli_txn_queue; +#endif + void *user_space; + void *opaque_parent_data; + void *opaque_user_data; + + struct lws_buflist *buflist; /* input-side buflist */ + struct lws_buflist *buflist_out; /* output-side buflist */ + +#if defined(LWS_WITH_TLS) + struct lws_lws_tls tls; +#endif + + lws_sock_file_fd_type desc; /* .filefd / .sockfd */ +#if defined(LWS_WITH_STATS) + uint64_t active_writable_req_us; +#if defined(LWS_WITH_TLS) + uint64_t accept_start_us; +#endif +#endif + +#ifdef LWS_LATENCY + unsigned long action_start; + unsigned long latency_start; +#endif + + /* ints */ +#define LWS_NO_FDS_POS (-1) + int position_in_fds_table; + +#ifndef LWS_NO_CLIENT + int chunk_remaining; +#endif + unsigned int cache_secs; + + unsigned int hdr_parsing_completed:1; + unsigned int http2_substream:1; + unsigned int upgraded_to_http2:1; + unsigned int h2_stream_carries_ws:1; + unsigned int h2_stream_carries_sse:1; + unsigned int seen_nonpseudoheader:1; + unsigned int listener:1; + unsigned int user_space_externally_allocated:1; + unsigned int socket_is_permanently_unusable:1; + unsigned int rxflow_change_to:2; + unsigned int conn_stat_done:1; + unsigned int cache_reuse:1; + unsigned int cache_revalidate:1; + unsigned int cache_intermediaries:1; + unsigned int favoured_pollin:1; + unsigned int sending_chunked:1; + unsigned int interpreting:1; + unsigned int already_did_cce:1; + unsigned int told_user_closed:1; + unsigned int told_event_loop_closed:1; + unsigned int waiting_to_send_close_frame:1; + unsigned int close_needs_ack:1; + unsigned int ipv6:1; + unsigned int parent_pending_cb_on_writable:1; + unsigned int cgi_stdout_zero_length:1; + unsigned int seen_zero_length_recv:1; + unsigned int rxflow_will_be_applied:1; + unsigned int event_pipe:1; + unsigned int handling_404:1; + unsigned int protocol_bind_balance:1; + unsigned int unix_skt:1; + unsigned int close_when_buffered_out_drained:1; + unsigned int h1_ws_proxied; + unsigned int proxied_ws_parent; + + unsigned int could_have_pending:1; /* detect back-to-back writes */ + unsigned int outer_will_close:1; + unsigned int shadow:1; /* we do not control fd lifecycle at all */ + +#ifdef LWS_WITH_ACCESS_LOG + unsigned int access_log_pending:1; +#endif +#ifndef LWS_NO_CLIENT + unsigned int do_ws:1; /* whether we are doing http or ws flow */ + unsigned int chunked:1; /* if the clientside connection is chunked */ + unsigned int client_rx_avail:1; + unsigned int client_http_body_pending:1; + unsigned int transaction_from_pipeline_queue:1; + unsigned int keepalive_active:1; + unsigned int keepalive_rejected:1; + unsigned int client_pipeline:1; + unsigned int client_h2_alpn:1; + unsigned int client_h2_substream:1; +#endif + +#ifdef _WIN32 + unsigned int sock_send_blocking:1; +#endif + +#ifndef LWS_NO_CLIENT - unsigned short c_port; ++ unsigned short ocport, c_port; +#endif + + /* chars */ + + char lws_rx_parse_state; /* enum lws_rx_parse_state */ + char rx_frame_type; /* enum lws_write_protocol */ + char pending_timeout; /* enum pending_timeout */ + char tsi; /* thread service index we belong to */ + char protocol_interpret_idx; + char redirects; + uint8_t rxflow_bitmap; + uint8_t bound_vhost_index; +#ifdef LWS_WITH_CGI + char cgi_channel; /* which of stdin/out/err */ + char hdr_state; +#endif +#ifndef LWS_NO_CLIENT + char chunk_parser; /* enum lws_chunk_parser */ +#endif +#if defined(LWS_WITH_CGI) || !defined(LWS_NO_CLIENT) + char reason_bf; /* internal writeable callback reason bitfield */ +#endif +#if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS) + char seen_rx; +#endif + uint8_t immortal_substream_count; + /* volatile to make sure code is aware other thread can change */ + volatile char handling_pollout; + volatile char leave_pollout_active; +#if LWS_MAX_SMP > 1 + volatile char undergoing_init_from_other_pt; +#endif + +}; + +#define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap)) + +void +lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt); + +const struct lws_role_ops * +lws_role_by_name(const char *name); + +LWS_EXTERN int +lws_socket_bind(struct lws_vhost *vhost, lws_sockfd_type sockfd, int port, + const char *iface, int ipv6_allowed); + +#if defined(LWS_WITH_IPV6) +LWS_EXTERN unsigned long +lws_get_addr_scope(const char *ipaddr); +#endif + +LWS_EXTERN void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); +LWS_EXTERN void +__lws_close_free_wsi(struct lws *wsi, enum lws_close_status, const char *caller); + +LWS_EXTERN void +__lws_free_wsi(struct lws *wsi); + +#if LWS_MAX_SMP > 1 + +static LWS_INLINE void +lws_pt_mutex_init(struct lws_context_per_thread *pt) +{ + lws_mutex_refcount_init(&pt->mr); + pthread_mutex_init(&pt->lock_stats, NULL); +} + +static LWS_INLINE void +lws_pt_mutex_destroy(struct lws_context_per_thread *pt) +{ + pthread_mutex_destroy(&pt->lock_stats); + lws_mutex_refcount_destroy(&pt->mr); +} + +#define lws_pt_lock(pt, reason) lws_mutex_refcount_lock(&pt->mr, reason) +#define lws_pt_unlock(pt) lws_mutex_refcount_unlock(&pt->mr) + +static LWS_INLINE void +lws_pt_stats_lock(struct lws_context_per_thread *pt) +{ + pthread_mutex_lock(&pt->lock_stats); +} + +static LWS_INLINE void +lws_pt_stats_unlock(struct lws_context_per_thread *pt) +{ + pthread_mutex_unlock(&pt->lock_stats); +} +#endif + +/* + * EXTENSIONS + */ + +#if defined(LWS_WITHOUT_EXTENSIONS) +#define lws_any_extension_handled(_a, _b, _c, _d) (0) +#define lws_ext_cb_active(_a, _b, _c, _d) (0) +#define lws_ext_cb_all_exts(_a, _b, _c, _d, _e) (0) +#define lws_issue_raw_ext_access lws_issue_raw +#define lws_context_init_extensions(_a, _b) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_client_interpret_server_handshake(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN void +lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, + const struct lws_role_ops *ops); + +int +lws_http_to_fallback(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, + enum lws_callback_reasons reason, void *user, + void *in, size_t len); + +LWS_EXTERN int +lws_plat_set_nonblocking(int fd); + +LWS_EXTERN int +lws_plat_set_socket_options(struct lws_vhost *vhost, lws_sockfd_type fd, + int unix_skt); + +LWS_EXTERN int +lws_plat_check_connection_error(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_header_table_attach(struct lws *wsi, int autoservice); + +LWS_EXTERN int +lws_header_table_detach(struct lws *wsi, int autoservice); +LWS_EXTERN int +__lws_header_table_detach(struct lws *wsi, int autoservice); + +LWS_EXTERN void +lws_header_table_reset(struct lws *wsi, int autoservice); + +void +__lws_header_table_reset(struct lws *wsi, int autoservice); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_hdr_simple_ptr(struct lws *wsi, enum lws_token_indexes h); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_hdr_simple_create(struct lws *wsi, enum lws_token_indexes h, const char *s); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ensure_user_space(struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_change_pollfd(struct lws *wsi, int _and, int _or); + +#ifndef LWS_NO_SERVER + int _lws_vhost_init_server(const struct lws_context_creation_info *info, + struct lws_vhost *vhost); + LWS_EXTERN struct lws_vhost * + lws_select_vhost(struct lws_context *context, int port, const char *servername); + LWS_EXTERN int LWS_WARN_UNUSED_RESULT + lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len); + LWS_EXTERN void + lws_server_get_canonical_hostname(struct lws_context *context, + const struct lws_context_creation_info *info); +#else + #define _lws_vhost_init_server(_a, _b) (0) + #define lws_parse_ws(_a, _b, _c) (0) + #define lws_server_get_canonical_hostname(_a, _b) +#endif + +LWS_EXTERN int +__remove_wsi_socket_from_fds(struct lws *wsi); + +enum { + LWSRXFC_ERROR = -1, + LWSRXFC_CACHED = 0, + LWSRXFC_ADDITIONAL = 1, + LWSRXFC_TRIMMED = 2, +}; + ++ ++int ++_lws_plat_service_forced_tsi(struct lws_context *context, int tsi); ++ +LWS_EXTERN int +lws_rxflow_cache(struct lws *wsi, unsigned char *buf, int n, int len); + +LWS_EXTERN int +lws_service_flag_pending(struct lws_context *context, int tsi); + +LWS_EXTERN void +lws_client_stash_destroy(struct lws *wsi); + +static LWS_INLINE int +lws_has_buffered_out(struct lws *wsi) { return !!wsi->buflist_out; } + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ws_client_rx_sm(struct lws *wsi, unsigned char c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse(struct lws *wsi, unsigned char *buf, int *len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_parse_urldecode(struct lws *wsi, uint8_t *_c); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_action(struct lws *wsi); + +LWS_EXTERN void +__lws_close_free_wsi_final(struct lws *wsi); +LWS_EXTERN void +lws_libuv_closehandle(struct lws *wsi); +LWS_EXTERN int +lws_libuv_check_watcher_active(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_init(struct lws_context * context, const char * const *d); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_plugins_destroy(struct lws_context * context); + +LWS_EXTERN void +lws_restart_ws_ping_pong_timer(struct lws *wsi); + +struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); + +void +lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi); +void +lws_vhost_unbind_wsi(struct lws *wsi); + +void +__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); +int +__lws_change_pollfd(struct lws *wsi, int _and, int _or); + + +int +lws_callback_as_writeable(struct lws *wsi); + +int +lws_role_call_client_bind(struct lws *wsi, + const struct lws_client_connect_info *i); +void +lws_remove_child_from_any_parent(struct lws *wsi); + +char * +lws_generate_client_ws_handshake(struct lws *wsi, char *p, const char *conn1); +int +lws_client_ws_upgrade(struct lws *wsi, const char **cce); +int +lws_create_client_ws_object(const struct lws_client_connect_info *i, + struct lws *wsi); +int +lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len); +int +lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn); +int +lws_tls_server_conn_alpn(struct lws *wsi); + +int +lws_ws_client_rx_sm_block(struct lws *wsi, unsigned char **buf, size_t len); +void +lws_destroy_event_pipe(struct lws *wsi); + +/* socks */ +int +socks_generate_msg(struct lws *wsi, enum socks_msg_type type, ssize_t *msg_len); + + +void +lws_sum_stats(const struct lws_context *ctx, struct lws_conn_stats *cs); + +LWS_EXTERN int +__lws_timed_callback_remove(struct lws_vhost *vh, struct lws_timed_vh_protocol *p); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +__insert_wsi_socket_into_fds(struct lws_context *context, struct lws *wsi); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len); + +LWS_EXTERN lws_usec_t +__lws_seq_timeout_check(struct lws_context_per_thread *pt, lws_usec_t usnow); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_client_connect_2(struct lws *wsi); + +LWS_VISIBLE struct lws * LWS_WARN_UNUSED_RESULT +lws_client_reset(struct lws **wsi, int ssl, const char *address, int port, + const char *path, const char *host); + +LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi); + +LWS_EXTERN char * LWS_WARN_UNUSED_RESULT +lws_generate_client_handshake(struct lws *wsi, char *pkt); + +LWS_EXTERN int +lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); + +LWS_EXTERN struct lws * +lws_http_client_connect_via_info2(struct lws *wsi); + + +#ifndef LWS_NO_CLIENT +LWS_EXTERN int lws_client_socket_service(struct lws *wsi, + struct lws_pollfd *pollfd, + struct lws *wsi_conn); +LWS_EXTERN struct lws * +lws_client_wsi_effective(struct lws *wsi); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi); +#if !defined(LWS_WITH_TLS) + #define lws_context_init_client_ssl(_a, _b) (0) +#endif +LWS_EXTERN void +lws_decode_ssl_error(void); +#else +#define lws_context_init_client_ssl(_a, _b) (0) +#endif + +LWS_EXTERN int +__lws_rx_flow_control(struct lws *wsi); + +LWS_EXTERN int +_lws_change_pollfd(struct lws *wsi, int _and, int _or, struct lws_pollargs *pa); + +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len); +#else +#define lws_server_socket_service(_b, _c) (0) +#define lws_handshake_server(_a, _b, _c) (0) +#endif + +#ifdef LWS_WITH_ACCESS_LOG +LWS_EXTERN int +lws_access_log(struct lws *wsi); +LWS_EXTERN void +lws_prepare_access_log_info(struct lws *wsi, char *uri_ptr, int len, int meth); +#else +#define lws_access_log(_a) +#endif + +LWS_EXTERN int +lws_cgi_kill_terminated(struct lws_context_per_thread *pt); + +LWS_EXTERN void +lws_cgi_remove_and_kill(struct lws *wsi); + +LWS_EXTERN void +lws_plat_delete_socket_from_fds(struct lws_context *context, + struct lws *wsi, int m); +LWS_EXTERN void +lws_plat_insert_socket_into_fds(struct lws_context *context, + struct lws *wsi); + +LWS_EXTERN int +lws_plat_change_pollfd(struct lws_context *context, struct lws *wsi, + struct lws_pollfd *pfd); + + +int +lws_plat_pipe_create(struct lws *wsi); +int +lws_plat_pipe_signal(struct lws *wsi); +void +lws_plat_pipe_close(struct lws *wsi); + +LWS_EXTERN void +lws_add_wsi_to_draining_ext_list(struct lws *wsi); +LWS_EXTERN void +lws_remove_wsi_from_draining_ext_list(struct lws *wsi); +LWS_EXTERN int +lws_poll_listen_fd(struct lws_pollfd *fd); +LWS_EXTERN int +lws_plat_service(struct lws_context *context, int timeout_ms); +LWS_EXTERN LWS_VISIBLE int +_lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi); + +LWS_EXTERN int +lws_pthread_self_to_tsi(struct lws_context *context); +LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_plat_inet_pton(int af, const char *src, void *dst); + +LWS_EXTERN void +lws_same_vh_protocol_remove(struct lws *wsi); +LWS_EXTERN void +__lws_same_vh_protocol_remove(struct lws *wsi); +LWS_EXTERN void +lws_same_vh_protocol_insert(struct lws *wsi, int n); + +void +lws_seq_destroy_all_on_pt(struct lws_context_per_thread *pt); + +LWS_EXTERN int +lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in, size_t len); + +#if defined(LWS_WITH_STATS) + void + lws_stats_bump(struct lws_context_per_thread *pt, int i, uint64_t bump); + void + lws_stats_max(struct lws_context_per_thread *pt, int index, uint64_t val); +#else + static LWS_INLINE uint64_t lws_stats_bump( + struct lws_context_per_thread *pt, int index, uint64_t bump) { + (void)pt; (void)index; (void)bump; return 0; } + static LWS_INLINE uint64_t lws_stats_max( + struct lws_context_per_thread *pt, int index, uint64_t val) { + (void)pt; (void)index; (void)val; return 0; } +#endif + + + +#if defined(LWS_WITH_PEER_LIMITS) +void +lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer); +int +lws_peer_confirm_ah_attach_ok(struct lws_context *context, + struct lws_peer *peer); +void +lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer); +void +lws_peer_cull_peer_wait_list(struct lws_context *context); +struct lws_peer * +lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd); +void +lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer, + struct lws *wsi); +void +lws_peer_dump_from_wsi(struct lws *wsi); +#endif + +#ifdef LWS_WITH_HUBBUB +hubbub_error +html_parser_cb(const hubbub_token *token, void *pw); +#endif + +int +lws_threadpool_tsi_context(struct lws_context *context, int tsi); + +void +__lws_wsi_remove_from_sul(struct lws *wsi); + +int +lws_seq_pt_init(struct lws_context_per_thread *pt); + +int +lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi, + struct lws_tokens *ebuf); +int +lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used, + int buffered); + +extern const struct lws_protocols protocol_abs_client_raw_skt, + protocol_abs_client_unit_test; + +void +lws_inform_client_conn_fail(struct lws *wsi, void *arg, size_t len); + +#ifdef __cplusplus +}; +#endif + +#endif diff --cc lib/core/private-lib-core.h index 4fcc6071,00000000..ca8e90db mode 100644,000000..100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@@ -1,621 -1,0 +1,621 @@@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "lws_config.h" +#include "lws_config_private.h" + +#if defined(LWS_WITH_CGI) && defined(LWS_HAVE_VFORK) && \ + !defined(NO_GNU_SOURCE_THIS_TIME) + #define _GNU_SOURCE +#endif + +/* +#if !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200112L +#endif +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef LWS_HAVE_INTTYPES_H +#include +#endif + +#include + +#ifdef LWS_HAVE_SYS_TYPES_H + #include +#endif +#if defined(LWS_HAVE_SYS_STAT_H) && !defined(LWS_PLAT_OPTEE) + #include +#endif + +#if LWS_MAX_SMP > 1 + #include +#endif + +#ifndef LWS_DEF_HEADER_LEN +#define LWS_DEF_HEADER_LEN 4096 +#endif +#ifndef LWS_DEF_HEADER_POOL +#define LWS_DEF_HEADER_POOL 4 +#endif +#ifndef LWS_MAX_PROTOCOLS +#define LWS_MAX_PROTOCOLS 5 +#endif +#ifndef LWS_MAX_EXTENSIONS_ACTIVE +#define LWS_MAX_EXTENSIONS_ACTIVE 1 +#endif +#ifndef LWS_MAX_EXT_OFFERS +#define LWS_MAX_EXT_OFFERS 8 +#endif +#ifndef SPEC_LATEST_SUPPORTED +#define SPEC_LATEST_SUPPORTED 13 +#endif +#ifndef AWAITING_TIMEOUT +#define AWAITING_TIMEOUT 20 +#endif +#ifndef CIPHERS_LIST_STRING +#define CIPHERS_LIST_STRING "DEFAULT" +#endif +#ifndef LWS_SOMAXCONN +#define LWS_SOMAXCONN SOMAXCONN +#endif + +#define MAX_WEBSOCKET_04_KEY_LEN 128 + +#ifndef SYSTEM_RANDOM_FILEPATH +#define SYSTEM_RANDOM_FILEPATH "/dev/urandom" +#endif + +#define LWS_H2_RX_SCRATCH_SIZE 512 + +#define lws_socket_is_valid(x) (x != LWS_SOCK_INVALID) + +#ifndef LWS_HAVE_STRERROR + #define strerror(x) "" +#endif + + + /* + * + * ------ private platform defines ------ + * + */ + +#if defined(LWS_WITH_ESP32) + #include "private-lib-plat-esp32.h" +#else + #if defined(WIN32) || defined(_WIN32) + #include "private-lib-plat-windows.h" + #else + #if defined(LWS_PLAT_OPTEE) + #include "private-lib-plat.h" + #else + #include "private-lib-plat-unix.h" + #endif + #endif +#endif + + /* + * + * ------ public api ------ + * + */ + +#include "libwebsockets.h" + +#include "private-lib-tls.h" + +#if defined(WIN32) || defined(_WIN32) + // Visual studio older than 2015 and WIN_CE has only _stricmp + #if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) + #define strcasecmp _stricmp + #define strncasecmp _strnicmp + #elif !defined(__MINGW32__) + #define strcasecmp stricmp + #define strncasecmp strnicmp + #endif + #define getdtablesize() 30000 +#endif + +#ifndef LWS_ARRAY_SIZE +#define LWS_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + + +#if defined(__clang__) +#define lws_memory_barrier() __sync_synchronize() +#elif defined(__GNUC__) +#define lws_memory_barrier() __sync_synchronize() +#else +#define lws_memory_barrier() +#endif + + +struct lws_ring { + void *buf; + void (*destroy_element)(void *element); + uint32_t buflen; + uint32_t element_len; + uint32_t head; + uint32_t oldest_tail; +}; + +struct lws_protocols; +struct lws; + +#if defined(LWS_WITH_NETWORK) +#include "private-lib-event-libs.h" + + +struct lws_io_watcher { +#ifdef LWS_WITH_LIBEV + struct lws_io_watcher_libev ev; +#endif +#ifdef LWS_WITH_LIBUV + struct lws_io_watcher_libuv uv; +#endif +#ifdef LWS_WITH_LIBEVENT + struct lws_io_watcher_libevent event; +#endif + struct lws_context *context; + + uint8_t actual_events; +}; + +struct lws_signal_watcher { +#ifdef LWS_WITH_LIBEV + struct lws_signal_watcher_libev ev; +#endif +#ifdef LWS_WITH_LIBUV + struct lws_signal_watcher_libuv uv; +#endif +#ifdef LWS_WITH_LIBEVENT + struct lws_signal_watcher_libevent event; +#endif + struct lws_context *context; +}; + +struct lws_foreign_thread_pollfd { + struct lws_foreign_thread_pollfd *next; + int fd_index; + int _and; + int _or; +}; +#endif + +#if LWS_MAX_SMP > 1 + +struct lws_mutex_refcount { + pthread_mutex_t lock; + pthread_t lock_owner; + const char *last_lock_reason; + char lock_depth; + char metadata; +}; + +void +lws_mutex_refcount_init(struct lws_mutex_refcount *mr); + +void +lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr); + +void +lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason); + +void +lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr); +#endif + +#if defined(LWS_WITH_NETWORK) +#include "private-lib-core-net.h" +#endif + +struct lws_deferred_free +{ + struct lws_deferred_free *next; + time_t deadline; + void *payload; +}; + +/* + * the rest is managed per-context, that includes + * + * - processwide single fd -> wsi lookup + * - contextwide headers pool + */ + +struct lws_context { + time_t last_ws_ping_pong_check_s; + lws_usec_t time_up; /* monotonic */ + const struct lws_plat_file_ops *fops; + struct lws_plat_file_ops fops_platform; + struct lws_context **pcontext_finalize; + + const struct lws_tls_ops *tls_ops; + + const char *username, *groupname; + +#if defined(LWS_WITH_HTTP2) + struct http2_settings set; +#endif +#if defined(LWS_WITH_ZIP_FOPS) + struct lws_plat_file_ops fops_zip; +#endif +#if defined(LWS_WITH_NETWORK) + struct lws_context_per_thread pt[LWS_MAX_SMP]; + struct lws_conn_stats conn_stats; + struct lws_vhost *vhost_list; + struct lws_vhost *no_listener_vhost_list; + struct lws_vhost *vhost_pending_destruction_list; + struct lws_plugin *plugin_list; +#ifdef _WIN32 +/* different implementation between unix and windows */ + struct lws_fd_hashtable fd_hashtable[FD_HASHTABLE_MODULUS]; +#else + struct lws **lws_lookup; + +#endif +#endif +#if LWS_MAX_SMP > 1 + struct lws_mutex_refcount mr; +#endif + +#if defined(LWS_AMAZON_RTOS) + mbedtls_entropy_context mec; + mbedtls_ctr_drbg_context mcdc; +#endif + + struct lws_deferred_free *deferred_free_list; + +#if defined(LWS_WITH_THREADPOOL) + struct lws_threadpool *tp_list_head; +#endif + +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer **pl_hash_table; + struct lws_peer *peer_wait_list; + time_t next_cull; +#endif + + const lws_system_ops_t *system_ops; + void *external_baggage_free_on_destroy; + const struct lws_token_limits *token_limits; + void *user_space; + const struct lws_protocol_vhost_options *reject_service_keywords; + lws_reload_func deprecation_cb; + void (*eventlib_signal_cb)(void *event_lib_handle, int signum); + +#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) + cap_value_t caps[4]; + char count_caps; +#endif + +#if defined(LWS_WITH_NETWORK) +#if defined(LWS_WITH_LIBEV) + struct lws_context_eventlibs_libev ev; +#endif +#if defined(LWS_WITH_LIBUV) + struct lws_context_eventlibs_libuv uv; +#endif +#if defined(LWS_WITH_LIBEVENT) + struct lws_context_eventlibs_libevent event; +#endif + struct lws_event_loop_ops *event_loop_ops; +#endif + +#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) + struct lws_context_tls tls; +#endif + + char canonical_hostname[128]; + const char *server_string; + +#ifdef LWS_LATENCY + unsigned long worst_latency; + char worst_latency_info[256]; +#endif + +#if defined(LWS_WITH_ESP32) + unsigned long time_last_state_dump; + uint32_t last_free_heap; +#endif + + int max_fds; + int count_event_loop_static_asset_handles; +#if !defined(LWS_NO_DAEMONIZE) + pid_t started_with_parent; +#endif + int uid, gid; + + int fd_random; + + int count_wsi_allocated; + int count_cgi_spawned; + unsigned int options; + unsigned int fd_limit_per_thread; + unsigned int timeout_secs; + unsigned int pt_serv_buf_size; + int max_http_header_data; + int max_http_header_pool; + int simultaneous_ssl_restriction; + int simultaneous_ssl; +#if defined(LWS_WITH_PEER_LIMITS) + uint32_t pl_hash_elements; /* protected by context->lock */ + uint32_t count_peers; /* protected by context->lock */ + unsigned short ip_limit_ah; + unsigned short ip_limit_wsi; +#endif + unsigned int deprecated:1; + unsigned int being_destroyed:1; + unsigned int being_destroyed1:1; + unsigned int being_destroyed2:1; + unsigned int requested_kill:1; + unsigned int protocol_init_done:1; + unsigned int doing_protocol_init:1; + unsigned int done_protocol_destroy_cb:1; + unsigned int finalize_destroy_after_internal_loops_stopped:1; + unsigned int max_fds_unrelated_to_ulimit:1; + + short count_threads; + short plugin_protocol_count; + short plugin_extension_count; + short server_string_len; + unsigned short ws_ping_pong_interval; + unsigned short deprecation_pending_listen_close_count; + + uint8_t max_fi; + +#if defined(LWS_WITH_STATS) + uint8_t updated; +#endif +}; + +int +lws_check_deferred_free(struct lws_context *context, int tsi, int force); + +#define lws_get_context_protocol(ctx, x) ctx->vhost_list->protocols[x] +#define lws_get_vh_protocol(vh, x) vh->protocols[x] + +int +lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max); + +void +lws_vhost_destroy1(struct lws_vhost *vh); + + +#if defined(LWS_WITH_ESP32) +LWS_EXTERN int +lws_find_string_in_file(const char *filename, const char *str, int stringlen); +#endif + + +signed char char_to_hex(const char c); + + +struct lws_buflist { + struct lws_buflist *next; + + size_t len; + size_t pos; + + uint8_t buf[1]; /* true length of this is set by the oversize malloc */ +}; + + +LWS_EXTERN char * +lws_strdup(const char *s); + +LWS_EXTERN int log_level; + + + +#ifndef LWS_LATENCY +static LWS_INLINE void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion) { + do { + (void)context; (void)wsi; (void)action; (void)ret; + (void)completion; + } while (0); +} +static LWS_INLINE void +lws_latency_pre(struct lws_context *context, struct lws *wsi) { + do { (void)context; (void)wsi; } while (0); +} +#else +#define lws_latency_pre(_context, _wsi) lws_latency(_context, _wsi, NULL, 0, 0) +extern void +lws_latency(struct lws_context *context, struct lws *wsi, const char *action, + int ret, int completion); +#endif + + +LWS_EXTERN int +lws_b64_selftest(void); + + + + + +#ifndef LWS_NO_DAEMONIZE - LWS_EXTERN int get_daemonize_pid(); ++ LWS_EXTERN pid_t get_daemonize_pid(); +#else + #define get_daemonize_pid() (0) +#endif + +LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); + +#if !defined(LWS_WITH_TLS) + #define LWS_SSL_ENABLED(context) (0) + #define lws_context_init_server_ssl(_a, _b) (0) + #define lws_ssl_destroy(_a) + #define lws_context_init_alpn(_a) + #define lws_ssl_capable_read lws_ssl_capable_read_no_ssl + #define lws_ssl_capable_write lws_ssl_capable_write_no_ssl + #define lws_ssl_pending lws_ssl_pending_no_ssl + #define lws_server_socket_service_ssl(_b, _c) (0) + #define lws_ssl_close(_a) (0) + #define lws_ssl_context_destroy(_a) + #define lws_ssl_SSL_CTX_destroy(_a) + #define lws_ssl_remove_wsi_from_buffered_list(_a) + #define __lws_ssl_remove_wsi_from_buffered_list(_a) + #define lws_context_init_ssl_library(_a) + #define lws_context_deinit_ssl_library(_a) + #define lws_tls_check_all_cert_lifetimes(_a) + #define lws_tls_acme_sni_cert_destroy(_a) +#endif + + + +#if LWS_MAX_SMP > 1 +#define lws_context_lock(c, reason) lws_mutex_refcount_lock(&c->mr, reason) +#define lws_context_unlock(c) lws_mutex_refcount_unlock(&c->mr) + +static LWS_INLINE void +lws_vhost_lock(struct lws_vhost *vhost) +{ + pthread_mutex_lock(&vhost->lock); +} + +static LWS_INLINE void +lws_vhost_unlock(struct lws_vhost *vhost) +{ + pthread_mutex_unlock(&vhost->lock); +} + + +#else +#define lws_pt_mutex_init(_a) (void)(_a) +#define lws_pt_mutex_destroy(_a) (void)(_a) +#define lws_pt_lock(_a, b) (void)(_a) +#define lws_pt_unlock(_a) (void)(_a) +#define lws_context_lock(_a, _b) (void)(_a) +#define lws_context_unlock(_a) (void)(_a) +#define lws_vhost_lock(_a) (void)(_a) +#define lws_vhost_unlock(_a) (void)(_a) +#define lws_pt_stats_lock(_a) (void)(_a) +#define lws_pt_stats_unlock(_a) (void)(_a) +#endif + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len); + +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ssl_pending_no_ssl(struct lws *wsi); + +int +lws_tls_check_cert_lifetime(struct lws_vhost *vhost); + +int lws_jws_selftest(void); +int lws_jwe_selftest(void); + +int +lws_protocol_init(struct lws_context *context); + +int +lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p, + const char *reason); + +const struct lws_protocol_vhost_options * +lws_vhost_protocol_options(struct lws_vhost *vh, const char *name); + +const struct lws_http_mount * +lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len); + +/* + * custom allocator + */ +LWS_EXTERN void * +lws_realloc(void *ptr, size_t size, const char *reason); + +LWS_EXTERN void * LWS_WARN_UNUSED_RESULT +lws_zalloc(size_t size, const char *reason); + +#ifdef LWS_PLAT_OPTEE +void *lws_malloc(size_t size, const char *reason); +void lws_free(void *p); +#define lws_free_set_NULL(P) do { lws_free(P); (P) = NULL; } while(0) +#else +#define lws_malloc(S, R) lws_realloc(NULL, S, R) +#define lws_free(P) lws_realloc(P, 0, "lws_free") +#define lws_free_set_NULL(P) do { lws_realloc(P, 0, "free"); (P) = NULL; } while(0) +#endif + +int +lws_create_event_pipes(struct lws_context *context); + +int +lws_plat_apply_FD_CLOEXEC(int n); + +const struct lws_plat_file_ops * +lws_vfs_select_fops(const struct lws_plat_file_ops *fops, const char *vfs_path, + const char **vpath); + +/* lws_plat_ */ + +LWS_EXTERN int +lws_plat_context_early_init(void); +LWS_EXTERN void +lws_plat_context_early_destroy(struct lws_context *context); +LWS_EXTERN void +lws_plat_context_late_destroy(struct lws_context *context); + +LWS_EXTERN int +lws_plat_init(struct lws_context *context, + const struct lws_context_creation_info *info); +LWS_EXTERN int +lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop); + +#if defined(LWS_WITH_UNIX_SOCK) +int +lws_plat_user_colon_group_to_ids(const char *u_colon_g, uid_t *puid, gid_t *pgid); +#endif + +LWS_EXTERN int +lws_check_byte_utf8(unsigned char state, unsigned char c); +LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); +LWS_EXTERN int alloc_file(struct lws_context *context, const char *filename, + uint8_t **buf, lws_filepos_t *amount); + +void +lws_context_destroy2(struct lws_context *context); + + +#ifdef __cplusplus +}; +#endif diff --cc lib/plat/unix/private-lib-plat-unix.h index 6aefa5c9,00000000..97ff5bad mode 100644,000000..100644 --- a/lib/plat/unix/private-lib-plat-unix.h +++ b/lib/plat/unix/private-lib-plat-unix.h @@@ -1,173 -1,0 +1,176 @@@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2018 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Included from lib/private-lib-core.h if no explicit platform + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef __cplusplus +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include ++#if defined(LWS_HAVE_EVENTFD) ++#include ++#endif + +#if defined(__APPLE__) +#include +#endif +#if defined(__FreeBSD__) +#include +#endif +#if defined(__linux__) +#include +#endif +#if defined(__QNX__) + #include + #if defined(__LITTLEENDIAN__) + #define BYTE_ORDER __LITTLEENDIAN__ + #define LITTLE_ENDIAN __LITTLEENDIAN__ + #define BIG_ENDIAN 4321 /* to show byte order (taken from gcc); for suppres warning that BIG_ENDIAN is not defined. */ + #endif + #if defined(__BIGENDIAN__) + #define BYTE_ORDER __BIGENDIAN__ + #define LITTLE_ENDIAN 1234 /* to show byte order (taken from gcc); for suppres warning that LITTLE_ENDIAN is not defined. */ + #define BIG_ENDIAN __BIGENDIAN__ + #endif +#endif + +#if defined(__sun) && defined(__GNUC__) + +#include + +#if !defined (BYTE_ORDER) +#define BYTE_ORDER __BYTE_ORDER__ +#endif + +#if !defined(LITTLE_ENDIAN) +#define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#endif + +#if !defined(BIG_ENDIAN) +#define BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#endif + +#endif /* sun + GNUC */ + +#if !defined(BYTE_ORDER) +#define BYTE_ORDER __BYTE_ORDER +#endif +#if !defined(LITTLE_ENDIAN) +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#if !defined(BIG_ENDIAN) +#define BIG_ENDIAN __BIG_ENDIAN +#endif + +#if defined(LWS_BUILTIN_GETIFADDRS) +#include "./misc/getifaddrs.h" +#else + +#if defined(__HAIKU__) +#define _BSD_SOURCE +#endif +#include + +#endif + +#if defined (__sun) || defined(__HAIKU__) || defined(__QNX__) || defined(__ANDROID__) +#include + +#if defined(__ANDROID__) +#include +#endif + +#else +#include +#endif + +#ifdef __QNX__ +# include "netinet/tcp_var.h" +# define TCP_KEEPINTVL TCPCTL_KEEPINTVL +# define TCP_KEEPIDLE TCPCTL_KEEPIDLE +# define TCP_KEEPCNT TCPCTL_KEEPCNT +#endif + +#define LWS_ERRNO errno +#define LWS_EAGAIN EAGAIN +#define LWS_EALREADY EALREADY +#define LWS_EINPROGRESS EINPROGRESS +#define LWS_EINTR EINTR +#define LWS_EISCONN EISCONN +#define LWS_ENOTCONN ENOTCONN +#define LWS_EWOULDBLOCK EWOULDBLOCK +#define LWS_EADDRINUSE EADDRINUSE +#define lws_set_blocking_send(wsi) +#define LWS_SOCK_INVALID (-1) + +struct lws_context; + +struct lws * +wsi_from_fd(const struct lws_context *context, int fd); + +int +insert_wsi(const struct lws_context *context, struct lws *wsi); + +void +delete_from_fd(const struct lws_context *context, int fd); + +#ifndef LWS_NO_FORK +#ifdef LWS_HAVE_SYS_PRCTL_H +#include +#endif +#endif + +#define compatible_close(x) close(x) +#define lws_plat_socket_offset() (0) + +/* + * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag, + * but happily have something equivalent in the SO_NOSIGPIPE flag. + */ +#ifdef __APPLE__ +#define MSG_NOSIGNAL SO_NOSIGPIPE +#endif + +/* + * Solaris 11.X only supports POSIX 2001, MSG_NOSIGNAL appears in + * POSIX 2008. + */ - #if defined(__sun) && !defined(__smartos__) ++#if defined(__sun) && !defined(MSG_NOSIGNAL) + #define MSG_NOSIGNAL 0 +#endif diff --cc lib/plat/windows/windows-service.c index cb2aba08,4036528f..7e35daa7 --- a/lib/plat/windows/windows-service.c +++ b/lib/plat/windows/windows-service.c @@@ -22,9 -22,36 +22,36 @@@ #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS #define _WINSOCK_DEPRECATED_NO_WARNINGS #endif -#include "core/private.h" +#include "private-lib-core.h" + int + _lws_plat_service_forced_tsi(struct lws_context *context, int tsi) + { + struct lws_context_per_thread *pt = &context->pt[tsi]; + int m, n; + + lws_service_flag_pending(context, tsi); + + /* any socket with events to service? */ + for (n = 0; n < (int)pt->fds_count; n++) { + if (!pt->fds[n].revents) + continue; + + m = lws_service_fd_tsi(context, &pt->fds[n], tsi); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + lws_service_do_ripe_rxflow(pt); + + return 0; + } + + LWS_EXTERN int _lws_plat_service_tsi(struct lws_context *context, int timeout_ms, int tsi) { diff --cc lib/roles/http/client/client-http.c index d0052cc9,00000000..f611f1a9 mode 100644,000000..100644 --- a/lib/roles/http/client/client-http.c +++ b/lib/roles/http/client/client-http.c @@@ -1,1334 -1,0 +1,1337 @@@ +/* + * libwebsockets - lib/client/client.c + * + * Copyright (C) 2010-2018 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-lib-core.h" + +LWS_VISIBLE LWS_EXTERN void +lws_client_http_body_pending(struct lws *wsi, int something_left_to_send) +{ + wsi->client_http_body_pending = !!something_left_to_send; +} + +/* + * return self, or queued client wsi we are acting on behalf of + * + * That is the TAIL of the queue (new queue elements are added at the HEAD) + */ + +struct lws * +lws_client_wsi_effective(struct lws *wsi) +{ + struct lws_dll2 *tail = lws_dll2_get_tail(&wsi->dll2_cli_txn_queue_owner); + + if (!wsi->transaction_from_pipeline_queue || !tail) + return wsi; + + return lws_container_of(tail, struct lws, dll2_cli_txn_queue); +} + +/* + * return self or the guy we are queued under + * + * REQUIRES VHOST LOCK HELD + */ + +static struct lws * +_lws_client_wsi_master(struct lws *wsi) +{ + struct lws_dll2_owner *o = wsi->dll2_cli_txn_queue.owner; + + if (!o) + return wsi; + + return lws_container_of(o, struct lws, dll2_cli_txn_queue_owner); +} + +int +lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd, + struct lws *wsi_conn) +{ + struct lws_context *context = wsi->context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + char *p = (char *)&pt->serv_buf[0]; + struct lws *w; +#if defined(LWS_WITH_TLS) + char ebuf[128]; +#endif + const char *cce = NULL; +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + ssize_t len = 0; + unsigned char c; +#endif + char *sb = p; + int n = 0; +#if defined(LWS_WITH_SOCKS5) + int conn_mode = 0, pending_timeout = 0; +#endif + + if ((pollfd->revents & LWS_POLLOUT) && + wsi->keepalive_active && + wsi->dll2_cli_txn_queue_owner.head) { + struct lws *wfound = NULL; + + lwsl_debug("%s: pollout HANDSHAKE2\n", __func__); + + /* + * We have a transaction queued that wants to pipeline. + * + * We have to allow it to send headers strictly in the order + * that it was queued, ie, tail-first. + */ + lws_vhost_lock(wsi->vhost); + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + wsi->dll2_cli_txn_queue_owner.head) { + struct lws *w = lws_container_of(d, struct lws, + dll2_cli_txn_queue); + + lwsl_debug("%s: %p states 0x%lx\n", __func__, w, + (unsigned long)w->wsistate); + if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) + wfound = w; + } lws_end_foreach_dll_safe(d, d1); + + if (wfound) { + /* + * pollfd has the master sockfd in it... we + * need to use that in HANDSHAKE2 to understand + * which wsi to actually write on + */ + if (lws_client_socket_service(wfound, pollfd, wsi) < 0) { + /* closed */ + + lws_vhost_unlock(wsi->vhost); + + return -1; + } + + lws_callback_on_writable(wsi); + } else + lwsl_debug("%s: didn't find anything in txn q in HS2\n", + __func__); + + lws_vhost_unlock(wsi->vhost); + + return 0; + } + + switch (lwsi_state(wsi)) { + + case LRS_WAITING_CONNECT: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + */ + + if (!lws_client_connect_2(wsi)) { + /* closed */ + lwsl_client("closed\n"); + return -1; + } + + /* either still pending connection, or changed mode */ + return 0; + +#if defined(LWS_WITH_SOCKS5) + /* SOCKS Greeting Reply */ + case LRS_WAITING_SOCKS_GREETING_REPLY: + case LRS_WAITING_SOCKS_AUTH_REPLY: + case LRS_WAITING_SOCKS_CONNECT_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + lwsl_warn("SOCKS connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + cce = "socks conn dead"; + goto bail3; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("SOCKS read EAGAIN, retrying\n"); + return 0; + } + lwsl_err("ERROR reading from SOCKS socket\n"); + cce = "socks recv fail"; + goto bail3; + } + + switch (lwsi_state(wsi)) { + + case LRS_WAITING_SOCKS_GREETING_REPLY: + if (pt->serv_buf[0] != SOCKS_VERSION_5) + goto socks_reply_fail; + + if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) { + lwsl_client("SOCKS GR: No Auth Method\n"); + if (socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len)) + goto socks_send_msg_fail; + conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = + PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; + goto socks_send; + } + + if (pt->serv_buf[1] == SOCKS_AUTH_USERNAME_PASSWORD) { + lwsl_client("SOCKS GR: User/Pw Method\n"); + if (socks_generate_msg(wsi, + SOCKS_MSG_USERNAME_PASSWORD, + &len)) + goto socks_send_msg_fail; + conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY; + pending_timeout = + PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY; + goto socks_send; + } + goto socks_reply_fail; + + case LRS_WAITING_SOCKS_AUTH_REPLY: + if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 || + pt->serv_buf[1] != + SOCKS_SUBNEGOTIATION_STATUS_SUCCESS) + goto socks_reply_fail; + + lwsl_client("SOCKS password OK, sending connect\n"); + if (socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len)) { +socks_send_msg_fail: - *cce = "socks gen msg fail"; ++ cce = "socks gen msg fail"; + goto bail3; + } + conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY; + pending_timeout = + PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY; +socks_send: + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, len, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing to socks proxy\n"); + cce = "socks write fail"; + goto bail3; + } + + lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT); + lwsi_set_state(wsi, conn_mode); + break; + +socks_reply_fail: + lwsl_notice("socks reply: v%d, err %d\n", + pt->serv_buf[0], pt->serv_buf[1]); + cce = "socks reply fail"; + goto bail3; + + case LRS_WAITING_SOCKS_CONNECT_REPLY: + if (pt->serv_buf[0] != SOCKS_VERSION_5 || + pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS) + goto socks_reply_fail; + + lwsl_client("socks connect OK\n"); + + /* free stash since we are done with it */ + lws_client_stash_destroy(wsi); + if (lws_hdr_simple_create(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->socks_proxy_address)) { + cce = "socks connect fail"; + goto bail3; + } + + wsi->c_port = wsi->vhost->socks_proxy_port; + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + goto start_ws_handshake; + default: + break; + } + break; +#endif + + case LRS_WAITING_PROXY_REPLY: + + /* handle proxy hung up on us */ + + if (pollfd->revents & LWS_POLLHUP) { + + lwsl_warn("Proxy connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + cce = "proxy conn dead"; + goto bail3; + } + + n = recv(wsi->desc.sockfd, sb, context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug("Proxy read EAGAIN... retrying\n"); + return 0; + } + lwsl_err("ERROR reading from proxy socket\n"); + cce = "proxy read err"; + goto bail3; + } + + pt->serv_buf[13] = '\0'; - if (strncmp(sb, "HTTP/1.0 200 ", 13) && - strncmp(sb, "HTTP/1.1 200 ", 13)) { ++ if (n < 13 || (strncmp(sb, "HTTP/1.0 200 ", 13) && ++ strncmp(sb, "HTTP/1.1 200 ", 13))) { + lwsl_err("%s: ERROR proxy did not reply with h1\n", + __func__); ++ /* lwsl_hexdump_notice(sb, n); */ + cce = "proxy not h1"; + goto bail3; + } + ++ lwsl_info("%s: proxy connection extablished\n", __func__); ++ + /* clear his proxy connection timeout */ + + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* fallthru */ + + case LRS_H1C_ISSUE_HANDSHAKE: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + * + * take care of our lws_callback_on_writable + * happening at a time when there's no real connection yet + */ +#if defined(LWS_WITH_SOCKS5) +start_ws_handshake: +#endif + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + return -1; + +#if defined(LWS_WITH_TLS) + /* we can retry this... just cook the SSL BIO the first time */ + + if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !wsi->tls.ssl && + lws_ssl_client_bio_create(wsi) < 0) { + cce = "bio_create failed"; + goto bail3; + } + + if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { + n = lws_ssl_client_connect1(wsi); + if (!n) + return 0; + if (n < 0) { + cce = "lws_ssl_client_connect1 failed"; + goto bail3; + } + } else + wsi->tls.ssl = NULL; + + /* fallthru */ + + case LRS_WAITING_SSL: + + if (wsi->tls.use_ssl & LCCSCF_USE_SSL) { + n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf)); + if (!n) + return 0; + if (n < 0) { + cce = ebuf; + goto bail3; + } + } else + wsi->tls.ssl = NULL; +#endif +#if defined (LWS_WITH_HTTP2) + if (wsi->client_h2_alpn) { + /* + * We connected to the server and set up tls, and + * negotiated "h2". + * + * So this is it, we are an h2 master client connection + * now, not an h1 client connection. + */ +#if defined (LWS_WITH_TLS) + lws_tls_server_conn_alpn(wsi); +#endif + + /* send the H2 preface to legitimize the connection */ + if (lws_h2_issue_preface(wsi)) { + cce = "error sending h2 preface"; + goto bail3; + } + + break; + } +#endif + lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, + context->timeout_secs); + + /* fallthru */ + + case LRS_H1C_ISSUE_HANDSHAKE2: + p = lws_generate_client_handshake(wsi, p); + if (p == NULL) { + if (wsi->role_ops == &role_ops_raw_skt || + wsi->role_ops == &role_ops_raw_file) + return 0; + + lwsl_err("Failed to generate handshake for client\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "chs"); + return 0; + } + + /* send our request to the server */ + lws_latency_pre(context, wsi); + + w = _lws_client_wsi_master(wsi); + lwsl_info("%s: HANDSHAKE2: %p: sending headers on %p " + "(wsistate 0x%lx 0x%lx), w sock %d, wsi sock %d\n", + __func__, wsi, w, (unsigned long)wsi->wsistate, + (unsigned long)w->wsistate, w->desc.sockfd, + wsi->desc.sockfd); + + n = lws_ssl_capable_write(w, (unsigned char *)sb, (int)(p - sb)); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == p - sb); + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + lwsl_debug("ERROR writing to client socket\n"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "cws"); + return 0; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lws_callback_on_writable(wsi); + break; + } + + if (wsi->client_http_body_pending) { + lwsl_debug("body pending\n"); + lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY); + lws_set_timeout(wsi, + PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + context->timeout_secs); +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->http.proxy_clientside) + lws_callback_on_writable(wsi); +#endif + /* user code must ask for writable callback */ + break; + } + + lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); + wsi->hdr_parsing_completed = 0; + + if (lwsi_state(w) == LRS_IDLING) { + lwsi_set_state(w, LRS_WAITING_SERVER_REPLY); + w->hdr_parsing_completed = 0; +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + w->http.ah->parser_state = WSI_TOKEN_NAME_PART; + w->http.ah->lextable_pos = 0; +#if defined(LWS_WITH_CUSTOM_HEADERS) + w->http.ah->unk_pos = 0; +#endif + /* If we're (re)starting on hdr, need other implied init */ + wsi->http.ah->ues = URIES_IDLE; +#endif + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, + wsi->context->timeout_secs); + + lws_callback_on_writable(w); + + goto client_http_body_sent; + + case LRS_ISSUE_HTTP_BODY: +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->http.proxy_clientside) { + lws_callback_on_writable(wsi); + break; + } +#endif + if (wsi->client_http_body_pending) { + //lws_set_timeout(wsi, + // PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, + // context->timeout_secs); + /* user code must ask for writable callback */ + break; + } +client_http_body_sent: +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + /* prepare ourselves to do the parsing */ + wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; + wsi->http.ah->lextable_pos = 0; +#if defined(LWS_WITH_CUSTOM_HEADERS) + wsi->http.ah->unk_pos = 0; +#endif +#endif + lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, + context->timeout_secs); + break; + + case LRS_WAITING_SERVER_REPLY: + /* + * handle server hanging up on us... + * but if there is POLLIN waiting, handle that first + */ + if ((pollfd->revents & (LWS_POLLIN | LWS_POLLHUP)) == + LWS_POLLHUP) { + + lwsl_debug("Server connection %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + cce = "Peer hung up"; + goto bail3; + } + + if (!(pollfd->revents & LWS_POLLIN)) + break; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + /* interpret the server response + * + * HTTP/1.1 101 Switching Protocols + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo= + * Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC== + * Sec-WebSocket-Protocol: chat + * + * we have to take some care here to only take from the + * socket bytewise. The browser may (and has been seen to + * in the case that onopen() performs websocket traffic) + * coalesce both handshake response and websocket traffic + * in one packet, since at that point the connection is + * definitively ready from browser pov. + */ + len = 1; + while (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE && + len > 0) { + int plen = 1; + + n = lws_ssl_capable_read(wsi, &c, 1); + lws_latency(context, wsi, "send lws_issue_raw", n, + n == 1); + switch (n) { + case 0: + case LWS_SSL_CAPABLE_ERROR: + cce = "read failed"; + goto bail3; + case LWS_SSL_CAPABLE_MORE_SERVICE: + return 0; + } + + if (lws_parse(wsi, &c, &plen)) { + lwsl_warn("problems parsing header\n"); + cce = "problems parsing header"; + goto bail3; + } + } + + /* + * hs may also be coming in multiple packets, there is a 5-sec + * libwebsocket timeout still active here too, so if parsing did + * not complete just wait for next packet coming in this state + */ + if (wsi->http.ah->parser_state != WSI_PARSING_COMPLETE) + break; + +#endif + + /* + * otherwise deal with the handshake. If there's any + * packet traffic already arrived we'll trigger poll() again + * right away and deal with it that way + */ + return lws_client_interpret_server_handshake(wsi); + +bail3: + lwsl_info("closing conn at LWS_CONNMODE...SERVER_REPLY\n"); + if (cce) + lwsl_info("reason: %s\n", cce); + lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce)); + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3"); + return -1; + + default: + break; + } + + return 0; +} + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + +int LWS_WARN_UNUSED_RESULT +lws_http_transaction_completed_client(struct lws *wsi) +{ + struct lws *wsi_eff = lws_client_wsi_effective(wsi); + + lwsl_info("%s: wsi: %p, wsi_eff: %p (%s)\n", __func__, wsi, wsi_eff, + wsi_eff->protocol->name); + + if (user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff, + LWS_CALLBACK_COMPLETED_CLIENT_HTTP, + wsi_eff->user_space, NULL, 0)) { + lwsl_debug("%s: Completed call returned nonzero (role 0x%lx)\n", + __func__, (unsigned long)lwsi_role(wsi_eff)); + return -1; + } + + /* + * Are we constitutionally capable of having a queue, ie, we are on + * the "active client connections" list? + * + * If not, that's it for us. + */ + + if (lws_dll2_is_detached(&wsi->dll_cli_active_conns)) + return -1; + + /* if this was a queued guy, close him and remove from queue */ + + if (wsi->transaction_from_pipeline_queue) { + lwsl_debug("closing queued wsi %p\n", wsi_eff); + /* so the close doesn't trigger a CCE */ + wsi_eff->already_did_cce = 1; + __lws_close_free_wsi(wsi_eff, + LWS_CLOSE_STATUS_CLIENT_TRANSACTION_DONE, + "queued client done"); + } + + _lws_header_table_reset(wsi->http.ah); + + /* after the first one, they can only be coming from the queue */ + wsi->transaction_from_pipeline_queue = 1; + + wsi->http.rx_content_length = 0; + wsi->hdr_parsing_completed = 0; + + /* is there a new tail after removing that one? */ + wsi_eff = lws_client_wsi_effective(wsi); + + /* + * Do we have something pipelined waiting? + * it's OK if he hasn't managed to send his headers yet... he's next + * in line to do that... + */ + if (wsi_eff == wsi) { + /* + * Nothing pipelined... we should hang around a bit + * in case something turns up... + */ + lwsl_info("%s: nothing pipelined waiting\n", __func__); + lwsi_set_state(wsi, LRS_IDLING); + + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5); + + return 0; + } + + /* + * H1: we can serialize the queued guys into the same ah + * H2: everybody needs their own ah until their own STREAM_END + */ + + /* otherwise set ourselves up ready to go again */ + lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); + + wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; + wsi->http.ah->lextable_pos = 0; +#if defined(LWS_WITH_CUSTOM_HEADERS) + wsi->http.ah->unk_pos = 0; +#endif + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, + wsi->context->timeout_secs); + + /* If we're (re)starting on headers, need other implied init */ + wsi->http.ah->ues = URIES_IDLE; + + lwsl_info("%s: %p: new queued transaction as %p\n", __func__, wsi, + wsi_eff); + lws_callback_on_writable(wsi); + + return 0; +} + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_http_client_http_response(struct lws *_wsi) +{ + struct lws *wsi; + unsigned int resp; + + if (_wsi->http.ah && _wsi->http.ah->http_response) + return _wsi->http.ah->http_response; + + lws_vhost_lock(_wsi->vhost); + wsi = _lws_client_wsi_master(_wsi); + resp = wsi->http.ah->http_response; + lws_vhost_unlock(_wsi->vhost); + + return resp; +} +#endif + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +int +lws_client_interpret_server_handshake(struct lws *wsi) +{ + int n, port = 0, ssl = 0; + int close_reason = LWS_CLOSE_STATUS_PROTOCOL_ERR; + const char *prot, *ads = NULL, *path, *cce = NULL; + struct allocated_headers *ah; + struct lws *w = lws_client_wsi_effective(wsi); + char *p, *q; + char new_path[300]; + + lws_client_stash_destroy(wsi); + + ah = wsi->http.ah; + if (!wsi->do_ws) { + /* we are being an http client... + */ +#if defined(LWS_ROLE_H2) + if (wsi->client_h2_alpn || wsi->client_h2_substream) { + lwsl_debug("%s: %p: transitioning to h2 client\n", + __func__, wsi); + lws_role_transition(wsi, LWSIFR_CLIENT, + LRS_ESTABLISHED, &role_ops_h2); + } else +#endif + { +#if defined(LWS_ROLE_H1) + { + lwsl_debug("%s: %p: transitioning to h1 client\n", + __func__, wsi); + lws_role_transition(wsi, LWSIFR_CLIENT, + LRS_ESTABLISHED, &role_ops_h1); + } +#else + return -1; +#endif + } + + wsi->http.ah = ah; + ah->http_response = 0; + } + + /* + * well, what the server sent looked reasonable for syntax. + * Now let's confirm it sent all the necessary headers + * + * http (non-ws) client will expect something like this + * + * HTTP/1.0.200 + * server:.libwebsockets + * content-type:.text/html + * content-length:.17703 + * set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000 + */ + + wsi->http.conn_type = HTTP_CONNECTION_KEEP_ALIVE; + if (!wsi->client_h2_substream) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP); + if (wsi->do_ws && !p) { + lwsl_info("no URI\n"); + cce = "HS: URI missing"; + goto bail3; + } + if (!p) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0); + wsi->http.conn_type = HTTP_CONNECTION_CLOSE; + } + if (!p) { + cce = "HS: URI missing"; + lwsl_info("no URI\n"); + goto bail3; + } + } else { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_STATUS); + if (!p) { + cce = "HS: :status missing"; + lwsl_info("no status\n"); + goto bail3; + } + } + n = atoi(p); + if (ah) + ah->http_response = n; + + if ( +#if defined(LWS_WITH_HTTP_PROXY) + !wsi->http.proxy_clientside && +#endif + (n == 301 || n == 302 || n == 303 || n == 307 || n == 308)) { + p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); + if (!p) { + cce = "HS: Redirect code but no Location"; + goto bail3; + } + + /* Relative reference absolute path */ + if (p[0] == '/') { +#if defined(LWS_WITH_TLS) + ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL; +#endif + ads = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + /* +1 as lws_client_reset expects leading / omitted */ + path = p + 1; + } + /* Absolute (Full) URI */ + else if (strchr(p, ':')) { + if (lws_parse_uri(p, &prot, &ads, &port, &path)) { + cce = "HS: URI did not parse"; + goto bail3; + } + + if (!strcmp(prot, "wss") || !strcmp(prot, "https")) + ssl = 1; + } + /* Relative reference relative path */ + else { + /* This doesn't try to calculate an absolute path, + * that will be left to the server */ +#if defined(LWS_WITH_TLS) + ssl = wsi->tls.use_ssl & LCCSCF_USE_SSL; +#endif + ads = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS); + port = wsi->c_port; + /* +1 as lws_client_reset expects leading / omitted */ + path = new_path + 1; + if (lws_hdr_simple_ptr(wsi,_WSI_TOKEN_CLIENT_URI)) + lws_strncpy(new_path, lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_URI), sizeof(new_path)); + else { + new_path[0] = '/'; + new_path[1] = '\0'; + } + q = strrchr(new_path, '/'); + if (q) + lws_strncpy(q + 1, p, sizeof(new_path) - + (q - new_path) - 1); + else + path = p; + } + +#if defined(LWS_WITH_TLS) + if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && !ssl) { + cce = "HS: Redirect attempted SSL downgrade"; + goto bail3; + } +#endif + + if (!ads) /* make coverity happy */ { + cce = "no ads"; + goto bail3; + } + + if (!lws_client_reset(&wsi, ssl, ads, port, path, ads)) { + /* there are two ways to fail out with NULL return... + * simple, early problem where the wsi is intact, or + * we went through with the reconnect attempt and the + * wsi is already closed. In the latter case, the wsi + * has beet set to NULL additionally. + */ + lwsl_err("Redirect failed\n"); + cce = "HS: Redirect failed"; + if (wsi) + goto bail3; + + return 1; + } + return 0; + } + + if (!wsi->do_ws) { + + /* if h1 KA is allowed, enable the queued pipeline guys */ + + if (!wsi->client_h2_alpn && !wsi->client_h2_substream && + w == wsi) { /* ie, coming to this for the first time */ + if (wsi->http.conn_type == HTTP_CONNECTION_KEEP_ALIVE) + wsi->keepalive_active = 1; + else { + /* + * Ugh... now the main http connection has seen + * both sides, we learn the server doesn't + * support keepalive. + * + * That means any guys queued on us are going + * to have to be restarted from connect2 with + * their own connections. + */ + + /* + * stick around telling any new guys they can't + * pipeline to this server + */ + wsi->keepalive_rejected = 1; + + lws_vhost_lock(wsi->vhost); + lws_start_foreach_dll_safe(struct lws_dll2 *, + d, d1, + wsi->dll2_cli_txn_queue_owner.head) { + struct lws *ww = lws_container_of(d, + struct lws, + dll2_cli_txn_queue); + + /* remove him from our queue */ + lws_dll2_remove(&ww->dll2_cli_txn_queue); + /* give up on pipelining */ + ww->client_pipeline = 0; + + /* go back to "trying to connect" state */ + lws_role_transition(ww, LWSIFR_CLIENT, + LRS_UNCONNECTED, +#if defined(LWS_ROLE_H1) + &role_ops_h1); +#else +#if defined (LWS_ROLE_H2) + &role_ops_h2); +#else + &role_ops_raw); +#endif +#endif + ww->user_space = NULL; + } lws_end_foreach_dll_safe(d, d1); + lws_vhost_unlock(wsi->vhost); + } + } + +#ifdef LWS_WITH_HTTP_PROXY + wsi->http.perform_rewrite = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + if (!strncmp(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_CONTENT_TYPE), + "text/html", 9)) + wsi->http.perform_rewrite = 0; + } +#endif + + /* allocate the per-connection user memory (if any) */ + if (lws_ensure_user_space(wsi)) { + lwsl_err("Problem allocating wsi user mem\n"); + cce = "HS: OOM"; + goto bail2; + } + + /* he may choose to send us stuff in chunked transfer-coding */ + wsi->chunked = 0; + wsi->chunk_remaining = 0; /* ie, next thing is chunk size */ + if (lws_hdr_total_length(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING)) { + wsi->chunked = !strcmp(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_TRANSFER_ENCODING), + "chunked"); + /* first thing is hex, after payload there is crlf */ + wsi->chunk_parser = ELCP_HEX; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + wsi->http.rx_content_length = + atoll(lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH)); + lwsl_info("%s: incoming content length %llu\n", + __func__, (unsigned long long) + wsi->http.rx_content_length); + wsi->http.rx_content_remain = + wsi->http.rx_content_length; + } else /* can't do 1.1 without a content length or chunked */ + if (!wsi->chunked) + wsi->http.conn_type = HTTP_CONNECTION_CLOSE; + + /* + * we seem to be good to go, give client last chance to check + * headers and OK it + */ + if (w->protocol->callback(w, + LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, + w->user_space, NULL, 0)) { + + cce = "HS: disallowed by client filter"; + goto bail2; + } + + /* clear his proxy connection timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* call him back to inform him he is up */ + if (w->protocol->callback(w, + LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP, + w->user_space, NULL, 0)) { + cce = "HS: disallowed at ESTABLISHED"; + goto bail3; + } + + /* + * for pipelining, master needs to keep his ah... guys who + * queued on him can drop it now though. + */ + + if (w != wsi) + /* free up parsing allocations for queued guy */ + lws_header_table_detach(w, 0); + + lwsl_info("%s: client connection up\n", __func__); + + /* + * Did we get a response from the server with an explicit + * content-length of zero? If so, this transaction is already + * completed at the end of the header processing... + */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) && + !wsi->http.rx_content_length) + return !!lws_http_transaction_completed_client(wsi); + + return 0; + } + +#if defined(LWS_ROLE_WS) + switch (lws_client_ws_upgrade(wsi, &cce)) { + case 2: + goto bail2; + case 3: + goto bail3; + } + + return 0; +#endif + +bail3: + close_reason = LWS_CLOSE_STATUS_NOSTATUS; + +bail2: + if (wsi->protocol) { + n = 0; + if (cce) + n = (int)strlen(cce); + + lws_inform_client_conn_fail(wsi, (void *)cce, (unsigned int)n); + } + + lwsl_info("closing connection (prot %s) " + "due to bail2 connection error: %s\n", wsi->protocol ? + wsi->protocol->name : "unknown", cce); + + /* closing will free up his parsing allocations */ + lws_close_free_wsi(wsi, close_reason, "c hs interp"); + + return 1; +} +#endif + +char * +lws_generate_client_handshake(struct lws *wsi, char *pkt) +{ + char *p = pkt; + const char *meth; + const char *pp = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_SENT_PROTOCOLS); + + meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + if (!meth) { + meth = "GET"; + wsi->do_ws = 1; + } else { + wsi->do_ws = 0; + } + + if (!strcmp(meth, "RAW")) { + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + lwsl_notice("client transition to raw\n"); + + if (pp) { + const struct lws_protocols *pr; + + pr = lws_vhost_name_to_protocol(wsi->vhost, pp); + + if (!pr) { + lwsl_err("protocol %s not enabled on vhost\n", + pp); + return NULL; + } + + lws_bind_protocol(wsi, pr, __func__); + } + + if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT, + wsi->user_space, NULL, 0)) + return NULL; + + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, + &role_ops_raw_skt); + lws_header_table_detach(wsi, 1); + + return NULL; + } + + /* + * 04 example client handshake + * + * GET /chat HTTP/1.1 + * Host: server.example.com + * Upgrade: websocket + * Connection: Upgrade + * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + * Sec-WebSocket-Origin: http://example.com + * Sec-WebSocket-Protocol: chat, superchat + * Sec-WebSocket-Version: 4 + */ + + p += lws_snprintf(p, 2048, "%s %s HTTP/1.1\x0d\x0a", meth, + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI)); + + p += lws_snprintf(p, 64, "Pragma: no-cache\x0d\x0a" + "Cache-Control: no-cache\x0d\x0a"); + + p += lws_snprintf(p, 128, "Host: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_HOST)); + + if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) { + if (lws_check_opt(wsi->context->options, + LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN)) + p += lws_snprintf(p, 128, "Origin: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_ORIGIN)); + else + p += lws_snprintf(p, 128, "Origin: http://%s\x0d\x0a", + lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_ORIGIN)); + } + +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->parent && + lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + p += lws_snprintf(p, 128, "Content-Length: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)); + if (atoi(lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH))) + wsi->client_http_body_pending = 1; + } + if (wsi->parent && + lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION)) { + p += lws_snprintf(p, 128, "Authorization: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_AUTHORIZATION)); + } + if (wsi->parent && + lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + p += lws_snprintf(p, 128, "Content-Type: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)); + } +#endif + +#if defined(LWS_ROLE_WS) + if (wsi->do_ws) { + const char *conn1 = ""; + // if (!wsi->client_pipeline) + // conn1 = "close, "; + p = lws_generate_client_ws_handshake(wsi, p, conn1); + } else +#endif + { + if (!wsi->client_pipeline) + p += lws_snprintf(p, 64, "connection: close\x0d\x0a"); + } + + /* give userland a chance to append, eg, cookies */ + + if (wsi->protocol->callback(wsi, + LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, + wsi->user_space, &p, + (pkt + wsi->context->pt_serv_buf_size) - p - 12)) + return NULL; + + p += lws_snprintf(p, 4, "\x0d\x0a"); + + // puts(pkt); + + return p; +} + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + +LWS_VISIBLE int +lws_http_client_read(struct lws *wsi, char **buf, int *len) +{ + int rlen, n; + + rlen = lws_ssl_capable_read(wsi, (unsigned char *)*buf, *len); + *len = 0; + + // lwsl_notice("%s: rlen %d\n", __func__, rlen); + + /* allow the source to signal he has data again next time */ + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) + return -1; + + if (rlen == LWS_SSL_CAPABLE_ERROR) { + lwsl_debug("%s: SSL capable error\n", __func__); + return -1; + } + + if (rlen <= 0) + return 0; + + *len = rlen; + wsi->client_rx_avail = 0; + + /* + * server may insist on transfer-encoding: chunked, + * so http client must deal with it + */ +spin_chunks: + while (wsi->chunked && (wsi->chunk_parser != ELCP_CONTENT) && *len) { + switch (wsi->chunk_parser) { + case ELCP_HEX: + if ((*buf)[0] == '\x0d') { + wsi->chunk_parser = ELCP_CR; + break; + } + n = char_to_hex((*buf)[0]); + if (n < 0) { + lwsl_info("%s: chunking failure\n", __func__); + return -1; + } + wsi->chunk_remaining <<= 4; + wsi->chunk_remaining |= n; + break; + case ELCP_CR: + if ((*buf)[0] != '\x0a') { + lwsl_info("%s: chunking failure\n", __func__); + return -1; + } + wsi->chunk_parser = ELCP_CONTENT; + lwsl_info("chunk %d\n", wsi->chunk_remaining); + if (wsi->chunk_remaining) + break; + lwsl_info("final chunk\n"); + goto completed; + + case ELCP_CONTENT: + break; + + case ELCP_POST_CR: + if ((*buf)[0] != '\x0d') { + lwsl_info("%s: chunking failure\n", __func__); + + return -1; + } + + wsi->chunk_parser = ELCP_POST_LF; + break; + + case ELCP_POST_LF: + if ((*buf)[0] != '\x0a') { + lwsl_info("%s: chunking failure\n", __func__); + + return -1; + } + + wsi->chunk_parser = ELCP_HEX; + wsi->chunk_remaining = 0; + break; + } + (*buf)++; + (*len)--; + } + + if (wsi->chunked && !wsi->chunk_remaining) + return 0; + + if (wsi->http.rx_content_remain && + wsi->http.rx_content_remain < (unsigned int)*len) + n = (int)wsi->http.rx_content_remain; + else + n = *len; + + if (wsi->chunked && wsi->chunk_remaining && + wsi->chunk_remaining < n) + n = wsi->chunk_remaining; + +#if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_WITH_HUBBUB) + /* hubbub */ + if (wsi->http.perform_rewrite) + lws_rewrite_parse(wsi->http.rw, (unsigned char *)*buf, n); + else +#endif + { + struct lws *wsi_eff = lws_client_wsi_effective(wsi); + + if ( +#if defined(LWS_WITH_HTTP_PROXY) + !wsi_eff->protocol_bind_balance == + !!wsi_eff->http.proxy_clientside && +#else + !!wsi_eff->protocol_bind_balance && +#endif + user_callback_handle_rxflow(wsi_eff->protocol->callback, + wsi_eff, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, + wsi_eff->user_space, *buf, n)) { + lwsl_info("%s: RECEIVE_CLIENT_HTTP_READ returned -1\n", + __func__); + + return -1; + } + } + + if (wsi->chunked && wsi->chunk_remaining) { + (*buf) += n; + wsi->chunk_remaining -= n; + *len -= n; + } + + if (wsi->chunked && !wsi->chunk_remaining) + wsi->chunk_parser = ELCP_POST_CR; + + if (wsi->chunked && *len) + goto spin_chunks; + + if (wsi->chunked) + return 0; + + /* if we know the content length, decrement the content remaining */ + if (wsi->http.rx_content_length > 0) + wsi->http.rx_content_remain -= n; + + // lwsl_notice("rx_content_remain %lld, rx_content_length %lld\n", + // wsi->http.rx_content_remain, wsi->http.rx_content_length); + + if (wsi->http.rx_content_remain || !wsi->http.rx_content_length) + return 0; + +completed: + + if (lws_http_transaction_completed_client(wsi)) { + lwsl_notice("%s: transaction completed says -1\n", __func__); + return -1; + } + + return 0; +} + +#endif diff --cc lib/roles/http/private-lib-roles-http.h index 73a74d9c,00000000..a9108a8a mode 100644,000000..100644 --- a/lib/roles/http/private-lib-roles-http.h +++ b/lib/roles/http/private-lib-roles-http.h @@@ -1,308 -1,0 +1,306 @@@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2018 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * This is included from private-lib-core.h if either H1 or H2 roles are + * enabled + */ + +#if defined(LWS_WITH_HUBBUB) + #include + #include + #endif + +#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) +#include "private-lib-roles-http-compression.h" +#endif + +#define lwsi_role_http(wsi) (lwsi_role_h1(wsi) || lwsi_role_h2(wsi)) + +enum http_version { + HTTP_VERSION_1_0, + HTTP_VERSION_1_1, + HTTP_VERSION_2 +}; + +enum http_conn_type { + HTTP_CONNECTION_CLOSE, + HTTP_CONNECTION_KEEP_ALIVE +}; + +/* + * This is totally opaque to code using the library. It's exported as a + * forward-reference pointer-only declaration; the user can use the pointer with + * other APIs to get information out of it. + */ + +#if defined(LWS_WITH_ESP32) +typedef uint16_t ah_data_idx_t; +#else +typedef uint32_t ah_data_idx_t; +#endif + +struct lws_fragments { + ah_data_idx_t offset; + uint16_t len; + uint8_t nfrag; /* which ah->frag[] continues this content, or 0 */ + uint8_t flags; /* only http2 cares */ +}; + +#if defined(LWS_WITH_RANGES) +enum range_states { + LWSRS_NO_ACTIVE_RANGE, + LWSRS_BYTES_EQ, + LWSRS_FIRST, + LWSRS_STARTING, + LWSRS_ENDING, + LWSRS_COMPLETED, + LWSRS_SYNTAX, +}; + +struct lws_range_parsing { + unsigned long long start, end, extent, agg, budget; + const char buf[128]; + int pos; + enum range_states state; + char start_valid, end_valid, ctr, count_ranges, did_try, inside, send_ctr; +}; + +int +lws_ranges_init(struct lws *wsi, struct lws_range_parsing *rp, + unsigned long long extent); +int +lws_ranges_next(struct lws_range_parsing *rp); +void +lws_ranges_reset(struct lws_range_parsing *rp); +#endif + +/* + * these are assigned from a pool held in the context. + * Both client and server mode uses them for http header analysis + */ + +struct allocated_headers { + struct allocated_headers *next; /* linked list */ + struct lws *wsi; /* owner */ + char *data; /* prepared by context init to point to dedicated storage */ + ah_data_idx_t data_length; + /* + * the randomly ordered fragments, indexed by frag_index and + * lws_fragments->nfrag for continuation. + */ + struct lws_fragments frags[WSI_TOKEN_COUNT]; + time_t assigned; + /* + * for each recognized token, frag_index says which frag[] his data + * starts in (0 means the token did not appear) + * the actual header data gets dumped as it comes in, into data[] + */ + uint8_t frag_index[WSI_TOKEN_COUNT]; + +#ifndef LWS_NO_CLIENT + char initial_handshake_hash_base64[30]; +#endif + int hdr_token_idx; + + ah_data_idx_t pos; + ah_data_idx_t http_response; + ah_data_idx_t current_token_limit; + +#if defined(LWS_WITH_CUSTOM_HEADERS) + ah_data_idx_t unk_pos; /* to undo speculative unknown header */ + ah_data_idx_t unk_value_pos; + + ah_data_idx_t unk_ll_head; + ah_data_idx_t unk_ll_tail; +#endif + + int16_t lextable_pos; + + uint8_t in_use; + uint8_t nfrag; + char /*enum uri_path_states */ ups; + char /*enum uri_esc_states */ ues; + + char esc_stash; + char post_literal_equal; + uint8_t /* enum lws_token_indexes */ parser_state; +}; + + + +#if defined(LWS_WITH_HUBBUB) +struct lws_rewrite { + hubbub_parser *parser; + hubbub_parser_optparams params; + const char *from, *to; + int from_len, to_len; + unsigned char *p, *end; + struct lws *wsi; +}; +static LWS_INLINE int hstrcmp(hubbub_string *s, const char *p, int len) +{ + if ((int)s->len != len) + return 1; + + return strncmp((const char *)s->ptr, p, len); +} +typedef hubbub_error (*hubbub_callback_t)(const hubbub_token *token, void *pw); +LWS_EXTERN struct lws_rewrite * +lws_rewrite_create(struct lws *wsi, hubbub_callback_t cb, const char *from, const char *to); +LWS_EXTERN void +lws_rewrite_destroy(struct lws_rewrite *r); +LWS_EXTERN int +lws_rewrite_parse(struct lws_rewrite *r, const unsigned char *in, int in_len); +#endif + +struct lws_pt_role_http { + struct allocated_headers *ah_list; + struct lws *ah_wait_list; +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi_list; +#endif + int ah_wait_list_length; + uint32_t ah_pool_length; + + int ah_count_in_use; +}; + +struct lws_peer_role_http { + uint32_t count_ah; + uint32_t total_ah; +}; + +struct lws_vhost_role_http { + char http_proxy_address[128]; + const struct lws_http_mount *mount_list; + const char *error_document_404; + unsigned int http_proxy_port; +}; + +#ifdef LWS_WITH_ACCESS_LOG +struct lws_access_log { + char *header_log; + char *user_agent; + char *referrer; + unsigned long sent; + int response; +}; +#endif + +#define LWS_HTTP_CHUNK_HDR_MAX_SIZE (6 + 2) /* 6 hex digits and then CRLF */ +#define LWS_HTTP_CHUNK_TRL_MAX_SIZE (2 + 5) /* CRLF, then maybe 0 CRLF CRLF */ + +struct _lws_http_mode_related { + struct lws *new_wsi_list; + + unsigned char *pending_return_headers; + size_t pending_return_headers_len; + size_t prh_content_length; + +#if defined(LWS_WITH_HTTP_PROXY) + struct lws_rewrite *rw; + struct lws_buflist *buflist_post_body; +#endif + struct allocated_headers *ah; + struct lws *ah_wait_list; + + lws_filepos_t filepos; + lws_filepos_t filelen; + lws_fop_fd_t fop_fd; + +#if defined(LWS_WITH_RANGES) + struct lws_range_parsing range; + char multipart_content_type[64]; +#endif + +#ifdef LWS_WITH_ACCESS_LOG + struct lws_access_log access_log; +#endif +#ifdef LWS_WITH_CGI + struct lws_cgi *cgi; /* wsi being cgi master have one of these */ +#endif +#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION) + struct lws_compression_support *lcs; + lws_comp_ctx_t comp_ctx; + unsigned char comp_accept_mask; +#endif + + enum http_version request_version; + enum http_conn_type conn_type; + lws_filepos_t tx_content_length; + lws_filepos_t tx_content_remain; + lws_filepos_t rx_content_length; + lws_filepos_t rx_content_remain; + +#if defined(LWS_WITH_HTTP_PROXY) + unsigned int perform_rewrite:1; + unsigned int proxy_clientside:1; + unsigned int proxy_parent_chunked:1; +#endif + unsigned int deferred_transaction_completed:1; + unsigned int content_length_explicitly_zero:1; + unsigned int did_stream_close:1; +}; + + +#ifndef LWS_NO_CLIENT +enum lws_chunk_parser { + ELCP_HEX, + ELCP_CR, + ELCP_CONTENT, + ELCP_POST_CR, + ELCP_POST_LF, +}; +#endif + +enum lws_parse_urldecode_results { + LPUR_CONTINUE, + LPUR_SWALLOW, + LPUR_FORBID, + LPUR_EXCESSIVE, +}; + +enum lws_check_basic_auth_results { + LCBA_CONTINUE, + LCBA_FAILED_AUTH, + LCBA_END_TRANSACTION, +}; + +enum lws_check_basic_auth_results +lws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file); + +int +lws_unauthorised_basic_auth(struct lws *wsi); + +int +lws_read_h1(struct lws *wsi, unsigned char *buf, lws_filepos_t len); + +void +_lws_header_table_reset(struct allocated_headers *ah); + +LWS_EXTERN int +_lws_destroy_ah(struct lws_context_per_thread *pt, struct allocated_headers *ah); + +int +lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit, + char *uri_ptr, char ws); + - typedef struct lws_sorted_usec_list lws_sorted_usec_list_t; - +void +lws_sul_http_ah_lifecheck(lws_sorted_usec_list_t *sul); diff --cc lib/tls/openssl/openssl-x509.c index 3dee44e7,00000000..79c34580 mode 100644,000000..100644 --- a/lib/tls/openssl/openssl-x509.c +++ b/lib/tls/openssl/openssl-x509.c @@@ -1,664 -1,0 +1,664 @@@ +/* + * libwebsockets - OpenSSL-specific lws apis + * + * Copyright (C) 2010 - 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "private-lib-core.h" +#include "private-lib-tls-openssl.h" + +#if !defined(LWS_PLAT_OPTEE) +static int +dec(char c) +{ + return c - '0'; +} +#endif + +static time_t +lws_tls_openssl_asn1time_to_unix(ASN1_TIME *as) +{ +#if !defined(LWS_PLAT_OPTEE) + + const char *p = (const char *)as->data; + struct tm t; + + /* [YY]YYMMDDHHMMSSZ */ + + memset(&t, 0, sizeof(t)); + + if (strlen(p) == 13) { + t.tm_year = (dec(p[0]) * 10) + dec(p[1]) + 100; + p += 2; + } else { + t.tm_year = (dec(p[0]) * 1000) + (dec(p[1]) * 100) + + (dec(p[2]) * 10) + dec(p[3]); + p += 4; + } + t.tm_mon = (dec(p[0]) * 10) + dec(p[1]) - 1; + p += 2; + t.tm_mday = (dec(p[0]) * 10) + dec(p[1]) - 1; + p += 2; + t.tm_hour = (dec(p[0]) * 10) + dec(p[1]); + p += 2; + t.tm_min = (dec(p[0]) * 10) + dec(p[1]); + p += 2; + t.tm_sec = (dec(p[0]) * 10) + dec(p[1]); + t.tm_isdst = 0; + + return mktime(&t); +#else + return (time_t)-1; +#endif +} + +int +lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ + X509_NAME *xn; +#if !defined(LWS_PLAT_OPTEE) + char *p; +#endif + + if (!x509) + return -1; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(X509_get_notBefore) +#define X509_get_notBefore(x) X509_getm_notBefore(x) +#define X509_get_notAfter(x) X509_getm_notAfter(x) +#endif + + switch (type) { + case LWS_TLS_CERT_INFO_VALIDITY_FROM: + buf->time = lws_tls_openssl_asn1time_to_unix( + X509_get_notBefore(x509)); + if (buf->time == (time_t)-1) + return -1; + break; + + case LWS_TLS_CERT_INFO_VALIDITY_TO: + buf->time = lws_tls_openssl_asn1time_to_unix( + X509_get_notAfter(x509)); + if (buf->time == (time_t)-1) + return -1; + break; + + case LWS_TLS_CERT_INFO_COMMON_NAME: +#if defined(LWS_PLAT_OPTEE) + return -1; +#else + xn = X509_get_subject_name(x509); + if (!xn) + return -1; + X509_NAME_oneline(xn, buf->ns.name, (int)len - 2); + p = strstr(buf->ns.name, "/CN="); + if (p) + memmove(buf->ns.name, p + 4, strlen(p + 4) + 1); + buf->ns.len = (int)strlen(buf->ns.name); + return 0; +#endif + case LWS_TLS_CERT_INFO_ISSUER_NAME: + xn = X509_get_issuer_name(x509); + if (!xn) + return -1; + X509_NAME_oneline(xn, buf->ns.name, (int)len - 1); + buf->ns.len = (int)strlen(buf->ns.name); + return 0; + + case LWS_TLS_CERT_INFO_USAGE: +#if defined(LWS_HAVE_X509_get_key_usage) + buf->usage = X509_get_key_usage(x509); + break; +#else + return -1; +#endif + + case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY: + { +#ifndef USE_WOLFSSL + size_t klen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), NULL); + uint8_t *tmp, *ptmp; + + if (!klen || klen > len) + return -1; + + tmp = (uint8_t *)OPENSSL_malloc(klen); + if (!tmp) + return -1; + + ptmp = tmp; + if (i2d_X509_PUBKEY( + X509_get_X509_PUBKEY(x509), &ptmp) != (int)klen || + !ptmp || lws_ptr_diff(ptmp, tmp) != (int)klen) { + lwsl_info("%s: cert public key extraction failed\n", + __func__); + if (ptmp) + OPENSSL_free(tmp); + + return -1; + } + + buf->ns.len = (int)klen; + memcpy(buf->ns.name, tmp, klen); + OPENSSL_free(tmp); +#endif + return 0; + } + default: + return -1; + } + + return 0; +} + +int +lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ + return lws_tls_openssl_cert_info(x509->cert, type, buf, len); +} + +#if defined(LWS_WITH_NETWORK) +int +lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ +#if defined(LWS_HAVE_SSL_CTX_get0_certificate) + X509 *x509 = SSL_CTX_get0_certificate(vhost->tls.ssl_ctx); + + return lws_tls_openssl_cert_info(x509, type, buf, len); +#else + lwsl_notice("openssl is too old to support %s\n", __func__); + + return -1; +#endif +} + + + +int +lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, + union lws_tls_cert_info_results *buf, size_t len) +{ + int rc = 0; + X509 *x509; + + wsi = lws_get_network_wsi(wsi); + + x509 = SSL_get_peer_certificate(wsi->tls.ssl); + + if (!x509) { + lwsl_debug("no peer cert\n"); + + return -1; + } + + switch (type) { + case LWS_TLS_CERT_INFO_VERIFIED: + buf->verified = SSL_get_verify_result(wsi->tls.ssl) == + X509_V_OK; + break; + default: + rc = lws_tls_openssl_cert_info(x509, type, buf, len); + } + + X509_free(x509); + + return rc; +} +#endif + +int +lws_x509_create(struct lws_x509_cert **x509) +{ + *x509 = lws_malloc(sizeof(**x509), __func__); + if (*x509) + (*x509)->cert = NULL; + + return !(*x509); +} + +int +lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len) +{ + BIO* bio = BIO_new(BIO_s_mem()); + - BIO_write(bio, pem, len); ++ BIO_write(bio, pem, (int)len); + x509->cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!x509->cert) { + lwsl_err("%s: unable to parse PEM cert\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + return 0; +} + +int +lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted, + const char *common_name) +{ + char c[32], *p; + int ret; + + if (common_name) { + X509_NAME *xn = X509_get_subject_name(x509->cert); + if (!xn) + return -1; + X509_NAME_oneline(xn, c, (int)sizeof(c) - 2); + p = strstr(c, "/CN="); + if (p) + p = p + 4; + else + p = c; + + if (strcmp(p, common_name)) { + lwsl_err("%s: common name mismatch\n", __func__); + return -1; + } + } + + ret = X509_check_issued(trusted->cert, x509->cert); + if (ret != X509_V_OK) { + lwsl_err("%s: unable to verify cert relationship\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + return 0; +} + +#if defined(LWS_WITH_JOSE) +int +lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509, + const char *curves, int rsa_min_bits) +{ + int id, n, ret = -1, count; + ASN1_OBJECT *obj = NULL; + const EC_POINT *ecpoint; + const EC_GROUP *ecgroup; + EC_KEY *ecpub = NULL; + X509_PUBKEY *pubkey; + RSA *rsapub = NULL; + BIGNUM *mpi[4]; + EVP_PKEY *pkey; + + memset(jwk, 0, sizeof(*jwk)); + + pubkey = X509_get_X509_PUBKEY(x509->cert); + if (!pubkey) { + lwsl_err("%s: missing pubkey alg in cert\n", __func__); + + goto bail; + } + + if (X509_PUBKEY_get0_param(&obj, NULL, NULL, NULL, pubkey) != 1) { + lwsl_err("%s: missing pubkey alg in cert\n", __func__); + + goto bail; + } + + id = OBJ_obj2nid(obj); + if (id == NID_undef) { + lwsl_err("%s: missing pubkey alg in cert\n", __func__); + + goto bail; + } + + lwsl_debug("%s: key type %d \"%s\"\n", __func__, id, OBJ_nid2ln(id)); + + pkey = X509_get_pubkey(x509->cert); + if (!pkey) { + lwsl_notice("%s: unable to extract pubkey", __func__); + + goto bail; + } + + switch (id) { + case NID_X9_62_id_ecPublicKey: + lwsl_debug("%s: EC key\n", __func__); + jwk->kty = LWS_GENCRYPTO_KTY_EC; + + if (!curves) { + lwsl_err("%s: ec curves not allowed\n", __func__); + + goto bail1; + } + + ecpub = EVP_PKEY_get1_EC_KEY(pkey); + if (!ecpub) { + lwsl_notice("%s: missing EC pubkey\n", __func__); + + goto bail1; + } + + ecpoint = EC_KEY_get0_public_key(ecpub); + if (!ecpoint) { + lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__); + goto bail2; + } + + ecgroup = EC_KEY_get0_group(ecpub); + if (!ecgroup) { + lwsl_err("%s: EC_KEY_get0_group failed\n", __func__); + goto bail2; + } + + /* validate the curve against ones we allow */ + + if (lws_genec_confirm_curve_allowed_by_tls_id(curves, + EC_GROUP_get_curve_name(ecgroup), jwk)) + /* already logged */ + goto bail2; + + mpi[LWS_GENCRYPTO_EC_KEYEL_CRV] = NULL; + mpi[LWS_GENCRYPTO_EC_KEYEL_X] = BN_new(); /* X */ + mpi[LWS_GENCRYPTO_EC_KEYEL_D] = NULL; + mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = BN_new(); /* Y */ + +#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates) + if (EC_POINT_get_affine_coordinates(ecgroup, ecpoint, +#else + if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint, +#endif + mpi[LWS_GENCRYPTO_EC_KEYEL_X], + mpi[LWS_GENCRYPTO_EC_KEYEL_Y], + NULL) != 1) { + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); + lwsl_err("%s: EC_POINT_get_aff failed\n", __func__); + goto bail2; + } + count = LWS_GENCRYPTO_EC_KEYEL_COUNT; + n = LWS_GENCRYPTO_EC_KEYEL_X; + break; + + case NID_rsaEncryption: + lwsl_debug("%s: rsa key\n", __func__); + jwk->kty = LWS_GENCRYPTO_KTY_RSA; + + rsapub = EVP_PKEY_get1_RSA(pkey); + if (!rsapub) { + lwsl_notice("%s: missing RSA pubkey\n", __func__); + + goto bail1; + } + + if ((size_t)RSA_size(rsapub) * 8 < (size_t)rsa_min_bits) { + lwsl_err("%s: key bits %d less than minimum %d\n", + __func__, RSA_size(rsapub) * 8, rsa_min_bits); + + goto bail2; + } + +#if defined(LWS_HAVE_RSA_SET0_KEY) + /* we don't need d... but the api wants to write it */ + RSA_get0_key(rsapub, + (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_N], + (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_E], + (const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_D]); +#else + mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = rsapub->e; + mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = rsapub->n; + mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = NULL; +#endif + count = LWS_GENCRYPTO_RSA_KEYEL_D; + n = LWS_GENCRYPTO_RSA_KEYEL_E; + break; + default: + lwsl_err("%s: unknown NID\n", __func__); + goto bail2; + } + + for (; n < count; n++) { + if (!mpi[n]) + continue; + jwk->e[n].len = BN_num_bytes(mpi[n]); + jwk->e[n].buf = lws_malloc(jwk->e[n].len, "certkeyimp"); + if (!jwk->e[n].buf) { + if (id == NID_X9_62_id_ecPublicKey) { + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); + } + goto bail2; + } + BN_bn2bin(mpi[n], jwk->e[n].buf); + } + + if (id == NID_X9_62_id_ecPublicKey) { + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]); + BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]); + } + + ret = 0; + +bail2: + if (id == NID_X9_62_id_ecPublicKey) + EC_KEY_free(ecpub); + else + RSA_free(rsapub); + +bail1: + EVP_PKEY_free(pkey); +bail: + /* jwk destroy will clean any partial state */ + if (ret) + lws_jwk_destroy(jwk); + + return ret; +} + +static int +lws_x509_jwk_privkey_pem_pp_cb(char *buf, int size, int rwflag, void *u) +{ + const char *pp = (const char *)u; + int n = strlen(pp); + + if (n > size - 1) + return -1; + + memcpy(buf, pp, n + 1); + + return n; +} + +int +lws_x509_jwk_privkey_pem(struct lws_jwk *jwk, void *pem, size_t len, + const char *passphrase) +{ + BIO* bio = BIO_new(BIO_s_mem()); + BIGNUM *mpi, *dummy[6]; + EVP_PKEY *pkey = NULL; + EC_KEY *ecpriv = NULL; + RSA *rsapriv = NULL; + const BIGNUM *cmpi; + int n, m, ret = -1; + - BIO_write(bio, pem, len); ++ BIO_write(bio, pem, (int)len); + PEM_read_bio_PrivateKey(bio, &pkey, lws_x509_jwk_privkey_pem_pp_cb, + (void *)passphrase); + BIO_free(bio); + lws_explicit_bzero((void *)pem, len); + if (!pkey) { + lwsl_err("%s: unable to parse PEM privkey\n", __func__); + lws_tls_err_describe_clear(); + + return -1; + } + + /* confirm the key type matches the existing jwk situation */ + + switch (jwk->kty) { + case LWS_GENCRYPTO_KTY_EC: + if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { + lwsl_err("%s: jwk is EC but privkey isn't\n", __func__); + + goto bail; + } + ecpriv = EVP_PKEY_get1_EC_KEY(pkey); + if (!ecpriv) { + lwsl_notice("%s: missing EC key\n", __func__); + + goto bail; + } + + cmpi = EC_KEY_get0_private_key(ecpriv); + + /* quick size check first */ + + n = BN_num_bytes(cmpi); + if (jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != (uint32_t)n) { + lwsl_err("%s: jwk key size doesn't match\n", __func__); + + goto bail1; + } + + /* TODO.. check public curve / group + point */ + + jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len = n; + jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf = lws_malloc(n, "ec"); + if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf) + goto bail1; + + m = BN_bn2binpad(cmpi, jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf, + jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len); + if ((unsigned int)m != (unsigned int)BN_num_bytes(cmpi)) + goto bail1; + + break; + + case LWS_GENCRYPTO_KTY_RSA: + if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA) { + lwsl_err("%s: RSA jwk, non-RSA privkey\n", __func__); + + goto bail; + } + rsapriv = EVP_PKEY_get1_RSA(pkey); + if (!rsapriv) { + lwsl_notice("%s: missing RSA key\n", __func__); + + goto bail; + } + +#if defined(LWS_HAVE_RSA_SET0_KEY) + RSA_get0_key(rsapriv, (const BIGNUM **)&dummy[0], /* n */ + (const BIGNUM **)&dummy[1], /* e */ + (const BIGNUM **)&mpi); /* d */ + RSA_get0_factors(rsapriv, (const BIGNUM **)&dummy[4], /* p */ + (const BIGNUM **)&dummy[5]); /* q */ +#else + dummy[0] = rsapriv->n; + dummy[1] = rsapriv->e; + dummy[4] = rsapriv->p; + dummy[5] = rsapriv->q; + mpi = rsapriv->d; +#endif + + /* quick size check first */ + + n = BN_num_bytes(mpi); + if (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len != (uint32_t)n) { + lwsl_err("%s: jwk key size doesn't match\n", __func__); + + goto bail1; + } + + /* then check that n & e match what we got from the cert */ + + dummy[2] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf, + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len, + NULL); + dummy[3] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf, + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len, + NULL); + + m = BN_cmp(dummy[2], dummy[0]) | BN_cmp(dummy[3], dummy[1]); + BN_clear_free(dummy[2]); + BN_clear_free(dummy[3]); + if (m) { + lwsl_err("%s: privkey doesn't match jwk pubkey\n", + __func__); + + goto bail1; + } + + /* accept d from the PEM privkey into the JWK */ + + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].len = n; + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf = lws_malloc(n, "privjk"); + if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf) + goto bail1; + + BN_bn2bin(mpi, jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); + + /* accept p and q from the PEM privkey into the JWK */ + + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].len = BN_num_bytes(dummy[4]); + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf = lws_malloc(n, "privjk"); + if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) { + lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); + goto bail1; + } + BN_bn2bin(dummy[4], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf); + + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].len = BN_num_bytes(dummy[5]); + jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf = lws_malloc(n, "privjk"); + if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf) { + lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf); + lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf); + goto bail1; + } + BN_bn2bin(dummy[5], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf); + break; + default: + lwsl_err("%s: JWK has unknown kty %d\n", __func__, jwk->kty); + return -1; + } + + ret = 0; + +bail1: + if (jwk->kty == LWS_GENCRYPTO_KTY_EC) + EC_KEY_free(ecpriv); + else + RSA_free(rsapriv); + +bail: + EVP_PKEY_free(pkey); + + return ret; +} +#endif + +void +lws_x509_destroy(struct lws_x509_cert **x509) +{ + if (!*x509) + return; + + if ((*x509)->cert) { + X509_free((*x509)->cert); + (*x509)->cert = NULL; + } + + lws_free_set_NULL(*x509); +} diff --cc packaging/libwebsockets.spec index 031d1b4a,00000000..920e6117 mode 100644,000000..100644 --- a/packaging/libwebsockets.spec +++ b/packaging/libwebsockets.spec @@@ -1,67 -1,0 +1,67 @@@ +Name: libwebsockets +Summary: WebSocket Library - Version: 3.2.0 ++Version: 3.2.3 +Release: 1 +Group: System/Libraries +License: LGPLv2 with exceptions +URL: https://github.com/warmcat/libwebsockets +Source0: %{name}-%{version}.tar.gz +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +BuildRequires: zlib-devel +BuildRequires: pkgconfig(openssl1.1) +BuildRequires: openssl1.1 +BuildRequires: cmake + +%define _optdeveldir /opt/usr/devel/usr/ + +%description +C Websockets Server Library + +%package devel +Summary: Development files for %{name} +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +Development files needed for building websocket clients and servers + +%prep +%setup -q -n %{name}-%{version} + +%build + +%cmake -DLWS_WITH_SSL=On \ + -DLWS_WITHOUT_TESTAPPS=ON \ + -DLWS_WITH_SERVER_STATUS=ON \ + -DLWS_IPV6=ON \ + -DLWS_WITH_SO_BINDTODEVICE=ON \ + -DLWS_WITH_HTTP2=OFF\ + . + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} + +%make_install + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%manifest %{name}.manifest +%defattr(-,root,root,-) +%{_libdir}/libwebsockets*.so.* +%license LICENSE + +%files devel +%defattr(-,root,root,-) +%{_includedir}/libwebsockets.h +%{_includedir}/lws-plugin-ssh.h +%{_includedir}/lws_config.h +%{_includedir}/libwebsockets/* +%{_libdir}/libwebsockets.so +%{_libdir}/pkgconfig/* +%{_libdir}/cmake/*