diff --git a/qse/cmd/http/httpd.c b/qse/cmd/http/httpd.c index d44de8a2..cbb565e3 100644 --- a/qse/cmd/http/httpd.c +++ b/qse/cmd/http/httpd.c @@ -156,6 +156,7 @@ struct loccfg_t { ROOT_TYPE_PATH = 0, ROOT_TYPE_NWAD, + ROOT_TYPE_NWAD_SECURE, ROOT_TYPE_RELOC, ROOT_TYPE_ERROR } root_type; @@ -234,9 +235,9 @@ struct server_xtn_t { int nodir; /* no directory listing */ - int num; - qse_nwad_t bind; - int secure; + int num; /* the server number in the xli configuration */ + qse_nwad_t bind; /* binding address */ + int secure; /* ssl */ qse_mchar_t* scfg[SCFG_MAX]; qse_httpd_serverstd_makersrc_t orgmakersrc; @@ -590,8 +591,11 @@ static int get_server_root ( switch (loccfg->root_type) { + case ROOT_TYPE_NWAD_SECURE: + root->u.proxy.flags |= QSE_HTTPD_RSRC_PROXY_DST_SECURE; + /* fall thru */ case ROOT_TYPE_NWAD: - /* simple forwarding. it's not controlled by proxy.http or proxy.connect */ + /* simple forwarding. it's not controlled by proxy.http, proxy.https or proxy.connect */ root->type = QSE_HTTPD_SERVERSTD_ROOT_PROXY; root->u.proxy.dst.nwad = loccfg->root.nwad; @@ -1679,6 +1683,7 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_t* xli, qse_xli_list_t* list { /* check if the root value is special */ const qse_mchar_t* root = cfg->xcfg[XCFG_ROOT]; + int proto_len = 0; if (root[0] == QSE_MT('<') && QSE_ISMDIGIT(root[1])) { @@ -1722,16 +1727,31 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_t* xli, qse_xli_list_t* list goto done; } } + + root = cfg->xcfg[XCFG_ROOT]; + } + else + { + if (qse_mbszcasecmp (root, QSE_MT("http://"), (proto_len = 7)) == 0 || + qse_mbszcasecmp (root, QSE_MT("https://"), (proto_len = 8)) == 0) + { + root += proto_len; + } + else + { + proto_len = 0; + } } - if (qse_mbstonwad (cfg->xcfg[XCFG_ROOT], &cfg->root.nwad) >= 0) + if (qse_mbstonwad (root, &cfg->root.nwad) >= 0) { if (cfg->root.nwad.type != QSE_NWAD_IN4 && cfg->root.nwad.type != QSE_NWAD_IN6) { qse_printf (QSE_T("ERROR: invalid address for root - [%hs]\n"), cfg->xcfg[XCFG_ROOT]); return -1; } - cfg->root_type = ROOT_TYPE_NWAD; + + cfg->root_type = (proto_len == 8)? ROOT_TYPE_NWAD_SECURE: ROOT_TYPE_NWAD; goto done; } } diff --git a/qse/cmd/http/httpd.conf b/qse/cmd/http/httpd.conf index 3af3f447..48a525e2 100644 --- a/qse/cmd/http/httpd.conf +++ b/qse/cmd/http/httpd.conf @@ -137,6 +137,7 @@ server-default { #################################################################### proxy { http = yes; # yes/on, no/off + https = yes; # yes/on, no/off connect = yes; # yes/on, no/off intercept = yes; # yes/proxy/on, local, no/off, upgrade = yes; # yes/on, no/off diff --git a/qse/include/qse/http/httpd.h b/qse/include/qse/http/httpd.h index 6da384f9..bde8977b 100644 --- a/qse/include/qse/http/httpd.h +++ b/qse/include/qse/http/httpd.h @@ -674,7 +674,7 @@ struct qse_httpd_server_t qse_httpd_server_dope_t dope; /* set by server.open callback */ - qse_httpd_hnd_t handle; + qse_httpd_hnd_t handle; /* private */ qse_httpd_t* httpd; diff --git a/qse/lib/http/httpd-std.c b/qse/lib/http/httpd-std.c index 943edfef..57371192 100644 --- a/qse/lib/http/httpd-std.c +++ b/qse/lib/http/httpd-std.c @@ -135,6 +135,10 @@ struct server_xtn_t qse_httpd_serverstd_makersrc_t makersrc; qse_httpd_serverstd_freersrc_t freersrc; +#if defined(HAVE_SSL) + SSL_CTX* ssl_ctx; +#endif + /* temporary buffer to handle authorization */ qse_mcstr_t auth; }; @@ -577,7 +581,6 @@ typedef struct httpd_xtn_t httpd_xtn_t; struct httpd_xtn_t { #if defined(HAVE_SSL) - SSL_CTX* ssl_client_ctx; SSL_CTX* ssl_peer_ctx; #endif qse_httpd_ecb_t ecb; @@ -586,15 +589,12 @@ struct httpd_xtn_t }; #if defined(HAVE_SSL) -static int init_xtn_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) +static int init_server_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) { -/* BUG BUG BUG. SSL context for client must exist inside the seerver, i guess */ - SSL_CTX* client_ctx = QSE_NULL; - httpd_xtn_t* xtn; + SSL_CTX* ssl_ctx = QSE_NULL; server_xtn_t* server_xtn; qse_httpd_serverstd_ssl_t ssl; - xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd); server_xtn = (server_xtn_t*)qse_httpd_getserverxtn (httpd, server); if (server_xtn->query (httpd, server, QSE_HTTPD_SERVERSTD_SSL, QSE_NULL, &ssl) <= -1) @@ -604,23 +604,23 @@ static int init_xtn_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) if (ssl.certfile == QSE_NULL || ssl.keyfile == QSE_NULL) { - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); + httpd->errnum = QSE_HTTPD_EINVAL; goto oops; } - client_ctx = SSL_CTX_new (SSLv23_server_method()); - if (!client_ctx) + ssl_ctx = SSL_CTX_new (SSLv23_server_method()); + if (!ssl_ctx) { - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM); + httpd->errnum = QSE_HTTPD_ESYSERR; goto oops; } /*SSL_CTX_set_info_callback(ctx,ssl_info_callback);*/ - 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 (SSL_CTX_use_certificate_file (ssl_ctx, ssl.certfile, SSL_FILETYPE_PEM) == 0 || + SSL_CTX_use_PrivateKey_file (ssl_ctx, ssl.keyfile, SSL_FILETYPE_PEM) == 0 || + SSL_CTX_check_private_key (ssl_ctx) == 0 /*|| + SSL_CTX_use_certificate_chain_file (ssl_ctx, chainfile) == 0*/) { if (httpd->opt.trait & QSE_HTTPD_LOGACT) { @@ -632,29 +632,29 @@ static int init_xtn_ssl (qse_httpd_t* httpd, qse_httpd_server_t* server) httpd->opt.rcb.logact (httpd, &msg); } - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); /* TODO: define a better error code */ + httpd->errnum = QSE_HTTPD_ESYSERR; /* 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); + SSL_CTX_set_read_ahead (ssl_ctx, 0); - xtn->ssl_client_ctx = client_ctx; + server_xtn->ssl_ctx = ssl_ctx; return 0; oops: - if (client_ctx) SSL_CTX_free (client_ctx); + if (ssl_ctx) SSL_CTX_free (ssl_ctx); return -1; } -static void fini_xtn_ssl (httpd_xtn_t* xtn) +static void fini_server_ssl (server_xtn_t* xtn) { /* TODO: CRYPTO_set_id_callback (QSE_NULL); */ /* TODO: CRYPTO_set_locking_callback (QSE_NULL); */ - SSL_CTX_free (xtn->ssl_client_ctx); + SSL_CTX_free (xtn->ssl_ctx); } static int init_xtn_peer_ssl (qse_httpd_t* httpd) @@ -667,13 +667,11 @@ static int init_xtn_peer_ssl (qse_httpd_t* httpd) peer_ctx = SSL_CTX_new (SSLv23_client_method()); if (!peer_ctx) { - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM); + httpd->errnum = QSE_HTTPD_ESYSERR; goto oops; } xtn->ssl_peer_ctx = peer_ctx; - -printf ("SSL PEER CTX ============>%p\n", xtn->ssl_peer_ctx); return 0; oops: @@ -698,7 +696,6 @@ static void cleanup_standard_httpd (qse_httpd_t* httpd) #if defined(HAVE_SSL) if (xtn->ssl_peer_ctx) fini_xtn_peer_ssl (xtn); - if (xtn->ssl_client_ctx) fini_xtn_ssl (xtn); #endif #if defined(USE_LTDL) @@ -713,11 +710,14 @@ qse_httpd_t* qse_httpd_openstd (qse_size_t xtnsize) qse_httpd_t* qse_httpd_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize) { - qse_httpd_t* httpd; - httpd_xtn_t* xtn; + qse_httpd_t* httpd = QSE_NULL; + httpd_xtn_t* xtn = QSE_NULL; +#if defined(USE_LTDL) + int lt_dlinited = 0; +#endif httpd = qse_httpd_open (mmgr, QSE_SIZEOF(httpd_xtn_t) + xtnsize); - if (httpd == QSE_NULL) return QSE_NULL; + if (httpd == QSE_NULL) goto oops; xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd); @@ -726,11 +726,12 @@ qse_httpd_t* qse_httpd_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize) * lt_dlexit() shuts down libltdl if it's called as many times as * corresponding lt_dlinit(). so it's safe to call lt_dlinit() * and lt_dlexit() at the library level. */ - if (lt_dlinit () != 0) - { - qse_httpd_close (httpd); - return QSE_NULL; - } + if (lt_dlinit () != 0) goto oops; + lt_dlinited = 1; +#endif + +#if defined(HAVE_SSL) + if (init_xtn_peer_ssl (httpd) <= -1) goto oops; #endif set_httpd_callbacks (httpd); @@ -739,6 +740,16 @@ qse_httpd_t* qse_httpd_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize) qse_httpd_pushecb (httpd, &xtn->ecb); return httpd; + +oops: +#if defined(HAVE_SSL) + if (xtn && xtn->ssl_peer_ctx) fini_xtn_peer_ssl (xtn); +#endif +#if defined(USE_LTDL) + if (lt_dlinited) lt_dlexit (); +#endif + if (httpd) qse_httpd_close (httpd); + return QSE_NULL; } void* qse_httpd_getxtnstd (qse_httpd_t* httpd) @@ -1169,11 +1180,7 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) 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; - } + QSE_ASSERT (xtn->ssl_peer_ctx != QSE_NULL); ssl = SSL_new (xtn->ssl_peer_ctx); if (ssl == QSE_NULL) @@ -2212,19 +2219,18 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client) #if defined(HAVE_SSL) int ret; SSL* ssl; - httpd_xtn_t* xtn; + server_xtn_t* server_xtn; - xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd); - if (!xtn->ssl_client_ctx) + server_xtn = (server_xtn_t*)qse_httpd_getserverxtn (httpd, client->server); + + + if (!server_xtn->ssl_ctx) { - /* delayed initialization of ssl */ - if (init_xtn_ssl (httpd, client->server) <= -1) - { - return -1; - } + /* performed the delayed ssl initialization */ + if (init_server_ssl (httpd, client->server) <= -1) return -1; } - QSE_ASSERT (xtn->ssl_client_ctx != QSE_NULL); + QSE_ASSERT (server_xtn->ssl_ctx != QSE_NULL); QSE_ASSERT (QSE_SIZEOF(client->handle2) >= QSE_SIZEOF(ssl)); if (HANDLE_TO_SSL(client->handle2)) @@ -2233,14 +2239,19 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client) } else { - ssl = SSL_new (xtn->ssl_client_ctx); - if (ssl == QSE_NULL) return -1; + ssl = SSL_new (server_xtn->ssl_ctx); + if (ssl == QSE_NULL) + { + httpd->errnum = QSE_HTTPD_ESYSERR; + return -1; + } client->handle2 = SSL_TO_HANDLE(ssl); if (SSL_set_fd (ssl, client->handle) == 0) { /* don't free ssl here since client_closed() * will free it */ + httpd->errnum = QSE_HTTPD_ESYSERR; return -1; } @@ -3335,6 +3346,10 @@ static void detach_server (qse_httpd_t* httpd, qse_httpd_server_t* server) server_xtn = (server_xtn_t*) qse_httpd_getserverxtn (httpd, server); if (server_xtn->detach) server_xtn->detach (httpd, server); if (server_xtn->auth.ptr) QSE_MMGR_FREE (httpd->mmgr, server_xtn->auth.ptr); + +#if defined(HAVE_SSL) + if (server_xtn->ssl_ctx) fini_server_ssl (server_xtn); +#endif } struct mime_tab_t @@ -3481,19 +3496,39 @@ qse_httpd_server_t* qse_httpd_attachserverstd ( server_xtn_t* server_xtn; xdope = *dope; + /* detach_server() is called when the server is detached */ xdope.detach = detach_server; xserver = qse_httpd_attachserver (httpd, &xdope, QSE_SIZEOF(*server_xtn) + xtnsize); if (xserver == QSE_NULL) return QSE_NULL; server_xtn = qse_httpd_getserverxtn (httpd, xserver); + QSE_MEMSET (server_xtn, 0, QSE_SIZEOF(*server_xtn)); + /* chain the original detach function */ server_xtn->detach = dope->detach; server_xtn->query = query_server; server_xtn->makersrc = make_resource; server_xtn->freersrc = free_resource; + /* init_server_ssl() queries the server instance for the SSL key and cert files. + * the standard query handler 'query_server' returns failure for this. + * the actual handler can be set with qse_httpd_setserverstdopt(). + * calling init_server_ssl() causes server attachment to fail for this reason. + * so initialization of ssl must be delayed. So the call is commented out. + if (init_server_ssl (httpd, xserver) <= -1) + { + / * reset server_xtn->detach as i want detach_server() to + * skip calling dope->detach. dope->detach should be called + * upon detachment only if qse_httpd_attachserverstd() has + * been successful. * / + server_xtn->detach = QSE_NULL; + qse_httpd_detachserver (httpd, xserver); + xserver = QSE_NULL; + } + */ + return xserver; } diff --git a/qse/lib/http/httpd.c b/qse/lib/http/httpd.c index 0be0912f..506eb988 100644 --- a/qse/lib/http/httpd.c +++ b/qse/lib/http/httpd.c @@ -1635,7 +1635,7 @@ int qse_httpd_loop (qse_httpd_t* httpd) if (httpd->server.list.head == QSE_NULL) { - httpd->errnum = QSE_HTTPD_EINVAL; + httpd->errnum = QSE_HTTPD_ENOSVR; return -1; }