added https proxying without certificate check. this is different from CONNECT. when this feature is used, the proxy establishes a https connection to the origin server
This commit is contained in:
		| @ -199,6 +199,7 @@ struct loccfg_t | ||||
| 	struct | ||||
| 	{ | ||||
| 		unsigned int allow_http: 1; | ||||
| 		unsigned int allow_https: 1; | ||||
| 		unsigned int allow_connect: 1; | ||||
| 		unsigned int allow_intercept: 2; /* 0: no, 1: proxy, 2: local */ | ||||
| 		unsigned int allow_upgrade: 1; | ||||
| @ -468,6 +469,7 @@ static int get_server_root ( | ||||
| { | ||||
| 	qse_http_method_t mth; | ||||
| 	qse_mchar_t* qpath; | ||||
| 	int proto_len; | ||||
|  | ||||
| 	qse_memset (root, 0, QSE_SIZEOF(*root)); | ||||
| 	mth = qse_htre_getqmethodtype (qinfo->req); | ||||
| @ -534,13 +536,12 @@ static int get_server_root ( | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| /* TODO: handle https:// .... */ | ||||
| 	if (loccfg->proxy.allow_http && | ||||
| 	    qse_mbszcasecmp (qpath, QSE_MT("http://"), 7) == 0) | ||||
| 	if ((loccfg->proxy.allow_http && qse_mbszcasecmp (qpath, QSE_MT("http://"), (proto_len = 7)) == 0) || | ||||
| 	    (loccfg->proxy.allow_https && qse_mbszcasecmp (qpath, QSE_MT("https://"), (proto_len = 8)) == 0)) | ||||
| 	{ | ||||
| 		qse_mchar_t* host, * slash; | ||||
|  | ||||
| 		host = qpath + 7; | ||||
| 		host = qpath + proto_len; | ||||
| 		slash = qse_mbschr (host, QSE_MT('/')); | ||||
|  | ||||
| 		if (slash && slash - host > 0) | ||||
| @ -561,6 +562,7 @@ static int get_server_root ( | ||||
| 			host = host - 1; | ||||
| 			root->u.proxy.host = host; | ||||
|  | ||||
| 			if (proto_len == 8) root->u.proxy.flags |= QSE_HTTPD_RSRC_PROXY_DST_SECURE; | ||||
| 			if (qse_mbstonwad (host, &root->u.proxy.dst.nwad) <= -1)  | ||||
| 			{ | ||||
| 				root->u.proxy.flags |= QSE_HTTPD_RSRC_PROXY_DST_STR; | ||||
| @ -576,7 +578,6 @@ static int get_server_root ( | ||||
|  | ||||
| /* TODO: refrain from manipulating the request like this */ | ||||
| 			qinfo->req->u.q.path = slash; /* TODO: use setqpath or something... */ | ||||
|  | ||||
| 			goto proxy_ok; | ||||
| 		} | ||||
| 		else | ||||
| @ -1513,6 +1514,11 @@ static int load_loccfg_proxy (qse_httpd_t* httpd, qse_xli_t* xli, qse_xli_list_t | ||||
| 	if (!pair && default_proxy) pair = qse_xli_findpair (xli, default_proxy, QSE_T("http")); /* server-default.proxy.http */ | ||||
| 	if (pair) cfg->proxy.allow_http = get_boolean ((qse_xli_str_t*)pair->val); | ||||
|  | ||||
| 	pair = QSE_NULL; | ||||
| 	if (proxy) pair = qse_xli_findpair (xli, proxy, QSE_T("https")); /* server.host[].location[].proxy.https */ | ||||
| 	if (!pair && default_proxy) pair = qse_xli_findpair (xli, default_proxy, QSE_T("https")); /* server-default.proxy.https */ | ||||
| 	if (pair) cfg->proxy.allow_https = get_boolean ((qse_xli_str_t*)pair->val); | ||||
|  | ||||
| 	pair = QSE_NULL; | ||||
| 	if (proxy) pair = qse_xli_findpair (xli, proxy, QSE_T("connect")); | ||||
| 	if (!pair && default_proxy) pair = qse_xli_findpair (xli, default_proxy, QSE_T("connect")); | ||||
| @ -2067,6 +2073,7 @@ static int open_config_file (qse_httpd_t* httpd) | ||||
| 		{ QSE_T("server-default.error-foot"),                        { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server-default.proxy"),                             { QSE_XLI_SCM_VALLIST | QSE_XLI_SCM_KEYNODUP, 0, 0      }  }, | ||||
| 		{ QSE_T("server-default.proxy.http"),                        { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server-default.proxy.https"),                       { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server-default.proxy.connect"),                     { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server-default.proxy.intercept"),                   { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server-default.proxy.upgrade"),                     { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| @ -2125,6 +2132,7 @@ static int open_config_file (qse_httpd_t* httpd) | ||||
| 		{ QSE_T("server.host.location.error-foot"),                  { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server.host.location.proxy"),                       { QSE_XLI_SCM_VALLIST | QSE_XLI_SCM_KEYNODUP, 0, 0      }  }, | ||||
| 		{ QSE_T("server.host.location.proxy.http"),                  { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server.host.location.proxy.https"),                 { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server.host.location.proxy.connect"),               { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server.host.location.proxy.intercept"),             { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
| 		{ QSE_T("server.host.location.proxy.upgrade"),               { QSE_XLI_SCM_VALSTR  | QSE_XLI_SCM_KEYNODUP, 1, 1      }  }, | ||||
|  | ||||
| @ -58,7 +58,8 @@ enum qse_httpd_errnum_t | ||||
| 	QSE_HTTPD_EAGAIN, | ||||
|  | ||||
| 	QSE_HTTPD_ENOSVR,  /* no active servers */ | ||||
| 	QSE_HTTPD_ECONN, | ||||
| 	QSE_HTTPD_ECONN,   /* connection failure */ | ||||
| 	QSE_HTTPD_ESCONN,  /* secure connection failure */ | ||||
| 	QSE_HTTPD_ENOBUF,  /* no buffer available */ | ||||
| 	QSE_HTTPD_EDISCON, /* client disconnnected */ | ||||
| 	QSE_HTTPD_EBADREQ, /* bad request */ | ||||
| @ -146,12 +147,31 @@ struct qse_httpd_stat_t | ||||
| 	qse_ntime_t mtime; | ||||
| }; | ||||
|  | ||||
| enum qse_httpd_peer_flag_t | ||||
| { | ||||
| 	QSE_HTTPD_PEER_SECURE = (1 << 0), | ||||
|  | ||||
| 	/* ---------------------------------- */ | ||||
|  | ||||
| 	/* indicate the underlying socket is connected. internal use only. don't set it */ | ||||
| 	QSE_HTTPD_PEER_CONNECTED = (1 << 20), | ||||
| 	 | ||||
| 	/* internal use only */ | ||||
| 	QSE_HTTPD_PEER_PENDING = (1 << 21), | ||||
|  | ||||
| 	/* all internal enumerators */ | ||||
| 	QSE_HTTPD_PEER_ALL_INTERNALS = (QSE_HTTPD_PEER_CONNECTED | QSE_HTTPD_PEER_PENDING) | ||||
| }; | ||||
| typedef enum qse_httpd_peer_flag_t qse_httpd_peer_flag_t; | ||||
|  | ||||
| typedef struct qse_httpd_peer_t qse_httpd_peer_t; | ||||
| struct qse_httpd_peer_t | ||||
| { | ||||
| 	int flags; /* 0 or bitwised-OR'ed of qse_httpd_peer_flag_t enumerators */ | ||||
| 	qse_nwad_t nwad; | ||||
| 	qse_nwad_t local; /* local side address facing the peer */ | ||||
| 	qse_httpd_hnd_t  handle; | ||||
| 	qse_httpd_hnd_t handle; | ||||
| 	qse_httpd_hnd_t handle2; | ||||
| }; | ||||
|  | ||||
| enum qse_httpd_mux_mask_t | ||||
| @ -529,7 +549,7 @@ typedef struct qse_httpd_task_trigger_t qse_httpd_task_trigger_t; | ||||
| struct qse_httpd_task_trigger_t | ||||
| { | ||||
| 	int flags; /**< [IN] bitwise-ORed of #qse_httpd_task_trigger_flag_t enumerators*/ | ||||
| 	int cmask; /* client mask - QSE_HTTPD_TASK_TRIGGER_READ | QSE_HTTPD_TASK_TRIGGER_WRITE */ | ||||
| 	unsigned int cmask; /* client mask - QSE_HTTPD_TASK_TRIGGER_READ | QSE_HTTPD_TASK_TRIGGER_WRITE */ | ||||
| 	struct | ||||
| 	{ | ||||
| 		int       mask; /* QSE_HTTPD_TASK_TRIGGER_READ | QSE_HTTPD_TASK_TRIGGER_WRITE */ | ||||
| @ -772,10 +792,11 @@ enum qse_httpd_rsrc_proxy_flag_t | ||||
| 	QSE_HTTPD_RSRC_PROXY_ALLOW_UPGRADE   = (1 << 2), /* allow protocol upgrade */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_X_FORWARDED     = (1 << 3), /* add x-forwarded-for and x-forwarded-proto */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_DST_STR         = (1 << 4), /* destination is an unresovled string pointed to by dst.str */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_ENABLE_DNS      = (1 << 5), /* dns service enabled (udp) */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_ENABLE_URS      = (1 << 6), /* url rewriting enabled (udp) */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_DNS_SERVER      = (1 << 7), /* dns address specified */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_URS_SERVER      = (1 << 8), /* urs address specified */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_DST_SECURE      = (1 << 5), /* use secure connection to destination */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_ENABLE_DNS      = (1 << 6), /* dns service enabled (udp) */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_ENABLE_URS      = (1 << 7), /* url rewriting enabled (udp) */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_DNS_SERVER      = (1 << 8), /* dns address specified */ | ||||
| 	QSE_HTTPD_RSRC_PROXY_URS_SERVER      = (1 << 9), /* urs address specified */ | ||||
| }; | ||||
| typedef enum qse_httpd_rsrc_proxy_flag_t qse_httpd_rsrc_proxy_flag_t; | ||||
|  | ||||
|  | ||||
| @ -35,31 +35,31 @@ struct task_proxy_arg_t | ||||
| typedef struct task_proxy_t task_proxy_t; | ||||
| struct task_proxy_t | ||||
| { | ||||
| #define PROXY_INIT_FAILED          (1 << 0) | ||||
| #define PROXY_RAW                  (1 << 1) | ||||
| #define PROXY_TRANSPARENT          (1 << 2) | ||||
| #define PROXY_DNS_SERVER           (1 << 3) /* dns server address specified */ | ||||
| #define PROXY_URS_SERVER           (1 << 4) /* urs server address specified */ | ||||
| #define PROXY_OUTBAND_PEER_NAME    (1 << 5) /* the peer_name pointer points to | ||||
| #define PROXY_INIT_FAILED          (1u << 0) | ||||
| #define PROXY_RAW                  (1u << 1) | ||||
| #define PROXY_TRANSPARENT          (1u << 2) | ||||
| #define PROXY_DNS_SERVER           (1u << 3) /* dns server address specified */ | ||||
| #define PROXY_URS_SERVER           (1u << 4) /* urs server address specified */ | ||||
| #define PROXY_OUTBAND_PEER_NAME    (1u << 5) /* the peer_name pointer points to | ||||
|                                                a separate memory chunk outside | ||||
|                                                the task_proxy_t chunk. explicit | ||||
|                                                deallocatin is required */ | ||||
| #define PROXY_RESOLVE_PEER_NAME    (1 << 6) | ||||
| #define PROXY_PEER_NAME_RESOLVING  (1 << 7) | ||||
| #define PROXY_PEER_NAME_RESOLVED   (1 << 8) | ||||
| #define PROXY_PEER_NAME_UNRESOLVED (1 << 9) | ||||
| #define PROXY_REWRITE_URL          (1 << 10) | ||||
| #define PROXY_URL_REWRITING        (1 << 11) | ||||
| #define PROXY_URL_PREREWRITTEN     (1 << 12) /* URL has been prerewritten in prerewrite(). */ | ||||
| #define PROXY_URL_REWRITTEN        (1 << 13) | ||||
| #define PROXY_URL_REDIRECTED       (1 << 14) | ||||
| #define PROXY_X_FORWARDED          (1 << 15) /* Add X-Forwarded-For and X-Forwarded-Proto */ | ||||
| #define PROXY_VIA                  (1 << 16) /* Via: added to the request */ | ||||
| #define PROXY_VIA_RETURNING        (1 << 17) /* Via: added to the response */ | ||||
| #define PROXY_ALLOW_UPGRADE        (1 << 18) | ||||
| #define PROXY_UPGRADE_REQUESTED    (1 << 19) | ||||
| #define PROXY_PROTOCOL_SWITCHED    (1 << 20) | ||||
| #define PROXY_GOT_BAD_REQUEST      (1 << 21) | ||||
| #define PROXY_RESOLVE_PEER_NAME    (1u << 6) | ||||
| #define PROXY_PEER_NAME_RESOLVING  (1u << 7) | ||||
| #define PROXY_PEER_NAME_RESOLVED   (1u << 8) | ||||
| #define PROXY_PEER_NAME_UNRESOLVED (1u << 9) | ||||
| #define PROXY_REWRITE_URL          (1u << 10) | ||||
| #define PROXY_URL_REWRITING        (1u << 11) | ||||
| #define PROXY_URL_PREREWRITTEN     (1u << 12) /* URL has been prerewritten in prerewrite(). */ | ||||
| #define PROXY_URL_REWRITTEN        (1u << 13) | ||||
| #define PROXY_URL_REDIRECTED       (1u << 14) | ||||
| #define PROXY_X_FORWARDED          (1u << 15) /* Add X-Forwarded-For and X-Forwarded-Proto */ | ||||
| #define PROXY_VIA                  (1u << 16) /* Via: added to the request */ | ||||
| #define PROXY_VIA_RETURNING        (1u << 17) /* Via: added to the response */ | ||||
| #define PROXY_ALLOW_UPGRADE        (1u << 18) | ||||
| #define PROXY_UPGRADE_REQUESTED    (1u << 19) | ||||
| #define PROXY_PROTOCOL_SWITCHED    (1u << 20) | ||||
| #define PROXY_GOT_BAD_REQUEST      (1u << 21) | ||||
| 	unsigned int flags; | ||||
| 	qse_httpd_t* httpd; | ||||
| 	qse_httpd_client_t* client; | ||||
| @ -899,7 +899,13 @@ static void adjust_peer_name_and_port (task_proxy_t* proxy) | ||||
| 	else | ||||
| 	{ | ||||
| 		if (proxy->flags & PROXY_RAW) proxy->peer_port = QSE_HTTPD_DEFAULT_SECURE_PORT; | ||||
| 		else proxy->peer_port = QSE_HTTPD_DEFAULT_PORT; | ||||
| 		else  | ||||
| 		{ | ||||
| 			if (proxy->peer.flags & QSE_HTTPD_PEER_SECURE) | ||||
| 				proxy->peer_port = QSE_HTTPD_DEFAULT_SECURE_PORT; | ||||
| 			else | ||||
| 				proxy->peer_port = QSE_HTTPD_DEFAULT_PORT; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -940,6 +946,8 @@ static int task_init_proxy ( | ||||
| 	if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_X_FORWARDED) proxy->flags |= PROXY_X_FORWARDED; | ||||
| 	if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_ALLOW_UPGRADE) proxy->flags |= PROXY_ALLOW_UPGRADE; | ||||
|  | ||||
| 	if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_DST_SECURE) proxy->peer.flags |= QSE_HTTPD_PEER_SECURE; | ||||
|  | ||||
| 	proxy->peer.local = arg->rsrc->src.nwad; | ||||
| 	if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_DST_STR) | ||||
| 	{ | ||||
| @ -1414,6 +1422,7 @@ static int task_main_proxy_4 ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) | ||||
| { | ||||
| 	task_proxy_t* proxy = (task_proxy_t*)task->ctx; | ||||
| 	qse_ssize_t n; | ||||
|  | ||||
| #if 0 | ||||
| printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger.cmask=%d\n",  | ||||
| @ -1425,8 +1434,7 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger.cmask=% | ||||
| 	if ((task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) && | ||||
| 	    proxy->buflen < QSE_SIZEOF(proxy->buf)) | ||||
| 	{ | ||||
| 		qse_ssize_t n; | ||||
|  | ||||
| 	reread: | ||||
| 		/* reading from the peer */ | ||||
| 		httpd->errnum = QSE_HTTPD_ENOERR; | ||||
| 		n = httpd->opt.scb.peer.recv ( | ||||
| @ -1521,7 +1529,6 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger.cmask=% | ||||
| 		 * side is writable. it should be safe to write whenever | ||||
| 		 * this task function is called. even if it's not writable, | ||||
| 		 * it should still be ok as the client socket is non-blocking. */ | ||||
| 		qse_ssize_t n; | ||||
|  | ||||
| 		httpd->errnum = QSE_HTTPD_ENOERR; | ||||
| 		n = httpd->opt.scb.client.send (httpd, client, proxy->buf, proxy->buflen); | ||||
| @ -1542,6 +1549,24 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger.cmask=% | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (proxy->peer.flags & QSE_HTTPD_PEER_PENDING) | ||||
| 	{ | ||||
| 		/* this QSE_HTTPD_CLIENT_PENDING thing is a dirty hack for SSL. | ||||
| 		 * In SSL, data is transmitted in a record. a record can be | ||||
| 		 * as large as 16K bytes since its length field is 2 bytes. | ||||
| 		 * If SSL_read() has a record but it's given a smaller buffer | ||||
| 		 * than the actual record, the next call to select() won't return.  | ||||
| 		 * there is no data to read at the socket layer. SSL_pending() can  | ||||
| 		 * tell you the amount of data in the SSL buffer. I try to consume | ||||
| 		 * the pending data if the client.recv handler has set QSE_HTTPD_CLIENT_PENDING. */ | ||||
|  | ||||
| 		/* BUG BUG BUG. | ||||
| 		 * it jumps back to read more. If the client-side is not writable, | ||||
| 		 * unnecessary loop is made between this 'goto' and the target label.  | ||||
| 		 * HOW SHOULD I SOLVE THIS? USE A BIG BUFFER AS LARGE AS 16K? */ | ||||
| 		/*if (proxy->buflen < QSE_SIZEOF(proxy->buf))*/ goto reread; | ||||
| 	} | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| @ -2059,17 +2084,20 @@ printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN TO [%s].....\n", new_url); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				int proto_len; | ||||
|  | ||||
| 				QSE_ASSERT (QSE_STR_LEN(proxy->reqfwdbuf) > 0); | ||||
|  | ||||
| 				/* TODO: Host rewriting?? */ | ||||
| 			/* TODO: Host rewriting - to support it, headers must be made available thru request cloning.  | ||||
| 			 *                        the request may not be valid after task_init_proxy */ | ||||
|  | ||||
| 				if (qse_mbszcasecmp (new_url, QSE_MT("http://"), 7) == 0) | ||||
| 				if (qse_mbszcasecmp (new_url, QSE_MT("http://"), (proto_len = 7)) == 0 || | ||||
| 				    qse_mbszcasecmp (new_url, QSE_MT("https://"), (proto_len = 8)) == 0) | ||||
| 				{ | ||||
| 					const qse_mchar_t* host; | ||||
|  | ||||
| 					host = new_url + 7; | ||||
| 					host = new_url + proto_len; | ||||
| 					if (host[0] != QSE_MT('/') && host[0] != QSE_MT('\0')) | ||||
| 					{ | ||||
| 						const qse_mchar_t* slash; | ||||
| @ -2093,6 +2121,12 @@ printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN TO [%s].....\n", new_url); | ||||
| 							goto fail; | ||||
| 						} | ||||
|  | ||||
| /* TODO: antything todo when http is rewritten to HTTPS or vice versa */ | ||||
| 						if (proto_len == 8) | ||||
| 							proxy->peer.flags |= QSE_HTTPD_PEER_SECURE; | ||||
| 						else | ||||
| 							proxy->peer.flags &= ~QSE_HTTPD_PEER_SECURE; | ||||
|  | ||||
| 						if (qse_mbstonwad (tmp, &nwad) <= -1) | ||||
| 						{ | ||||
| 							proxy->flags |= PROXY_RESOLVE_PEER_NAME | PROXY_OUTBAND_PEER_NAME; | ||||
| @ -2104,7 +2138,7 @@ printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN TO [%s].....\n", new_url); | ||||
| 							if (qse_getnwadport(&nwad) == 0)  | ||||
| 							{ | ||||
| 								/* i don't care if tmp is X.X.X.X:0 or just X.X.X.X */ | ||||
| 								qse_setnwadport (&nwad, qse_hton16(QSE_HTTPD_DEFAULT_PORT)); | ||||
| 								qse_setnwadport (&nwad, qse_hton16(proto_len == 8? QSE_HTTPD_DEFAULT_SECURE_PORT: QSE_HTTPD_DEFAULT_PORT)); | ||||
| 							} | ||||
|  | ||||
| 							proxy->peer.nwad = nwad; | ||||
|  | ||||
| @ -577,7 +577,8 @@ typedef struct httpd_xtn_t httpd_xtn_t; | ||||
| struct httpd_xtn_t | ||||
| { | ||||
| #if defined(HAVE_SSL) | ||||
| 	SSL_CTX* ssl_ctx; | ||||
| 	SSL_CTX* ssl_client_ctx; | ||||
| 	SSL_CTX* ssl_peer_ctx; | ||||
| #endif | ||||
| 	qse_httpd_ecb_t ecb; | ||||
| 	qse_httpd_dnsstd_t dns; | ||||
| @ -587,7 +588,8 @@ struct httpd_xtn_t | ||||
| #if defined(HAVE_SSL) | ||||
| static int init_xtn_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| { | ||||
| 	SSL_CTX* ctx; | ||||
| /* BUG BUG BUG. SSL context for client must exist inside the seerver, i guess */ | ||||
| 	SSL_CTX* client_ctx = QSE_NULL; | ||||
| 	httpd_xtn_t* xtn; | ||||
| 	server_xtn_t* server_xtn; | ||||
| 	qse_httpd_serverstd_ssl_t ssl; | ||||
| @ -597,24 +599,28 @@ static int init_xtn_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
|  | ||||
| 	if (server_xtn->query (httpd, server, QSE_HTTPD_SERVERSTD_SSL, QSE_NULL, &ssl) <= -1) | ||||
| 	{ | ||||
| 		return -1; | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	if (ssl.certfile == QSE_NULL || ssl.keyfile == QSE_NULL) | ||||
| 	{ | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); | ||||
| 		return -1; | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	ctx = SSL_CTX_new (SSLv23_server_method()); | ||||
| 	if (ctx == QSE_NULL) return -1; | ||||
| 	client_ctx = SSL_CTX_new (SSLv23_server_method()); | ||||
| 	if (!client_ctx)  | ||||
| 	{ | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	/*SSL_CTX_set_info_callback(ctx,ssl_info_callback);*/ | ||||
|  | ||||
| 	if (SSL_CTX_use_certificate_file (ctx, ssl.certfile, SSL_FILETYPE_PEM) == 0 || | ||||
| 	    SSL_CTX_use_PrivateKey_file (ctx, ssl.keyfile, SSL_FILETYPE_PEM) == 0 || | ||||
| 	    SSL_CTX_check_private_key (ctx) == 0 /*|| | ||||
| 	    SSL_CTX_use_certificate_chain_file (ctx, chainfile) == 0*/) | ||||
| 	if (SSL_CTX_use_certificate_file (client_ctx, ssl.certfile, SSL_FILETYPE_PEM) == 0 || | ||||
| 	    SSL_CTX_use_PrivateKey_file (client_ctx, ssl.keyfile, SSL_FILETYPE_PEM) == 0 || | ||||
| 	    SSL_CTX_check_private_key (client_ctx) == 0 /*|| | ||||
| 	    SSL_CTX_use_certificate_chain_file (client_ctx, chainfile) == 0*/) | ||||
| 	{ | ||||
| 		if (httpd->opt.trait & QSE_HTTPD_LOGACT) | ||||
| 		{ | ||||
| @ -626,25 +632,60 @@ static int init_xtn_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) | ||||
| 			httpd->opt.rcb.logact (httpd, &msg); | ||||
| 		} | ||||
|  | ||||
| 		SSL_CTX_free (ctx); | ||||
| 		return -1; | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); /* TODO: define a better error code */ | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	/* TODO: SSL_CTX_set_verify(); SSL_CTX_set_verify_depth() */ | ||||
| 	/* TODO: CRYPTO_set_id_callback (); */ | ||||
| 	/* TODO: CRYPTO_set_locking_callback (); */ | ||||
| 	SSL_CTX_set_read_ahead (client_ctx, 0); | ||||
|  | ||||
| 	xtn->ssl_client_ctx = client_ctx; | ||||
|  | ||||
| 	SSL_CTX_set_read_ahead (ctx, 0); | ||||
| 	xtn->ssl_ctx = ctx; | ||||
| 	return 0; | ||||
|  | ||||
| oops: | ||||
| 	if (client_ctx) SSL_CTX_free (client_ctx); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static void fini_xtn_ssl (httpd_xtn_t* xtn) | ||||
| { | ||||
| 	/* TODO: CRYPTO_set_id_callback (QSE_NULL); */ | ||||
| 	/* TODO: CRYPTO_set_locking_callback (QSE_NULL); */ | ||||
| 	SSL_CTX_free (xtn->ssl_ctx); | ||||
| 	SSL_CTX_free (xtn->ssl_client_ctx); | ||||
| } | ||||
|  | ||||
| static int init_xtn_peer_ssl (qse_httpd_t* httpd) | ||||
| { | ||||
| 	SSL_CTX* peer_ctx = QSE_NULL; | ||||
| 	httpd_xtn_t* xtn; | ||||
|  | ||||
| 	xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd); | ||||
|  | ||||
| 	peer_ctx = SSL_CTX_new (SSLv23_client_method()); | ||||
| 	if (!peer_ctx)  | ||||
| 	{ | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM); | ||||
| 		goto oops; | ||||
| 	} | ||||
|  | ||||
| 	xtn->ssl_peer_ctx = peer_ctx; | ||||
|  | ||||
| printf ("SSL PEER CTX ============>%p\n", xtn->ssl_peer_ctx); | ||||
| 	return 0; | ||||
|  | ||||
| oops: | ||||
| 	if (peer_ctx) SSL_CTX_free (peer_ctx); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static void fini_xtn_peer_ssl (httpd_xtn_t* xtn) | ||||
| { | ||||
| 	/* TODO: CRYPTO_set_id_callback (QSE_NULL); */ | ||||
| 	/* TODO: CRYPTO_set_locking_callback (QSE_NULL); */ | ||||
| 	SSL_CTX_free (xtn->ssl_peer_ctx); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -656,7 +697,8 @@ static void cleanup_standard_httpd (qse_httpd_t* httpd) | ||||
| 	xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd); | ||||
|  | ||||
| #if defined(HAVE_SSL) | ||||
| 	if (xtn->ssl_ctx) fini_xtn_ssl (xtn); | ||||
| 	if (xtn->ssl_peer_ctx) fini_xtn_peer_ssl (xtn); | ||||
| 	if (xtn->ssl_client_ctx) fini_xtn_ssl (xtn); | ||||
| #endif | ||||
|  | ||||
| #if defined(USE_LTDL) | ||||
| @ -1069,6 +1111,10 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| 	int connaddrsize, bindaddrsize; | ||||
| 	int connected = 1; | ||||
| 	qse_sck_hnd_t fd = QSE_INVALID_SCKHND; | ||||
| 	SSL* ssl = QSE_NULL; | ||||
| 	httpd_xtn_t* xtn; | ||||
|  | ||||
| 	xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd); | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| 	unsigned long cmd; | ||||
| @ -1080,6 +1126,9 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| 	int flag; | ||||
| #endif | ||||
| 	 | ||||
| 	/* turn off internally used bits */ | ||||
| 	peer->flags &= ~QSE_HTTPD_PEER_ALL_INTERNALS; | ||||
|  | ||||
| 	connaddrsize = qse_nwadtoskad (&peer->nwad, &connaddr); | ||||
| 	if (connaddrsize <= -1) | ||||
| 	{ | ||||
| @ -1117,6 +1166,33 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
|  | ||||
| 	if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; | ||||
|  | ||||
| 	if (peer->flags & QSE_HTTPD_PEER_SECURE) | ||||
| 	{ | ||||
| 	#if defined(HAVE_SSL) | ||||
| 		if (!xtn->ssl_peer_ctx) | ||||
| 		{ | ||||
| 			/* TODO: peer ssl initialization doesn't have to be delayed... */ | ||||
| 			if (init_xtn_peer_ssl (httpd) <= -1) goto oops; | ||||
| 		} | ||||
|  | ||||
| 		ssl = SSL_new (xtn->ssl_peer_ctx); | ||||
| 		if (ssl == QSE_NULL)  | ||||
| 		{ | ||||
| 			qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR); /* TODO: better error code */ | ||||
| 			goto oops; | ||||
| 		} | ||||
|  | ||||
| 		if (SSL_set_fd (ssl, fd) == 0)  | ||||
| 		{ | ||||
| 			qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR); /* TODO: better error code */ | ||||
| 			goto oops; | ||||
| 		} | ||||
| 	#else | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| 		goto oops; | ||||
| 	#endif | ||||
| 	} | ||||
|  | ||||
| #if defined(_WIN32) | ||||
| 	if (connect (fd, (struct sockaddr*)&connaddr, connaddrsize) <= -1) | ||||
| 	{ | ||||
| @ -1154,13 +1230,48 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	/*if (set_socket_nonblock (httpd, fd, 0) <= -1) goto oops;*/ | ||||
| 	if ((peer->flags & QSE_HTTPD_PEER_SECURE) && connected) | ||||
| 	{ | ||||
| 	#if defined(HAVE_SSL) | ||||
| 		int ret = SSL_connect (ssl); | ||||
| 		if (ret <= 0) | ||||
| 		{ | ||||
| 			int err = SSL_get_error(ssl, ret); | ||||
| 			if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) | ||||
| 			{ | ||||
| 				/* handshaking isn't complete. */ | ||||
| 				peer->flags |= QSE_HTTPD_PEER_CONNECTED; | ||||
| 				connected = 0; /* not fully connected yet */ | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				qse_httpd_seterrnum (httpd, QSE_HTTPD_ESCONN); | ||||
| 				goto oops; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			peer->flags |= QSE_HTTPD_PEER_CONNECTED; | ||||
| 			/* socket connected + ssl connected */ | ||||
| 		} | ||||
| 	#endif | ||||
| 	} | ||||
|  | ||||
| 	/* take note the socket handle is in the non-blocking mode here */ | ||||
| 	peer->handle = fd; | ||||
| 	if (peer->flags & QSE_HTTPD_PEER_SECURE) | ||||
| 	{ | ||||
| 	#if defined(HAVE_SSL) | ||||
| 		peer->handle2 = SSL_TO_HANDLE(ssl); | ||||
| 	#endif | ||||
| 	} | ||||
| 	return connected; | ||||
|  | ||||
| oops: | ||||
| 	qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); | ||||
| #if defined(HAVE_SSL) | ||||
| 	if (ssl) SSL_free (ssl); | ||||
| #endif | ||||
| 	if (qse_isvalidsckhnd(fd)) qse_closesckhnd (fd); | ||||
| 	return -1; | ||||
|  | ||||
| @ -1170,10 +1281,16 @@ oops: | ||||
|  | ||||
| static void peer_close (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| { | ||||
| 	if (peer->flags & QSE_HTTPD_PEER_SECURE) | ||||
| 	{ | ||||
| 	#if defined(HAVE_SSL) | ||||
| 		SSL_free (HANDLE_TO_SSL(peer->handle2)); | ||||
| 	#endif | ||||
| 	} | ||||
| 	qse_closesckhnd (peer->handle); | ||||
| } | ||||
|  | ||||
| static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| static int is_peer_socket_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
| 	int len; | ||||
| @ -1245,32 +1362,122 @@ static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int is_peer_connected_securely (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| { | ||||
| 	int ret = SSL_connect (HANDLE_TO_SSL(peer->handle2)); | ||||
| 	if (ret <= 0) | ||||
| 	{ | ||||
| 		int err = SSL_get_error(HANDLE_TO_SSL(peer->handle2), ret); | ||||
| 		if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) | ||||
| 		{ | ||||
| 			/* handshaking isn't complete. */ | ||||
| 			return 0; /* not connected */ | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			qse_httpd_seterrnum (httpd, QSE_HTTPD_ESCONN); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer) | ||||
| { | ||||
| 	if (peer->flags & QSE_HTTPD_PEER_SECURE) | ||||
| 	{ | ||||
| 		if (peer->flags & QSE_HTTPD_PEER_CONNECTED) | ||||
| 		{ | ||||
| 			return is_peer_connected_securely (httpd, peer); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			int ret = is_peer_socket_connected (httpd, peer); | ||||
| 			if (ret <= 0) return ret; | ||||
| 			peer->flags |= QSE_HTTPD_PEER_CONNECTED; | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return is_peer_socket_connected (httpd, peer); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static qse_ssize_t peer_recv ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_peer_t* peer, | ||||
| 	qse_mchar_t* buf, qse_size_t bufsize) | ||||
| { | ||||
| #if defined(__DOS__) | ||||
| 	qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| 	return -1; | ||||
| #else | ||||
| 	qse_ssize_t ret = recv (peer->handle, buf, bufsize, 0); | ||||
| 	if (ret <= -1) qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); | ||||
| 	return ret; | ||||
| #endif | ||||
| 	if (peer->flags & QSE_HTTPD_PEER_SECURE) | ||||
| 	{ | ||||
| 	#if defined(HAVE_SSL) | ||||
| 		int ret = SSL_read (HANDLE_TO_SSL(peer->handle2), buf, bufsize); | ||||
| 		if (ret <= -1) | ||||
| 		{ | ||||
| 			int err = SSL_get_error(HANDLE_TO_SSL(peer->handle2),ret); | ||||
| 			if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) | ||||
| 				qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN); | ||||
| 			else | ||||
| 				qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR); | ||||
| 		} | ||||
|  | ||||
| 		if (SSL_pending (HANDLE_TO_SSL(peer->handle2)) > 0)  | ||||
| 			peer->flags |= QSE_HTTPD_PEER_PENDING; | ||||
| 		else | ||||
| 			peer->flags &= ~QSE_HTTPD_PEER_PENDING; | ||||
|  | ||||
| 		return ret; | ||||
| 	#else | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| 		return -1; | ||||
| 	#endif | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 	#if defined(__DOS__) | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| 		return -1; | ||||
| 	#else | ||||
| 		qse_ssize_t ret = recv (peer->handle, buf, bufsize, 0); | ||||
| 		if (ret <= -1) qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); | ||||
| 		return ret; | ||||
| 	#endif | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static qse_ssize_t peer_send ( | ||||
| 	qse_httpd_t* httpd, qse_httpd_peer_t* peer, | ||||
| 	const qse_mchar_t* buf, qse_size_t bufsize) | ||||
| { | ||||
| #if defined(__DOS__) | ||||
| 	qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| 	return -1; | ||||
| #else | ||||
| 	qse_ssize_t ret = send (peer->handle, buf, bufsize, 0); | ||||
| 	if (ret <= -1) qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); | ||||
| 	return ret; | ||||
| #endif | ||||
| 	if (peer->flags & QSE_HTTPD_PEER_SECURE) | ||||
| 	{ | ||||
| 	#if defined(HAVE_SSL) | ||||
| 		int ret = SSL_write (HANDLE_TO_SSL(peer->handle2), buf, bufsize); | ||||
| 		if (ret <= -1) | ||||
| 		{ | ||||
| 			int err = SSL_get_error(HANDLE_TO_SSL(peer->handle2),ret); | ||||
| 			if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) | ||||
| 				qse_httpd_seterrnum (httpd, QSE_HTTPD_EAGAIN); | ||||
| 			else | ||||
| 				qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR); | ||||
| 		} | ||||
| 		return ret; | ||||
| 	#else | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| 		return -1; | ||||
| 	#endif | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 	#if defined(__DOS__) | ||||
| 		qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); | ||||
| 		return -1; | ||||
| 	#else | ||||
| 		qse_ssize_t ret = send (peer->handle, buf, bufsize, 0); | ||||
| 		if (ret <= -1) qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); | ||||
| 		return ret; | ||||
| 	#endif | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* ------------------------------------------------------------------- */ | ||||
| @ -2008,7 +2215,7 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client) | ||||
| 		httpd_xtn_t* xtn; | ||||
|  | ||||
| 		xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd); | ||||
| 		if (!xtn->ssl_ctx) | ||||
| 		if (!xtn->ssl_client_ctx) | ||||
| 		{ | ||||
| 			/* delayed initialization of ssl */ | ||||
| 			if (init_xtn_ssl (httpd, client->server) <= -1)  | ||||
| @ -2017,7 +2224,7 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		QSE_ASSERT (xtn->ssl_ctx != QSE_NULL); | ||||
| 		QSE_ASSERT (xtn->ssl_client_ctx != QSE_NULL); | ||||
| 		QSE_ASSERT (QSE_SIZEOF(client->handle2) >= QSE_SIZEOF(ssl)); | ||||
|  | ||||
| 		if (HANDLE_TO_SSL(client->handle2)) | ||||
| @ -2026,7 +2233,7 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client) | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			ssl = SSL_new (xtn->ssl_ctx); | ||||
| 			ssl = SSL_new (xtn->ssl_client_ctx); | ||||
| 			if (ssl == QSE_NULL) return -1; | ||||
|  | ||||
| 			client->handle2 = SSL_TO_HANDLE(ssl); | ||||
| @ -2058,7 +2265,8 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client) | ||||
| 				httpd->opt.rcb.logact (httpd, &msg); | ||||
| 			} | ||||
|  | ||||
| 			/* SSL_free (ssl); */ | ||||
| 			/* client_closed() free this. no SSL_free() here. | ||||
| 			SSL_free (ssl); */ | ||||
| 			return -1; | ||||
| 		} | ||||
|  | ||||
|  | ||||
| @ -1103,12 +1103,11 @@ qse_printf (QSE_T("!!!!!FEEDING OK OK OK OK %d from %d\n"), (int)m, (int)client- | ||||
| 		/* this QSE_HTTPD_CLIENT_PENDING thing is a dirty hack for SSL. | ||||
| 		 * In SSL, data is transmitted in a record. a record can be | ||||
| 		 * as large as 16K bytes since its length field is 2 bytes. | ||||
| 		 * If SSL_read() has record a record but it's given a | ||||
| 		 * smaller buffer than the actuaal record, the next call | ||||
| 		 * to select() won't return. there is no data to read | ||||
| 		 * at the socket layer. SSL_pending() can tell you the | ||||
| 		 * amount of data in the SSL buffer. I try to consume | ||||
| 		 * the pending data if the client.recv handler set QSE_HTTPD_CLIENT_PENDING. | ||||
| 		 * If SSL_read() has a record but it's given a smaller buffer | ||||
| 		 * than the actual record, the next call to select() won't return.  | ||||
| 		 * there is no data to read at the socket layer. SSL_pending() can  | ||||
| 		 * tell you the amount of data in the SSL buffer. I try to consume | ||||
| 		 * the pending data if the client.recv handler has set QSE_HTTPD_CLIENT_PENDING. | ||||
| 		 * | ||||
| 		 * TODO: Investigate if there is any starvation issues. | ||||
| 		 *       What if a single client never stops sending?  | ||||
|  | ||||
| @ -100,8 +100,8 @@ struct qse_httpd_real_task_t | ||||
| 	qse_httpd_real_task_t* next; | ||||
| }; | ||||
|  | ||||
| #define MAX_SEND_SIZE 4096 | ||||
| #define MAX_RECV_SIZE 4096 | ||||
| #define MAX_SEND_SIZE (4096 * 4) | ||||
| #define MAX_RECV_SIZE (4096 * 2) | ||||
|  | ||||
| #define MAX_NWAD_TEXT_SIZE 96 | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user