./configure --prefix=/usr/local CPPFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/include" LDFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/lib"
参考老外服务器代码:
+https://github.com/ppelleti/https-example
+确保libevent在2.1.2之上版本。
+确保系统安装openssl,确保libevent_openssl.so存在
+libevent
+http://libevent.org/
+*这里要注意,在执行./config的时候一定要加上 --prefix=/usr/local 和--openssldir=/usr/local/ssl/
+否则libevent是找不到openssl库,那就不会编译生成带有openssl的libevent库了。*
+问题(1)如果出现
+ ./autogen.sh: 11: ./autogen.sh: aclocal: not found需要安装automake工具。
+ sudo apt-get install automake问题(2) 如果出现
+ configure.ac:137: error: possibly undefined macro: AC_PROG_LIBTOOL需要安装libtool工具。
+ sudo apt-get install libtool sudo apt-get install libsysfs-dev如果成功,执行
+ ./configure make sudo make install结合我们之前用libevent实现的登陆服务器代码和老外的参考代码,
+现在提供登陆的功能的https服务器代码.
+此时服务器路径文件包括以下:
+ $ls cJSON.c https-common.h server-private-key.pem cJSON.h https-server.c server-certificate-chain.pem https-common.c Makefile其中 server-certificate-chain.pem 为 当前https服务器的私人证书,此证书并没有CA认证,如果想要合法证书请购买。(改证书里携带 此服务器 认证公钥)
+其中 server-private-key.pem 是此服务器 认证私钥
+其中 https-common.h https-server.c https-common.c 为https服务器源码
+https-common.h
+ /* (c) Oblong Industries */ #ifndef COMMON_MAN #define COMMON_MAN /** * Rather than using the standard https port of 443, use this one. */ #define COMMON_HTTPS_PORT 8421 /** * This is the string the client tells the server in the POST request. */ #define COMMON_PASSCODE "R23" /** * If an OpenSSL function returns a return value indicating failure * (for non-pointery functions, generally 1 means success, and non-1 * means failure), then usually it ought to have left one or more * entries in the OpenSSL "error stack", which is a bit of thread-local * state containing error information. * * This function is a simple way to handle OpenSSL errors (something * better may be needed in a real application), which prints the * name of the function which failed (which you must supply as an * argument), prints the OpenSSL error stack (which hopefully says * something meaningful) and exits. */ void die_most_horribly_from_openssl_error (const char *func); void error_exit (const char *fmt, ...); #define error_report printf #define info_report printf /** * Calls some OpenSSL setup functions, which both the client and * server need to do. */ void common_setup (void); #endif /* COMMON_MAN */https-common.c
+ /* (c) Oblong Industries */ #include <signal.h> #include "https-common.h" #include <openssl/ssl.h> #include <openssl/err.h> #include <event2/event.h> /* 这个是调用openSSL提供的打印log接口 */ void die_most_horribly_from_openssl_error (const char *func) { fprintf (stderr, "%s failed:\n", func); /* This is the OpenSSL function that prints the contents of the * error stack to the specified file handle. */ ERR_print_errors_fp (stderr); exit (EXIT_FAILURE); } void error_exit (const char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); exit (EXIT_FAILURE); } /* OpenSSL has a habit of using uninitialized memory. (They turn up their * nose at tools like valgrind.) To avoid spurious valgrind errors (as well * as to allay any concerns that the uninitialized memory is actually * affecting behavior), let's install a custom malloc function which is * actually calloc. */ static void *my_zeroing_malloc (size_t howmuch) { return calloc (1, howmuch); } void common_setup (void) { signal (SIGPIPE, SIG_IGN); CRYPTO_set_mem_functions (my_zeroing_malloc, realloc, free); SSL_library_init (); SSL_load_error_strings (); OpenSSL_add_all_algorithms (); printf ("Using OpenSSL version \"%s\"\nand libevent version \"%s\"\n", SSLeay_version (SSLEAY_VERSION), event_get_version ()); }https-server.c
+ /** * @file https-server.c * @brief * @author liu_danbing * @version 1.0 * @date 2016-10-26 */ #include "https-common.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/stat.h> #include <sys/socket.h> #include <fcntl.h> #include <unistd.h> #include <dirent.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <event2/bufferevent.h> #include <event2/bufferevent_ssl.h> #include <event2/event.h> #include <event2/http.h> #include <event2/buffer.h> #include <event2/util.h> #include <event2/keyvalq_struct.h> #include <cJSON.h> #define MYHTTPD_SIGNATURE "MoCarHttpd v0.1" #ifdef EVENT__HAVE_NETINET_IN_H #include <netinet/in.h> # ifdef _XOPEN_SOURCE_EXTENDED # include <arpa/inet.h> # endif #endif unsigned short serverPort = COMMON_HTTPS_PORT; /* Instead of casting between these types, create a union with all of them, * to avoid -Wstrict-aliasing warnings. */ typedef union { struct sockaddr_storage ss; struct sockaddr sa; struct sockaddr_in in; struct sockaddr_in6 i6; } sock_hop; /* This callback gets invoked when we get any http request that doesn't match * any other callback. Like any evhttp server callback, it has a simple job: * it must eventually call evhttp_send_error() or evhttp_send_reply(). */ static void login_cb (struct evhttp_request *req, void *arg) { struct evbuffer *evb = NULL; const char *uri = evhttp_request_get_uri (req); struct evhttp_uri *decoded = NULL; /* 判断 req 是否是GET 请求 */ if (evhttp_request_get_command (req) == EVHTTP_REQ_GET) { struct evbuffer *buf = evbuffer_new(); if (buf == NULL) return; evbuffer_add_printf(buf, "Requested: %s\n", uri); evhttp_send_reply(req, HTTP_OK, "OK", buf); return; } /* 这里只处理Post请求, Get请求,就直接return 200 OK */ if (evhttp_request_get_command (req) != EVHTTP_REQ_POST) { evhttp_send_reply (req, 200, "OK", NULL); return; } printf ("Got a POST request for <%s>\n", uri); //判断此URI是否合法 decoded = evhttp_uri_parse (uri); if (! decoded) { printf ("It's not a good URI. Sending BADREQUEST\n"); evhttp_send_error (req, HTTP_BADREQUEST, 0); return; } /* Decode the payload */ struct evbuffer *buf = evhttp_request_get_input_buffer (req); evbuffer_add (buf, "", 1); /* NUL-terminate the buffer */ char *payload = (char *) evbuffer_pullup (buf, -1); int post_data_len = evbuffer_get_length(buf); char request_data_buf[4096] = {0}; memcpy(request_data_buf, payload, post_data_len); printf("[post_data][%d]=\n %s\n", post_data_len, payload); /* 具体的:可以根据Post的参数执行相应操作,然后将结果输出 ... */ //unpack json cJSON* root = cJSON_Parse(request_data_buf); cJSON* username = cJSON_GetObjectItem(root, "username"); cJSON* password = cJSON_GetObjectItem(root, "password"); printf("username = %s\n", username->valuestring); printf("password = %s\n", password->valuestring); cJSON_Delete(root); //packet json root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "result", "ok"); cJSON_AddStringToObject(root, "sessionid", "xxxxxxxx"); char *response_data = cJSON_Print(root); cJSON_Delete(root); /* This holds the content we're sending. */ //HTTP header evhttp_add_header(evhttp_request_get_output_headers(req), "Server", MYHTTPD_SIGNATURE); evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/plain; charset=UTF-8"); evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); evb = evbuffer_new (); evbuffer_add_printf(evb, "%s", response_data); //将封装好的evbuffer 发送给客户端 evhttp_send_reply(req, HTTP_OK, "OK", evb); if (decoded) evhttp_uri_free (decoded); if (evb) evbuffer_free (evb); printf("[response]:\n"); printf("%s\n", response_data); free(response_data); } /** * This callback is responsible for creating a new SSL connection * and wrapping it in an OpenSSL bufferevent. This is the way * we implement an https server instead of a plain old http server. */ static struct bufferevent* bevcb (struct event_base *base, void *arg) { struct bufferevent* r; SSL_CTX *ctx = (SSL_CTX *) arg; r = bufferevent_openssl_socket_new (base, -1, SSL_new (ctx), BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE); return r; } static void server_setup_certs (SSL_CTX *ctx, const char *certificate_chain, const char *private_key) { info_report ("Loading certificate chain from '%s'\n" "and private key from '%s'\n", certificate_chain, private_key); if (1 != SSL_CTX_use_certificate_chain_file (ctx, certificate_chain)) die_most_horribly_from_openssl_error ("SSL_CTX_use_certificate_chain_file"); if (1 != SSL_CTX_use_PrivateKey_file (ctx, private_key, SSL_FILETYPE_PEM)) die_most_horribly_from_openssl_error ("SSL_CTX_use_PrivateKey_file"); if (1 != SSL_CTX_check_private_key (ctx)) die_most_horribly_from_openssl_error ("SSL_CTX_check_private_key"); } static int serve_some_http (void) { struct event_base *base; struct evhttp *http; struct evhttp_bound_socket *handle; base = event_base_new (); if (! base) { fprintf (stderr, "Couldn't create an event_base: exiting\n"); return 1; } /* 创建一个 evhttp 句柄,去处理用户端的requests请求 */ http = evhttp_new (base); if (! http) { fprintf (stderr, "couldn't create evhttp. Exiting.\n"); return 1; } /* 创建SSL上下文环境 ,可以理解为 SSL句柄 */ SSL_CTX *ctx = SSL_CTX_new (SSLv23_server_method ()); SSL_CTX_set_options (ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_SSLv2); /* Cheesily pick an elliptic curve to use with elliptic curve ciphersuites. * We just hardcode a single curve which is reasonably decent. * See http://www.mail-archive.com/openssl-dev@openssl.org/msg30957.html */ EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1); if (! ecdh) die_most_horribly_from_openssl_error ("EC_KEY_new_by_curve_name"); if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh)) die_most_horribly_from_openssl_error ("SSL_CTX_set_tmp_ecdh"); /* 选择服务器证书 和 服务器私钥. */ const char *certificate_chain = "server-certificate-chain.pem"; const char *private_key = "server-private-key.pem"; /* 设置服务器证书 和 服务器私钥 到 OPENSSL ctx上下文句柄中 */ server_setup_certs (ctx, certificate_chain, private_key); /* 使我们创建好的evhttp句柄 支持 SSL加密 实际上,加密的动作和解密的动作都已经帮 我们自动完成,我们拿到的数据就已经解密之后的 */ evhttp_set_bevcb (http, bevcb, ctx); /* 设置http回调函数 */ //默认回调 //evhttp_set_gencb (http, send_document_cb, NULL); //专属uri路径回调 evhttp_set_cb(http, "/login", login_cb, NULL); /* 设置监听IP和端口 */ handle = evhttp_bind_socket_with_handle (http, "0.0.0.0", serverPort); if (! handle) { fprintf (stderr, "couldn't bind to port %d. Exiting.\n",(int) serverPort); return 1; } { /* 打印收到的客户端链接信息 */ sock_hop ss; evutil_socket_t fd; ev_socklen_t socklen = sizeof (ss); char addrbuf[128]; void *inaddr; const char *addr; int got_port = -1; fd = evhttp_bound_socket_get_fd (handle); memset (&ss, 0, sizeof(ss)); if (getsockname (fd, &ss.sa, &socklen)) { perror ("getsockname() failed"); return 1; } if (ss.ss.ss_family == AF_INET) { got_port = ntohs (ss.in.sin_port); inaddr = &ss.in.sin_addr; } else if (ss.ss.ss_family == AF_INET6) { got_port = ntohs (ss.i6.sin6_port); inaddr = &ss.i6.sin6_addr; } else { fprintf (stderr, "Weird address family %d\n", ss.ss.ss_family); return 1; } addr = evutil_inet_ntop (ss.ss.ss_family, inaddr, addrbuf, sizeof (addrbuf)); if (addr) printf ("Listening on %s:%d\n", addr, got_port); else { fprintf (stderr, "evutil_inet_ntop failed\n"); return 1; } } /* 开始阻塞监听 (永久执行) */ event_base_dispatch (base); return 0; } int main (int argc, char **argv) { /*OpenSSL 初始化 */ common_setup (); if (argc > 1) { char *end_ptr; long lp = strtol(argv[1], &end_ptr, 0); if (*end_ptr) { fprintf(stderr, "Invalid integer\n"); return -1; } if (lp <= 0) { fprintf(stderr, "Port must be positive\n"); return -1; } if (lp >= USHRT_MAX) { fprintf(stderr, "Port must fit 16-bit range\n"); return -1; } serverPort = (unsigned short)lp; } /* now run http server (never returns) */ return serve_some_http (); }Makefile
+ CC=gcc CPPFLAGS= -I. -I./include CFLAGS=-Wall LIBS=-lpthread -levent -lm -lssl -lcrypto -levent_openssl #找到当前目录下所有的.c文件 src = $(wildcard *.c) #将当前目录下所有的.c 转换成.o给obj obj = $(patsubst %.c, %.o, $(src)) server=server target=$(server) ALL:$(target) #生成所有的.o文件 $(obj):%.o:%.c $(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS) #test_main程序 $(server): https-server.o https-common.o cJSON.o $(CC) $^ -o $@ $(LIBS) #clean指令 clean: -rm -rf $(obj) $(target) distclean: -rm -rf $(obj) $(target) #将clean目标 改成一个虚拟符号 .PHONY: clean ALL distclean./configure --prefix=/usr/local CPPFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/include" LDFLAGS="-I/usr/local/Cellar/openssl/1.0.2h_1/lib"