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:
hyung-hwan 2014-10-15 15:33:37 +00:00
parent ca272bb55e
commit c9d23a0d8b
6 changed files with 358 additions and 88 deletions

View File

@ -199,6 +199,7 @@ struct loccfg_t
struct struct
{ {
unsigned int allow_http: 1; unsigned int allow_http: 1;
unsigned int allow_https: 1;
unsigned int allow_connect: 1; unsigned int allow_connect: 1;
unsigned int allow_intercept: 2; /* 0: no, 1: proxy, 2: local */ unsigned int allow_intercept: 2; /* 0: no, 1: proxy, 2: local */
unsigned int allow_upgrade: 1; unsigned int allow_upgrade: 1;
@ -468,6 +469,7 @@ static int get_server_root (
{ {
qse_http_method_t mth; qse_http_method_t mth;
qse_mchar_t* qpath; qse_mchar_t* qpath;
int proto_len;
qse_memset (root, 0, QSE_SIZEOF(*root)); qse_memset (root, 0, QSE_SIZEOF(*root));
mth = qse_htre_getqmethodtype (qinfo->req); 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://"), (proto_len = 7)) == 0) ||
if (loccfg->proxy.allow_http && (loccfg->proxy.allow_https && qse_mbszcasecmp (qpath, QSE_MT("https://"), (proto_len = 8)) == 0))
qse_mbszcasecmp (qpath, QSE_MT("http://"), 7) == 0)
{ {
qse_mchar_t* host, * slash; qse_mchar_t* host, * slash;
host = qpath + 7; host = qpath + proto_len;
slash = qse_mbschr (host, QSE_MT('/')); slash = qse_mbschr (host, QSE_MT('/'));
if (slash && slash - host > 0) if (slash && slash - host > 0)
@ -561,6 +562,7 @@ static int get_server_root (
host = host - 1; host = host - 1;
root->u.proxy.host = host; 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) if (qse_mbstonwad (host, &root->u.proxy.dst.nwad) <= -1)
{ {
root->u.proxy.flags |= QSE_HTTPD_RSRC_PROXY_DST_STR; 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 */ /* TODO: refrain from manipulating the request like this */
qinfo->req->u.q.path = slash; /* TODO: use setqpath or something... */ qinfo->req->u.q.path = slash; /* TODO: use setqpath or something... */
goto proxy_ok; goto proxy_ok;
} }
else 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 && 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); 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; pair = QSE_NULL;
if (proxy) pair = qse_xli_findpair (xli, proxy, QSE_T("connect")); 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")); 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.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"), { 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.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.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.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 } }, { 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.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"), { 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.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.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.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 } }, { QSE_T("server.host.location.proxy.upgrade"), { QSE_XLI_SCM_VALSTR | QSE_XLI_SCM_KEYNODUP, 1, 1 } },

View File

@ -58,7 +58,8 @@ enum qse_httpd_errnum_t
QSE_HTTPD_EAGAIN, QSE_HTTPD_EAGAIN,
QSE_HTTPD_ENOSVR, /* no active servers */ 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_ENOBUF, /* no buffer available */
QSE_HTTPD_EDISCON, /* client disconnnected */ QSE_HTTPD_EDISCON, /* client disconnnected */
QSE_HTTPD_EBADREQ, /* bad request */ QSE_HTTPD_EBADREQ, /* bad request */
@ -146,12 +147,31 @@ struct qse_httpd_stat_t
qse_ntime_t mtime; 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; typedef struct qse_httpd_peer_t qse_httpd_peer_t;
struct 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 nwad;
qse_nwad_t local; /* local side address facing the peer */ 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 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 struct qse_httpd_task_trigger_t
{ {
int flags; /**< [IN] bitwise-ORed of #qse_httpd_task_trigger_flag_t enumerators*/ 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 struct
{ {
int mask; /* QSE_HTTPD_TASK_TRIGGER_READ | QSE_HTTPD_TASK_TRIGGER_WRITE */ 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_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_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_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_DST_SECURE = (1 << 5), /* use secure connection to destination */
QSE_HTTPD_RSRC_PROXY_ENABLE_URS = (1 << 6), /* url rewriting enabled (udp) */ QSE_HTTPD_RSRC_PROXY_ENABLE_DNS = (1 << 6), /* dns service enabled (udp) */
QSE_HTTPD_RSRC_PROXY_DNS_SERVER = (1 << 7), /* dns address specified */ QSE_HTTPD_RSRC_PROXY_ENABLE_URS = (1 << 7), /* url rewriting enabled (udp) */
QSE_HTTPD_RSRC_PROXY_URS_SERVER = (1 << 8), /* urs address specified */ 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; typedef enum qse_httpd_rsrc_proxy_flag_t qse_httpd_rsrc_proxy_flag_t;

View File

@ -35,31 +35,31 @@ struct task_proxy_arg_t
typedef struct task_proxy_t task_proxy_t; typedef struct task_proxy_t task_proxy_t;
struct task_proxy_t struct task_proxy_t
{ {
#define PROXY_INIT_FAILED (1 << 0) #define PROXY_INIT_FAILED (1u << 0)
#define PROXY_RAW (1 << 1) #define PROXY_RAW (1u << 1)
#define PROXY_TRANSPARENT (1 << 2) #define PROXY_TRANSPARENT (1u << 2)
#define PROXY_DNS_SERVER (1 << 3) /* dns server address specified */ #define PROXY_DNS_SERVER (1u << 3) /* dns server address specified */
#define PROXY_URS_SERVER (1 << 4) /* urs server address specified */ #define PROXY_URS_SERVER (1u << 4) /* urs server address specified */
#define PROXY_OUTBAND_PEER_NAME (1 << 5) /* the peer_name pointer points to #define PROXY_OUTBAND_PEER_NAME (1u << 5) /* the peer_name pointer points to
a separate memory chunk outside a separate memory chunk outside
the task_proxy_t chunk. explicit the task_proxy_t chunk. explicit
deallocatin is required */ deallocatin is required */
#define PROXY_RESOLVE_PEER_NAME (1 << 6) #define PROXY_RESOLVE_PEER_NAME (1u << 6)
#define PROXY_PEER_NAME_RESOLVING (1 << 7) #define PROXY_PEER_NAME_RESOLVING (1u << 7)
#define PROXY_PEER_NAME_RESOLVED (1 << 8) #define PROXY_PEER_NAME_RESOLVED (1u << 8)
#define PROXY_PEER_NAME_UNRESOLVED (1 << 9) #define PROXY_PEER_NAME_UNRESOLVED (1u << 9)
#define PROXY_REWRITE_URL (1 << 10) #define PROXY_REWRITE_URL (1u << 10)
#define PROXY_URL_REWRITING (1 << 11) #define PROXY_URL_REWRITING (1u << 11)
#define PROXY_URL_PREREWRITTEN (1 << 12) /* URL has been prerewritten in prerewrite(). */ #define PROXY_URL_PREREWRITTEN (1u << 12) /* URL has been prerewritten in prerewrite(). */
#define PROXY_URL_REWRITTEN (1 << 13) #define PROXY_URL_REWRITTEN (1u << 13)
#define PROXY_URL_REDIRECTED (1 << 14) #define PROXY_URL_REDIRECTED (1u << 14)
#define PROXY_X_FORWARDED (1 << 15) /* Add X-Forwarded-For and X-Forwarded-Proto */ #define PROXY_X_FORWARDED (1u << 15) /* Add X-Forwarded-For and X-Forwarded-Proto */
#define PROXY_VIA (1 << 16) /* Via: added to the request */ #define PROXY_VIA (1u << 16) /* Via: added to the request */
#define PROXY_VIA_RETURNING (1 << 17) /* Via: added to the response */ #define PROXY_VIA_RETURNING (1u << 17) /* Via: added to the response */
#define PROXY_ALLOW_UPGRADE (1 << 18) #define PROXY_ALLOW_UPGRADE (1u << 18)
#define PROXY_UPGRADE_REQUESTED (1 << 19) #define PROXY_UPGRADE_REQUESTED (1u << 19)
#define PROXY_PROTOCOL_SWITCHED (1 << 20) #define PROXY_PROTOCOL_SWITCHED (1u << 20)
#define PROXY_GOT_BAD_REQUEST (1 << 21) #define PROXY_GOT_BAD_REQUEST (1u << 21)
unsigned int flags; unsigned int flags;
qse_httpd_t* httpd; qse_httpd_t* httpd;
qse_httpd_client_t* client; qse_httpd_client_t* client;
@ -899,7 +899,13 @@ static void adjust_peer_name_and_port (task_proxy_t* proxy)
else else
{ {
if (proxy->flags & PROXY_RAW) proxy->peer_port = QSE_HTTPD_DEFAULT_SECURE_PORT; 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_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_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; proxy->peer.local = arg->rsrc->src.nwad;
if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_DST_STR) 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) qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{ {
task_proxy_t* proxy = (task_proxy_t*)task->ctx; task_proxy_t* proxy = (task_proxy_t*)task->ctx;
qse_ssize_t n;
#if 0 #if 0
printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger.cmask=%d\n", 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) && if ((task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) &&
proxy->buflen < QSE_SIZEOF(proxy->buf)) proxy->buflen < QSE_SIZEOF(proxy->buf))
{ {
qse_ssize_t n; reread:
/* reading from the peer */ /* reading from the peer */
httpd->errnum = QSE_HTTPD_ENOERR; httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.peer.recv ( 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 * side is writable. it should be safe to write whenever
* this task function is called. even if it's not writable, * this task function is called. even if it's not writable,
* it should still be ok as the client socket is non-blocking. */ * it should still be ok as the client socket is non-blocking. */
qse_ssize_t n;
httpd->errnum = QSE_HTTPD_ENOERR; httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.client.send (httpd, client, proxy->buf, proxy->buflen); 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; return 1;
} }
@ -2059,17 +2084,20 @@ printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN TO [%s].....\n", new_url);
} }
else else
{ {
int proto_len;
QSE_ASSERT (QSE_STR_LEN(proxy->reqfwdbuf) > 0); QSE_ASSERT (QSE_STR_LEN(proxy->reqfwdbuf) > 0);
/* TODO: Host rewriting?? */ /* TODO: Host rewriting?? */
/* TODO: Host rewriting - to support it, headers must be made available thru request cloning. /* TODO: Host rewriting - to support it, headers must be made available thru request cloning.
* the request may not be valid after task_init_proxy */ * 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; const qse_mchar_t* host;
host = new_url + 7; host = new_url + proto_len;
if (host[0] != QSE_MT('/') && host[0] != QSE_MT('\0')) if (host[0] != QSE_MT('/') && host[0] != QSE_MT('\0'))
{ {
const qse_mchar_t* slash; const qse_mchar_t* slash;
@ -2093,6 +2121,12 @@ printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN TO [%s].....\n", new_url);
goto fail; 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) if (qse_mbstonwad (tmp, &nwad) <= -1)
{ {
proxy->flags |= PROXY_RESOLVE_PEER_NAME | PROXY_OUTBAND_PEER_NAME; 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) if (qse_getnwadport(&nwad) == 0)
{ {
/* i don't care if tmp is X.X.X.X:0 or just X.X.X.X */ /* 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; proxy->peer.nwad = nwad;

View File

@ -577,7 +577,8 @@ typedef struct httpd_xtn_t httpd_xtn_t;
struct httpd_xtn_t struct httpd_xtn_t
{ {
#if defined(HAVE_SSL) #if defined(HAVE_SSL)
SSL_CTX* ssl_ctx; SSL_CTX* ssl_client_ctx;
SSL_CTX* ssl_peer_ctx;
#endif #endif
qse_httpd_ecb_t ecb; qse_httpd_ecb_t ecb;
qse_httpd_dnsstd_t dns; qse_httpd_dnsstd_t dns;
@ -587,7 +588,8 @@ struct httpd_xtn_t
#if defined(HAVE_SSL) #if defined(HAVE_SSL)
static int init_xtn_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) 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; httpd_xtn_t* xtn;
server_xtn_t* server_xtn; server_xtn_t* server_xtn;
qse_httpd_serverstd_ssl_t ssl; 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) 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) if (ssl.certfile == QSE_NULL || ssl.keyfile == QSE_NULL)
{ {
qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL);
return -1; goto oops;
} }
ctx = SSL_CTX_new (SSLv23_server_method()); client_ctx = SSL_CTX_new (SSLv23_server_method());
if (ctx == QSE_NULL) return -1; if (!client_ctx)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
goto oops;
}
/*SSL_CTX_set_info_callback(ctx,ssl_info_callback);*/ /*SSL_CTX_set_info_callback(ctx,ssl_info_callback);*/
if (SSL_CTX_use_certificate_file (ctx, ssl.certfile, SSL_FILETYPE_PEM) == 0 || if (SSL_CTX_use_certificate_file (client_ctx, ssl.certfile, SSL_FILETYPE_PEM) == 0 ||
SSL_CTX_use_PrivateKey_file (ctx, ssl.keyfile, SSL_FILETYPE_PEM) == 0 || SSL_CTX_use_PrivateKey_file (client_ctx, ssl.keyfile, SSL_FILETYPE_PEM) == 0 ||
SSL_CTX_check_private_key (ctx) == 0 /*|| SSL_CTX_check_private_key (client_ctx) == 0 /*||
SSL_CTX_use_certificate_chain_file (ctx, chainfile) == 0*/) SSL_CTX_use_certificate_chain_file (client_ctx, chainfile) == 0*/)
{ {
if (httpd->opt.trait & QSE_HTTPD_LOGACT) 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); httpd->opt.rcb.logact (httpd, &msg);
} }
SSL_CTX_free (ctx); qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); /* TODO: define a better error code */
return -1; goto oops;
} }
/* TODO: SSL_CTX_set_verify(); SSL_CTX_set_verify_depth() */ /* TODO: SSL_CTX_set_verify(); SSL_CTX_set_verify_depth() */
/* TODO: CRYPTO_set_id_callback (); */ /* TODO: CRYPTO_set_id_callback (); */
/* TODO: CRYPTO_set_locking_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; return 0;
oops:
if (client_ctx) SSL_CTX_free (client_ctx);
return -1;
} }
static void fini_xtn_ssl (httpd_xtn_t* xtn) static void fini_xtn_ssl (httpd_xtn_t* xtn)
{ {
/* TODO: CRYPTO_set_id_callback (QSE_NULL); */ /* TODO: CRYPTO_set_id_callback (QSE_NULL); */
/* TODO: CRYPTO_set_locking_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 #endif
@ -656,7 +697,8 @@ static void cleanup_standard_httpd (qse_httpd_t* httpd)
xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd); xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd);
#if defined(HAVE_SSL) #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 #endif
#if defined(USE_LTDL) #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 connaddrsize, bindaddrsize;
int connected = 1; int connected = 1;
qse_sck_hnd_t fd = QSE_INVALID_SCKHND; 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) #if defined(_WIN32)
unsigned long cmd; unsigned long cmd;
@ -1079,6 +1125,9 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
#else #else
int flag; int flag;
#endif #endif
/* turn off internally used bits */
peer->flags &= ~QSE_HTTPD_PEER_ALL_INTERNALS;
connaddrsize = qse_nwadtoskad (&peer->nwad, &connaddr); connaddrsize = qse_nwadtoskad (&peer->nwad, &connaddr);
if (connaddrsize <= -1) 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 (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 defined(_WIN32)
if (connect (fd, (struct sockaddr*)&connaddr, connaddrsize) <= -1) 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 #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; peer->handle = fd;
if (peer->flags & QSE_HTTPD_PEER_SECURE)
{
#if defined(HAVE_SSL)
peer->handle2 = SSL_TO_HANDLE(ssl);
#endif
}
return connected; return connected;
oops: oops:
qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM());
#if defined(HAVE_SSL)
if (ssl) SSL_free (ssl);
#endif
if (qse_isvalidsckhnd(fd)) qse_closesckhnd (fd); if (qse_isvalidsckhnd(fd)) qse_closesckhnd (fd);
return -1; return -1;
@ -1170,10 +1281,16 @@ oops:
static void peer_close (qse_httpd_t* httpd, qse_httpd_peer_t* peer) 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); 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) #if defined(_WIN32)
int len; int len;
@ -1245,32 +1362,122 @@ static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
#endif #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 ( static qse_ssize_t peer_recv (
qse_httpd_t* httpd, qse_httpd_peer_t* peer, qse_httpd_t* httpd, qse_httpd_peer_t* peer,
qse_mchar_t* buf, qse_size_t bufsize) qse_mchar_t* buf, qse_size_t bufsize)
{ {
#if defined(__DOS__) if (peer->flags & QSE_HTTPD_PEER_SECURE)
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); {
return -1; #if defined(HAVE_SSL)
#else int ret = SSL_read (HANDLE_TO_SSL(peer->handle2), buf, bufsize);
qse_ssize_t ret = recv (peer->handle, buf, bufsize, 0); if (ret <= -1)
if (ret <= -1) qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); {
return ret; int err = SSL_get_error(HANDLE_TO_SSL(peer->handle2),ret);
#endif 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 ( static qse_ssize_t peer_send (
qse_httpd_t* httpd, qse_httpd_peer_t* peer, qse_httpd_t* httpd, qse_httpd_peer_t* peer,
const qse_mchar_t* buf, qse_size_t bufsize) const qse_mchar_t* buf, qse_size_t bufsize)
{ {
#if defined(__DOS__) if (peer->flags & QSE_HTTPD_PEER_SECURE)
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); {
return -1; #if defined(HAVE_SSL)
#else int ret = SSL_write (HANDLE_TO_SSL(peer->handle2), buf, bufsize);
qse_ssize_t ret = send (peer->handle, buf, bufsize, 0); if (ret <= -1)
if (ret <= -1) qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); {
return ret; int err = SSL_get_error(HANDLE_TO_SSL(peer->handle2),ret);
#endif 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; httpd_xtn_t* xtn;
xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd); xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd);
if (!xtn->ssl_ctx) if (!xtn->ssl_client_ctx)
{ {
/* delayed initialization of ssl */ /* delayed initialization of ssl */
if (init_xtn_ssl (httpd, client->server) <= -1) 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)); QSE_ASSERT (QSE_SIZEOF(client->handle2) >= QSE_SIZEOF(ssl));
if (HANDLE_TO_SSL(client->handle2)) if (HANDLE_TO_SSL(client->handle2))
@ -2026,7 +2233,7 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client)
} }
else else
{ {
ssl = SSL_new (xtn->ssl_ctx); ssl = SSL_new (xtn->ssl_client_ctx);
if (ssl == QSE_NULL) return -1; if (ssl == QSE_NULL) return -1;
client->handle2 = SSL_TO_HANDLE(ssl); 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); httpd->opt.rcb.logact (httpd, &msg);
} }
/* SSL_free (ssl); */ /* client_closed() free this. no SSL_free() here.
SSL_free (ssl); */
return -1; return -1;
} }

View File

@ -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. /* this QSE_HTTPD_CLIENT_PENDING thing is a dirty hack for SSL.
* In SSL, data is transmitted in a record. a record can be * In SSL, data is transmitted in a record. a record can be
* as large as 16K bytes since its length field is 2 bytes. * as large as 16K bytes since its length field is 2 bytes.
* If SSL_read() has record a record but it's given a * If SSL_read() has a record but it's given a smaller buffer
* smaller buffer than the actuaal record, the next call * than the actual record, the next call to select() won't return.
* to select() won't return. there is no data to read * there is no data to read at the socket layer. SSL_pending() can
* at the socket layer. SSL_pending() can tell you the * tell you the amount of data in the SSL buffer. I try to consume
* 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.
* the pending data if the client.recv handler set QSE_HTTPD_CLIENT_PENDING.
* *
* TODO: Investigate if there is any starvation issues. * TODO: Investigate if there is any starvation issues.
* What if a single client never stops sending? * What if a single client never stops sending?

View File

@ -100,8 +100,8 @@ struct qse_httpd_real_task_t
qse_httpd_real_task_t* next; qse_httpd_real_task_t* next;
}; };
#define MAX_SEND_SIZE 4096 #define MAX_SEND_SIZE (4096 * 4)
#define MAX_RECV_SIZE 4096 #define MAX_RECV_SIZE (4096 * 2)
#define MAX_NWAD_TEXT_SIZE 96 #define MAX_NWAD_TEXT_SIZE 96