/************************************************************************* > File Name: detect_http2_plugin.cpp > Author: > Mail: > Created Time: 2018年09月11日 星期二 15时38分54秒 ************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Magic Header : PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n */ static const uint8_t kMagicHello[] = { 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a }; #define get_u_int8_t(X,O) (*(u_int8_t *)(((u_int8_t *)X) + O)) #define TFE_BASIC_CFG "conf/tfe/tfe.conf" #define MAGIC_FRAME_LENGTH 24 #define SET_FRAME_LENGTH 9 struct event_timer_ctx { Http2Plugin *plugin; unsigned int thread_id; }; void load_logging_conf(const char *config) { RTLogInit2Data *logging_sc_lid = logger(); logging_sc_lid->handle = MESA_create_runtime_log_handle("http2", RLOG_LV_DEBUG); if(logging_sc_lid->handle == NULL){ TFE_LOG_ERROR(logging_sc_lid->handle, "Create log runtime_log_handle error, init failed!"); } return; } UNUSED static void http2_plugin_timer_gc_cb(evutil_socket_t fd, short what, void * arg) { struct tfe_h2_stream *h2_stream_info = (struct tfe_h2_stream *)arg; sess_data_ctx_fini(h2_stream_info, NULL, h2_stream_info->thread_id); // Although this function is not called now, // there is a potential bug in memory leaks where it is called. // A better practice is to free the memory in the sess_data_ctx_fini() function. free(h2_stream_info); h2_stream_info = NULL; } static int http2_plugin_init(struct tfe_proxy * proxy) { UNUSED Http2Plugin *plugin = http2_plugin(); unsigned int thread_nu = tfe_proxy_get_work_thread_count(); load_logging_conf(TFE_BASIC_CFG); for (unsigned int thread_id = 0; thread_id < thread_nu; thread_id++){ #if 0 struct tfe_session_info_t h2_stream_info = plugin->h2_stream_info[thread_id]; TAILQ_INIT(&(h2_stream_info.list)); h2_stream_info.thread_id = thread_id; h2_stream_info.as_client = 0; struct event_base * ev_base = tfe_proxy_get_work_thread_evbase(thread_id); struct timeval timer = {0, 500 * 1000}; struct event * event = event_new(ev_base, -1, EV_PERSIST, http2_plugin_timer_gc_cb, &h2_stream_info); if (unlikely(event == NULL)){ TFE_LOG_ERROR(logger()->handle, "Create timer error, init failed!"); } evtimer_add(event, &timer); plugin->event[thread_id] = event; #endif } return 0; } static void http2_plugin_deinit(struct tfe_proxy * proxy) { return; } static int http2_stream_open(const struct tfe_stream *stream, unsigned int thread_id, enum tfe_conn_dir dir, void ** pme) { struct stream_tap_info_t *tapinfo = ALLOC(struct stream_tap_info_t, 1); assert(tapinfo); memset(tapinfo, 0, sizeof(struct stream_tap_info_t)); tapinfo->preempted = 0; tapinfo->h2_stream_info = tfe_session_info_init(); *pme = (void *)tapinfo; return 0; } /*setting frame: 00 00 06 04 00 00 00 00 00 00 04**/ static int search_up_stream_data(const unsigned char * data, size_t len) { uint8_t indetifier = 0; if ((get_u_int8_t(data, 3) != 0x4) && (get_u_int8_t(data, 4) != 0) && (get_u_int8_t(data, 9) != 0)) { return 0; } indetifier = get_u_int8_t(data, 10); if (indetifier < NGHTTP2_SETTINGS_HEADER_TABLE_SIZE || indetifier > NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE) { return 0; } return 1; } static enum tfe_stream_action http2_stream_data(const struct tfe_stream * stream, unsigned int thread_id, enum tfe_conn_dir dir, const unsigned char * data, size_t len, void ** pme) { static unsigned int defer_bytes = 0; struct stream_tap_info_t *tapinfo = (struct stream_tap_info_t *)(*pme); if (tapinfo->preempted == 0){ if (dir == CONN_DIR_UPSTREAM){ if (len < SET_FRAME_LENGTH){ defer_bytes = SET_FRAME_LENGTH; tfe_stream_action_set_opt(stream, ACTION_OPT_DEFER_BYTES, (void *) &defer_bytes, sizeof(defer_bytes)); return ACTION_DEFER_DATA; } if (search_up_stream_data(data, len)){ }else{ goto detach; } } if (dir == CONN_DIR_DOWNSTREAM){ /* Protocol Identification, we need 24 bytes at least to tell it is HTTP2 or not */ if (len < MAGIC_FRAME_LENGTH){ defer_bytes = MAGIC_FRAME_LENGTH; tfe_stream_action_set_opt(stream, ACTION_OPT_DEFER_BYTES, (void *) &defer_bytes, sizeof(defer_bytes)); return ACTION_DEFER_DATA; } if (memcmp(data, kMagicHello, MAGIC_FRAME_LENGTH) != 0){ goto finish; } } if (tfe_stream_preempt(stream) != 0) goto detach; tapinfo->preempted = 1; } return (dir == CONN_DIR_DOWNSTREAM) ? detect_down_stream_protocol(tapinfo->h2_stream_info, stream, thread_id, data, len) : detect_up_stream_protocol(tapinfo->h2_stream_info, stream, thread_id, data, len); detach: tfe_stream_detach(stream); finish: return ACTION_FORWARD_DATA; } static void http2_stream_close(const struct tfe_stream * stream, unsigned int thread_id, enum tfe_stream_close_reason reason, void ** pme) { struct stream_tap_info_t *tapinfo = (struct stream_tap_info_t *)(*pme); sess_data_ctx_fini(tapinfo->h2_stream_info, stream, thread_id); free(tapinfo->h2_stream_info); tapinfo->h2_stream_info = NULL; free(tapinfo); tapinfo = NULL; *pme = NULL; return; } static struct tfe_plugin __nghttp2_plugin_info = { .symbol = "HTTP2", .type = TFE_PLUGIN_TYPE_PROTOCOL, .on_init = http2_plugin_init, .on_deinit = http2_plugin_deinit, .on_open = http2_stream_open, .on_data = http2_stream_data, .on_close = http2_stream_close }; TFE_PLUGIN_REGISTER(HTTP2, __nghttp2_plugin_info)