diff --git a/qse/cmd/http/httpd.c b/qse/cmd/http/httpd.c index edb6ae93..2c230cb1 100644 --- a/qse/cmd/http/httpd.c +++ b/qse/cmd/http/httpd.c @@ -1937,7 +1937,13 @@ static int httpd_main (int argc, qse_char_t* argv[]) if (g_debug) rcb.logact = logact_httpd; /* i don't remember old logging handler */ qse_httpd_setopt (httpd, QSE_HTTPD_RCB, &rcb); - ret = qse_httpd_loopstd (httpd, QSE_NULL); +/* TODO: read from configuration... */ +{ +qse_httpd_ursstd_t urs; +qse_memset (&urs, 0, QSE_SIZEOF(urs)); +qse_mbstonwad ("[::1]:97", &urs.nwad); + ret = qse_httpd_loopstd (httpd, QSE_NULL, &urs); +} restore_signal_handlers (); g_httpd = QSE_NULL; diff --git a/qse/include/qse/cmn/tmr.h b/qse/include/qse/cmn/tmr.h index a5bc2fb3..59519a28 100644 --- a/qse/include/qse/cmn/tmr.h +++ b/qse/include/qse/cmn/tmr.h @@ -25,6 +25,7 @@ typedef struct qse_tmr_t qse_tmr_t; typedef struct qse_tmr_event_t qse_tmr_event_t; +typedef qse_size_t qse_tmr_index_t; typedef void (*qse_tmr_handler_t) ( qse_tmr_t* tmr, @@ -33,10 +34,10 @@ typedef void (*qse_tmr_handler_t) ( ); typedef void (*qse_tmr_updater_t) ( - qse_tmr_t* tmr, - qse_size_t old_index, - qse_size_t new_index, - void* ctx + qse_tmr_t* tmr, + qse_tmr_index_t old_index, + qse_tmr_index_t new_index, + void* ctx ); struct qse_tmr_t @@ -55,7 +56,7 @@ struct qse_tmr_event_t qse_tmr_updater_t updater; }; -#define QSE_TMR_INVALID ((qse_size_t)-1) +#define QSE_TMR_INVALID_INDEX ((qse_size_t)-1) #define QSE_TMR_SIZE(tmr) ((tmr)->size) #define QSE_TMR_CAPA(tmr) ((tmr)->capa); @@ -99,23 +100,23 @@ QSE_EXPORT void qse_tmr_clear ( /** * The qse_tmr_insert() function schedules a new event. * - * \return #QSE_TMR_INVALID on failure, valid index on success. + * \return #QSE_TMR_INVALID_INDEX on failure, valid index on success. */ -QSE_EXPORT qse_size_t qse_tmr_insert ( +QSE_EXPORT qse_tmr_index_t qse_tmr_insert ( qse_tmr_t* tmr, const qse_tmr_event_t* event ); QSE_EXPORT qse_size_t qse_tmr_update ( qse_tmr_t* tmr, - qse_size_t index, + qse_tmr_index_t index, const qse_tmr_event_t* event ); QSE_EXPORT void qse_tmr_remove ( - qse_tmr_t* tmr, - qse_size_t index + qse_tmr_t* tmr, + qse_tmr_index_t index ); QSE_EXPORT qse_size_t qse_tmr_fire ( diff --git a/qse/include/qse/http/httpd.h b/qse/include/qse/http/httpd.h index ab27f268..8030ce14 100644 --- a/qse/include/qse/http/httpd.h +++ b/qse/include/qse/http/httpd.h @@ -59,6 +59,7 @@ enum qse_httpd_errnum_t QSE_HTTPD_EDISCON, /* client disconnnected */ QSE_HTTPD_EBADREQ, /* bad request */ QSE_HTTPD_ENODNS, /* dns service not activated */ + QSE_HTTPD_ENOURS, /* urs service not activated */ QSE_HTTPD_ETASK }; typedef enum qse_httpd_errnum_t qse_httpd_errnum_t; @@ -261,7 +262,8 @@ struct qse_httpd_scb_t int (*open) (qse_httpd_t* httpd, qse_httpd_dns_t* dns); void (*close) (qse_httpd_t* httpd, qse_httpd_dns_t* dns); int (*recv) (qse_httpd_t* httpd, qse_httpd_dns_t* dns); - int (*send) (qse_httpd_t* httpd, qse_httpd_dns_t* dns, const qse_mchar_t* name, qse_httpd_resol_t resol, void* ctx); + int (*send) (qse_httpd_t* httpd, qse_httpd_dns_t* dns, + const qse_mchar_t* name, qse_httpd_resol_t resol, void* ctx); } dns; struct @@ -269,7 +271,8 @@ struct qse_httpd_scb_t int (*open) (qse_httpd_t* httpd, qse_httpd_urs_t* urs); void (*close) (qse_httpd_t* httpd, qse_httpd_urs_t* urs); int (*recv) (qse_httpd_t* httpd, qse_httpd_urs_t* urs); - int (*send) (qse_httpd_t* httpd, qse_httpd_urs_t* urs, const qse_mchar_t* url, qse_httpd_rewrite_t rewrite, void* ctx); + int (*send) (qse_httpd_t* httpd, qse_httpd_urs_t* urs, + const qse_mchar_t* url, qse_httpd_rewrite_t rewrite, void* ctx); } urs; }; @@ -439,7 +442,7 @@ struct qse_httpd_client_t qse_httpd_task_trigger_t trigger; qse_ntime_t last_active; - qse_size_t tmr_idle; + qse_tmr_index_t tmr_idle; qse_httpd_client_t* prev; qse_httpd_client_t* next; @@ -552,14 +555,18 @@ struct qse_httpd_rsrc_cgi_t enum qse_httpd_rsrc_proxy_flag_t { - QSE_HTTPD_RSRC_PROXY_RAW = (1 << 0), - QSE_HTTPD_RSRC_PROXY_DST_STR = (1 << 1) + QSE_HTTPD_RSRC_PROXY_RAW = (1 << 0), + QSE_HTTPD_RSRC_PROXY_DST_STR = (1 << 1), + QSE_HTTPD_RSRC_PROXY_TRANSPARENT = (1 << 2), + QSE_HTTPD_RSRC_PROXY_URS = (1 << 3) /* url rewriting enabled */ }; typedef struct qse_httpd_rsrc_proxy_t qse_httpd_rsrc_proxy_t; struct qse_httpd_rsrc_proxy_t { int flags; /* bitwise-ORed of qse_httpd_rsrc_proxy_flag_t enumerators */ + + const qse_mchar_t* host; /* host name part that were in the original request url */ union { qse_nwad_t nwad; @@ -990,7 +997,6 @@ QSE_EXPORT qse_mchar_t* qse_httpd_escapehtml ( const qse_mchar_t* str ); - QSE_EXPORT int qse_httpd_resolname ( qse_httpd_t* httpd, const qse_mchar_t* name, @@ -998,6 +1004,13 @@ QSE_EXPORT int qse_httpd_resolname ( void* ctx ); +QSE_EXPORT int qse_httpd_rewriteurl ( + qse_httpd_t* ttpd, + const qse_mchar_t* url, + qse_httpd_rewrite_t rewrite, + void* ctx +); + #ifdef __cplusplus } diff --git a/qse/include/qse/http/stdhttpd.h b/qse/include/qse/http/stdhttpd.h index fd63ab9e..10892ee4 100644 --- a/qse/include/qse/http/stdhttpd.h +++ b/qse/include/qse/http/stdhttpd.h @@ -228,7 +228,8 @@ QSE_EXPORT void* qse_httpd_getserverstdxtn ( QSE_EXPORT int qse_httpd_loopstd ( qse_httpd_t* httpd, - const qse_httpd_dnsstd_t* dns + const qse_httpd_dnsstd_t* dns, + const qse_httpd_ursstd_t* urs ); #ifdef __cplusplus diff --git a/qse/lib/cmn/tmr.c b/qse/lib/cmn/tmr.c index 9c265164..175ad5a9 100644 --- a/qse/lib/cmn/tmr.c +++ b/qse/lib/cmn/tmr.c @@ -55,20 +55,25 @@ void qse_tmr_close (qse_tmr_t* tmr) int qse_tmr_init (qse_tmr_t* tmr, qse_mmgr_t* mmgr, qse_size_t capa) { + qse_tmr_event_t* tmp; + QSE_MEMSET (tmr, 0, QSE_SIZEOF(*tmr)); if (capa <= 0) capa = 1; - tmr->event = QSE_MMGR_ALLOC (mmgr, capa * QSE_SIZEOF(*tmr->event)); - if (tmr->event == QSE_NULL) return -1; + tmp = QSE_MMGR_ALLOC (mmgr, capa * QSE_SIZEOF(*tmp)); + if (tmp == QSE_NULL) return -1; tmr->mmgr = mmgr; tmr->capa = capa; + tmr->event = tmp; + return 0; } void qse_tmr_fini (qse_tmr_t* tmr) { + qse_tmr_clear (tmr); if (tmr->event) QSE_MMGR_FREE (tmr->mmgr, tmr->event); } @@ -84,12 +89,10 @@ void* qse_tmr_getxtn (qse_tmr_t* tmr) void qse_tmr_clear (qse_tmr_t* tmr) { - tmr->size = 0; - -/* TOOD: use tmr_remove for notification.... */ + while (tmr->size > 0) qse_tmr_remove (tmr, 0); } -static qse_size_t sift_up (qse_tmr_t* tmr, qse_size_t index, int notify) +static qse_tmr_index_t sift_up (qse_tmr_t* tmr, qse_tmr_index_t index, int notify) { qse_size_t parent; @@ -122,7 +125,7 @@ static qse_size_t sift_up (qse_tmr_t* tmr, qse_size_t index, int notify) return index; } -static qse_size_t sift_down (qse_tmr_t* tmr, qse_size_t index, int notify) +static qse_tmr_index_t sift_down (qse_tmr_t* tmr, qse_tmr_index_t index, int notify) { qse_size_t base = tmr->size / 2; @@ -164,14 +167,14 @@ static qse_size_t sift_down (qse_tmr_t* tmr, qse_size_t index, int notify) return index; } -void qse_tmr_remove (qse_tmr_t* tmr, qse_size_t index) +void qse_tmr_remove (qse_tmr_t* tmr, qse_tmr_index_t index) { qse_tmr_event_t item; QSE_ASSERT (index < tmr->size); item = tmr->event[index]; - tmr->event[index].updater (tmr, index, QSE_TMR_INVALID, tmr->event[index].ctx); + tmr->event[index].updater (tmr, index, QSE_TMR_INVALID_INDEX, tmr->event[index].ctx); tmr->size = tmr->size - 1; if (tmr->size > 0 && index != tmr->size) @@ -182,9 +185,9 @@ void qse_tmr_remove (qse_tmr_t* tmr, qse_size_t index) } } -qse_size_t qse_tmr_insert (qse_tmr_t* tmr, const qse_tmr_event_t* event) +qse_tmr_index_t qse_tmr_insert (qse_tmr_t* tmr, const qse_tmr_event_t* event) { - qse_size_t index = tmr->size; + qse_tmr_index_t index = tmr->size; if (index >= tmr->capa) { @@ -192,8 +195,8 @@ qse_size_t qse_tmr_insert (qse_tmr_t* tmr, const qse_tmr_event_t* event) qse_size_t new_capa; new_capa = tmr->capa * 2; - tmp = QSE_MMGR_REALLOC (tmr->mmgr, tmr->event, new_capa); - if (tmp == QSE_NULL) return QSE_TMR_INVALID; + tmp = QSE_MMGR_REALLOC (tmr->mmgr, tmr->event, new_capa * QSE_SIZEOF(*tmp)); + if (tmp == QSE_NULL) return QSE_TMR_INVALID_INDEX; tmr->event = tmp; tmr->capa = new_capa; diff --git a/qse/lib/http/Makefile.am b/qse/lib/http/Makefile.am index 92da738e..603cb218 100644 --- a/qse/lib/http/Makefile.am +++ b/qse/lib/http/Makefile.am @@ -17,6 +17,8 @@ libqsehttp_la_SOURCES = \ httpd-file.c \ httpd-proxy.c \ httpd-std.c \ + httpd-std-dns.h \ + httpd-std-urs.h \ httpd-task.c \ httpd-text.c \ upxd.c diff --git a/qse/lib/http/Makefile.in b/qse/lib/http/Makefile.in index be457266..ffadb49a 100644 --- a/qse/lib/http/Makefile.in +++ b/qse/lib/http/Makefile.in @@ -338,6 +338,8 @@ libqsehttp_la_SOURCES = \ httpd-file.c \ httpd-proxy.c \ httpd-std.c \ + httpd-std-dns.h \ + httpd-std-urs.h \ httpd-task.c \ httpd-text.c \ upxd.c diff --git a/qse/lib/http/httpd-proxy.c b/qse/lib/http/httpd-proxy.c index be615266..ba4dafa9 100644 --- a/qse/lib/http/httpd-proxy.c +++ b/qse/lib/http/httpd-proxy.c @@ -37,12 +37,15 @@ struct task_proxy_t { #define PROXY_INIT_FAILED (1 << 0) #define PROXY_RAW (1 << 1) -#define PROXY_RESOLVE_PEER_NAME (1 << 2) -#define PROXY_PEER_NAME_RESOLVED (1 << 3) -#define PROXY_PEER_NAME_UNRESOLVED (1 << 4) -#define PROXY_X_FORWARDED_FOR (1 << 5) -#define PROXY_VIA (1 << 6) -#define PROXY_VIA_RETURNING (1 << 7) +#define PROXY_TRANSPARENT (1 << 2) +#define PROXY_RESOLVE_PEER_NAME (1 << 3) +#define PROXY_PEER_NAME_RESOLVED (1 << 4) +#define PROXY_PEER_NAME_UNRESOLVED (1 << 5) +#define PROXY_REWRITE_URL (1 << 6) +#define PROXY_URL_REWRITTEN (1 << 7) +#define PROXY_X_FORWARDED_FOR (1 << 8) /* X-Forwarded-For added */ +#define PROXY_VIA (1 << 9) /* Via added to the request */ +#define PROXY_VIA_RETURNING (1 << 10) /* Via added to the response */ int flags; qse_httpd_t* httpd; qse_httpd_client_t* client; @@ -50,8 +53,8 @@ struct task_proxy_t int method; qse_http_version_t version; int keepalive; /* taken from the request */ - int raw; + qse_mchar_t* url_to_rewrite; qse_mchar_t* pseudonym; qse_htrd_t* peer_htrd; @@ -66,8 +69,9 @@ struct task_proxy_t #define PROXY_REQ_FWDERR (1 << 0) #define PROXY_REQ_FWDCHUNKED (1 << 1) int reqflags; - qse_htre_t* req; /* original client request associated with this */ + qse_htre_t* req; /* set to original client request associated with this if necessary */ qse_mbs_t* reqfwdbuf; /* content from the request */ + const qse_mchar_t* host; qse_mbs_t* res; qse_size_t res_consumed; @@ -234,8 +238,7 @@ static int proxy_capture_client_header (qse_htre_t* req, const qse_mchar_t* key, { task_proxy_t* proxy = (task_proxy_t*)ctx; - - if (!(proxy->flags & PROXY_X_FORWARDED_FOR)) + if (!(proxy->flags & (PROXY_TRANSPARENT | PROXY_X_FORWARDED_FOR))) { if (qse_mbscasecmp (key, QSE_MT("X-Forwarded-For")) == 0) { @@ -246,7 +249,7 @@ static int proxy_capture_client_header (qse_htre_t* req, const qse_mchar_t* key, qse_mchar_t extra[128]; proxy->flags |= PROXY_X_FORWARDED_FOR; - qse_nwadtombs (&proxy->client->remote_addr, extra, QSE_COUNTOF(extra), QSE_NWADTOMBS_ALL); + qse_nwadtombs (&proxy->client->remote_addr, extra, QSE_COUNTOF(extra), QSE_NWADTOMBS_ADDR); return proxy_add_header_to_buffer_with_extra_data (proxy, proxy->reqfwdbuf, key, val, QSE_MT(", %hs"), extra); } @@ -903,6 +906,41 @@ static int task_init_proxy ( } if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_RAW) proxy->flags |= PROXY_RAW; + if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_TRANSPARENT) proxy->flags |= PROXY_TRANSPARENT; + + if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_URS) + { +#if 0 + const qse_mchar_t* qpath; + const qse_mchar_t* metnam; + const qse_htre_hdrval_t* hosthv; + qse_mchar_t cliaddrbuf[128]; + + /* URL client-ip/client-fqdn ident method */ + qpath = qse_htre_getqpath(htreq); + method = qse_htre_getqmethodtype(htreq); + metnam = qse_httpmethodtombs(method); + hosthv = qse_htre_getheaderval(htreq, QSE_MT("Host")); + if (hosthv) printf ("hosthv -> %s\n", hosthv->ptr); + qse_nwadtombs (&client->remote_addr, cliaddrbuf, QSE_COUNTOF(cliaddrbuf), QSE_NWADTOMBS_ADDR); +#endif + + /* enable url rewriting */ + proxy->flags |= PROXY_REWRITE_URL; + +#if 0 + /* TODO: build URL TO REWRITE */ + if (method == QSE_HTTP_CONNECT) + url_len = qse_mbsxfmt (QSE_NULL, 0, QSE_MT("%s %s/- - %s"), qpath, cliaddrbuf, metnam); + else if (host) + url_len = qse_mbsxfmt (QSE_NULL, 0, QSE_MT("http://%s%s %s/- - %s"), host, qpath, cliaddrbuf, metnam); + else if (hosthv) + url_len = qse_mbsxfmt (QSE_NULL, 0, QSE_MT("http://%s%s %s/- - %s"), hosthv->ptr, qpath, cliaddrbuf, metnam); + else + url_len = qse_mbsxfmt (QSE_NULL, 0, QSE_MT("%s %s/- - %s"), qpath, cliaddrbuf, metnam); +#endif + } + proxy->peer.local = arg->rsrc->src.nwad; if (arg->rsrc->flags & QSE_HTTPD_RSRC_PROXY_DST_STR) { @@ -938,7 +976,6 @@ static int task_init_proxy ( else proxy->peer.local.type = arg->rsrc->dst.type;*/ proxy->req = QSE_NULL; - /* -------------------------------------------------------------------- * TODO: compose headers to send to peer and push them to fwdbuf... * TODO: also change the content length check logic below... @@ -950,6 +987,7 @@ static int task_init_proxy ( proxy->reqfwdbuf = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len)); if (proxy->reqfwdbuf == QSE_NULL) goto oops; + proxy->host = arg->rsrc->host; if (proxy->flags & PROXY_RAW) { /* TODO: when connect is attempted, no keep-alive must be hornored. @@ -985,7 +1023,7 @@ static int task_init_proxy ( qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 || qse_htre_walkheaders (arg->req, proxy_capture_client_header, proxy) <= -1) goto oops; - if (!(proxy->flags & PROXY_X_FORWARDED_FOR)) + if (!(proxy->flags & (PROXY_TRANSPARENT | PROXY_X_FORWARDED_FOR))) { /* X-Forwarded-For is not added by proxy_capture_client_header() * above. I don't care if it's included in the trailer. */ @@ -993,7 +1031,7 @@ static int task_init_proxy ( proxy->flags |= PROXY_X_FORWARDED_FOR; - qse_nwadtombs (&proxy->client->remote_addr, extra, QSE_COUNTOF(extra), QSE_NWADTOMBS_ALL); + qse_nwadtombs (&proxy->client->remote_addr, extra, QSE_COUNTOF(extra), QSE_NWADTOMBS_ADDR); if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("X-Forwarded-For: ")) == (qse_size_t)-1 || qse_mbs_cat (proxy->reqfwdbuf, extra) == (qse_size_t)-1 || @@ -1144,7 +1182,7 @@ static int task_init_proxy ( { /* set up a callback to be called when the request content * is fed to the htrd reader. qse_htre_addcontent() that - * htrd calls invokes this callback. */ + * htrd calls invokes this callback. */ proxy->req = arg->req; qse_htre_setconcb (proxy->req, proxy_snatch_client_input, task); } @@ -1311,7 +1349,7 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask if (proxy->peer_output_received < proxy->peer_output_length) { if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy premature eof - "); + log_proxy_error (proxy, "proxy premature eof(content) - "); return -1; } } @@ -1594,9 +1632,8 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", { /* end of output from peer before it has seen a header. * the proxy peer must be bad. */ - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy premature eof - "); + log_proxy_error (proxy, "proxy premature eof(header) - "); if (!(proxy->resflags & PROXY_RES_RECEIVED_100)) http_errnum = 502; goto oops; @@ -1618,7 +1655,7 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", /* premature eof from the peer */ if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy premature eof - "); + log_proxy_error (proxy, "proxy no content(chunked) - "); goto oops; } } @@ -1826,6 +1863,21 @@ static void on_peer_name_resolved (qse_httpd_t* httpd, const qse_mchar_t* name, printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX PEER NAME RESOLVED.....\n"); } +static void on_url_rewritten (qse_httpd_t* httpd, const qse_mchar_t* url, const qse_mchar_t* new_url, void* ctx) +{ + qse_httpd_task_t* task = (qse_httpd_task_t*)ctx; + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + +/* TODO: HANDLE THIS PROPERLY */ + proxy->flags |= PROXY_INIT_FAILED; +printf ("XXXXXXXXXXXXXXXXXXXXXXXXXX URL REWRITTEN ....\n"); + + if (qse_httpd_activatetasktrigger (httpd, proxy->client, task) <= -1) + { + proxy->flags |= PROXY_INIT_FAILED; + } +} + static int task_main_proxy ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { @@ -1840,19 +1892,18 @@ static int task_main_proxy ( goto oops; } -#if 0 if (proxy->flags & PROXY_REWRITE_URL) { - if (qse_httpd_rewriteurl (httpd, proxy->url, on_url_rewritten, task) <= -1) goto oops; + /* note that url_to_rewrite is URL + extra information. */ + if (qse_httpd_rewriteurl (httpd, proxy->url_to_rewrite, on_url_rewritten, task) <= -1) goto oops; if (proxy->flags & PROXY_INIT_FAILED) goto oops; - if ((proxy->flags & PROXY_RESOL_REWRITE_URL) && + if ((proxy->flags & PROXY_REWRITE_URL) && qse_httpd_inactivatetasktrigger (httpd, client, task) <= -1) goto oops; return 1; } -#endif if (proxy->flags & PROXY_RESOLVE_PEER_NAME) { @@ -2000,7 +2051,7 @@ qse_httpd_task_t* qse_httpd_entaskproxy ( arg.rsrc = proxy; arg.req = req; - + if (proxy->pseudonym) xtnsize += qse_mbslen (proxy->pseudonym) + 1; else @@ -2009,7 +2060,6 @@ qse_httpd_task_t* qse_httpd_entaskproxy ( if (proxy->flags & QSE_HTTPD_RSRC_PROXY_DST_STR) xtnsize += qse_mbslen (proxy->dst.str) + 1; - QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); task.init = task_init_proxy; task.fini = task_fini_proxy; diff --git a/qse/lib/http/httpd-std-dns.h b/qse/lib/http/httpd-std-dns.h new file mode 100644 index 00000000..1c41c3c1 --- /dev/null +++ b/qse/lib/http/httpd-std-dns.h @@ -0,0 +1,915 @@ +/* + * $Id$ + * + Copyright 2006-2014 Chung, Hyung-Hwan. + This file is part of QSE. + + QSE is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + QSE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with QSE. If not, see . + */ + +/* + * This file holds dns support code and is included by httpd-std.c + */ + +#define DNS_MAX_DN_LEN 255 /* full domain name length in binary form (i.e. 3xyz2eu0) */ +#define DNS_MAX_MSG_LEN 512 /* basic dns only. no EDNS0. so 512 at most */ + +#define DNS_MIN_TTL 10 +#define DNS_MAX_TTL 120 /* TODO: make these configurable... */ + +#define DNS_SEQ_RANGE_SIZE ((QSE_TYPE_MAX(qse_uint16_t) / 2) - 2) + +typedef struct dns_ctx_t dns_ctx_t; +typedef struct dns_req_t dns_req_t; +typedef struct dns_ans_t dns_ans_t; +typedef struct dns_hdr_t dns_hdr_t; +typedef struct dns_qdtrail_t dns_qdtrail_t; +typedef struct dns_antrail_t dns_antrail_t; + +enum +{ + DNS_OPCODE_QUERY = 0, + DNS_OPCODE_IQUERY = 1, + DNS_OPCODE_STATUS = 2, + DNS_OPCODE_NOTIFY = 4, + DNS_OPCODE_UPDATE = 5, + + DNS_RCODE_NOERROR = 0, + DNS_RCODE_FORMERR = 1, + DNS_RCODE_SERVFAIL = 2, + DNS_RCODE_NXDOMAIN = 3, + DNS_RCODE_NOTIMPL = 4, + DNS_RCODE_REFUSED = 5, + + DNS_QTYPE_A = 1, + DNS_QTYPE_NS = 2, + DNS_QTYPE_CNAME = 5, + DNS_QTYPE_SOA = 6, + DNS_QTYPE_PTR = 12, + DNS_QTYPE_MX = 15, + DNS_QTYPE_TXT = 16, + DNS_QTYPE_AAAA = 28, + DNS_QTYPE_OPT = 41, + DNS_QTYPE_ANY = 255, + + DNS_QCLASS_IN = 1, /* internet */ + DNS_QCLASS_CH = 3, /* chaos */ + DNS_QCLASS_HS = 4, /* hesiod */ + DNS_QCLASS_NONE = 254, + DNS_QCLASS_ANY = 255 +}; + +#include +struct dns_hdr_t +{ + qse_uint16_t id; + +#if defined(QSE_ENDIAN_BIG) + qse_uint16_t qr: 1; /* question or response */ + qse_uint16_t opcode: 4; + qse_uint16_t aa: 1; /* authoritative answer */ + qse_uint16_t tc: 1; /* truncated message */ + qse_uint16_t rd: 1; /* recursion desired */ + + qse_uint16_t ra: 1; /* recursion available */ + qse_uint16_t z: 1; + qse_uint16_t ad: 1; + qse_uint16_t cd: 1; + qse_uint16_t rcode: 4; +#else + qse_uint16_t rd: 1; + qse_uint16_t tc: 1; + qse_uint16_t aa: 1; + qse_uint16_t opcode: 4; + qse_uint16_t qr: 1; + + qse_uint16_t rcode: 4; + qse_uint16_t cd: 1; + qse_uint16_t ad: 1; + qse_uint16_t z: 1; + qse_uint16_t ra: 1; +#endif + + qse_uint16_t qdcount; /* questions */ + qse_uint16_t ancount; /* answers */ + qse_uint16_t nscount; /* name servers */ + qse_uint16_t arcount; /* additional resource */ +}; + +struct dns_qdtrail_t +{ + qse_uint16_t qtype; + qse_uint16_t qclass; +}; + +struct dns_antrail_t +{ + qse_uint16_t qtype; + qse_uint16_t qclass; + qse_uint32_t ttl; + qse_uint16_t dlen; /* data length */ +}; +#include + +struct dns_ctx_t +{ + qse_httpd_t* httpd; + qse_httpd_dns_t* dns; + + qse_skad_t skad; + int skadlen; + + qse_uint16_t seq; + dns_req_t* reqs[2048]; /* TOOD: choose the right size or make it configurable. must be < DNS_SEQ_RANGE_SIZE */ + dns_ans_t* anss[2048]; + qse_uint16_t req_count; /* the number of pending requests */ + + qse_uint16_t n_qcin; /* DNS_QCLASS_IN in network byte order */ + qse_uint16_t n_qta; /* DNS_QTYPE_A in network byte order */ + qse_uint16_t n_qtaaaa; /* DNS_QTYPE_AAAA in network byte order */ +}; + +struct dns_req_t +{ + qse_mchar_t* name; + +#define DNS_REQ_A_NX (1 << 0) +#define DNS_REQ_AAAA_NX (1 << 1) + int flags; + qse_uint16_t seqa, seqaaaa; + + qse_uint8_t* dn; + qse_size_t dnlen; + + qse_httpd_resol_t resol; + void* ctx; + + qse_uint8_t qa[DNS_MAX_DN_LEN + QSE_SIZEOF(dns_hdr_t) + QSE_SIZEOF(dns_qdtrail_t)]; + qse_uint8_t qaaaa[DNS_MAX_DN_LEN + QSE_SIZEOF(dns_hdr_t) + QSE_SIZEOF(dns_qdtrail_t)]; + int qalen; + int qaaaalen; + + dns_ctx_t* dc; + qse_tmr_index_t tmr_tmout; + int resends; + + dns_req_t* next; + dns_req_t* prev; +}; + +struct dns_ans_t +{ + /* the name part must be the same as dns_req_t */ + qse_mchar_t* name; + + /* the total size of data fields below must not be greater than + * the total size of data fields of dns_req_t excluding name. + * this condition is required for reusing the dns_req_t chunk + * when caching an answer without allocating another chunk. */ + qse_nwad_t nwad; + qse_int64_t age; + qse_uint32_t ttl; + dns_ans_t* next; +}; + +#define DN_AT_END(ptr) (ptr[0] == QSE_MT('\0') || (ptr[0] == QSE_MT('.') && ptr[1] == QSE_MT('\0'))) + +static qse_size_t to_dn (const qse_mchar_t* str, qse_uint8_t* buf, qse_size_t bufsz) +{ + qse_uint8_t* bp = buf, * be = buf + bufsz; + + QSE_ASSERT (QSE_SIZEOF(qse_uint8_t) == QSE_SIZEOF(qse_mchar_t)); + + if (!DN_AT_END(str)) + { + qse_uint8_t* lp; + qse_size_t len; + const qse_mchar_t* seg; + const qse_mchar_t* cur = str - 1; + + do + { + if (bp < be) lp = bp++; + else lp = QSE_NULL; + + seg = ++cur; + while (*cur != QSE_MT('\0') && *cur != QSE_MT('.')) + { + if (bp < be) *bp++ = *cur; + cur++; + } + len = cur - seg; + if (len <= 0 || len > 63) return 0; + + if (lp) *lp = (qse_uint8_t)len; + } + while (!DN_AT_END(cur)); + } + + if (bp < be) *bp++ = 0; + return bp - buf; +} + +static qse_size_t dn_length (qse_uint8_t* ptr, qse_size_t len) +{ + qse_uint8_t* curptr; + qse_size_t curlen, seglen; + + curptr = ptr; + curlen = len; + + do + { + if (curlen <= 0) return 0; + + seglen = *curptr++; + curlen = curlen - 1; + if (seglen == 0) break; + else if (seglen > curlen || seglen > 63) return 0; + + curptr += seglen; + curlen -= seglen; + } + while (1); + + return curptr - ptr; +} + +int init_dns_query (qse_uint8_t* buf, qse_size_t len, const qse_mchar_t* name, int qtype, qse_uint16_t seq) +{ + dns_hdr_t* hdr; + dns_qdtrail_t* qdtrail; + qse_size_t x; + + if (len < QSE_SIZEOF(*hdr)) return -1; + + QSE_MEMSET (buf, 0, len); + hdr = (dns_hdr_t*)buf; + hdr->id = qse_hton16(seq); + hdr->opcode = DNS_OPCODE_QUERY; + hdr->rd = 1; /* recursion desired*/ + hdr->qdcount = qse_hton16(1); /* 1 question */ + + len -= QSE_SIZEOF(*hdr); + + x = to_dn (name, (qse_uint8_t*)(hdr + 1), len); + if (x <= 0) return -1; + len -= x; + + if (len < QSE_SIZEOF(*qdtrail)) return -1; + qdtrail = (dns_qdtrail_t*)((qse_uint8_t*)(hdr + 1) + x); + + qdtrail->qtype = qse_hton16(qtype); + qdtrail->qclass = qse_hton16(DNS_QCLASS_IN); + return QSE_SIZEOF(*hdr) + x + QSE_SIZEOF(*qdtrail); +} + +static int dns_open (qse_httpd_t* httpd, qse_httpd_dns_t* dns) +{ +#if defined(__DOS__) + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); + return -1; +#else + sock_t fd = SOCK_INIT; + int flag; + qse_nwad_t nwad; + dns_ctx_t* dc; + httpd_xtn_t* httpd_xtn; + + httpd_xtn = qse_httpd_getxtn (httpd); + + dc = (dns_ctx_t*) qse_httpd_callocmem (httpd, QSE_SIZEOF(dns_ctx_t)); + if (dc == NULL) goto oops; + + dc->httpd = httpd; + dc->dns = dns; + dc->n_qcin = qse_hton16(DNS_QCLASS_IN); + dc->n_qta = qse_hton16(DNS_QTYPE_A); + dc->n_qtaaaa = qse_hton16(DNS_QTYPE_AAAA); + +/* TODO: add static cache entries from /etc/hosts */ + + nwad = httpd_xtn->dns.nwad; + if (nwad.type == QSE_NWAD_NX) + { + qse_sio_t* sio; + #if defined(_WIN32) + /* TODO: windns.h dnsapi.lib DnsQueryConfig... */ + #elif defined(__OS2__) + /* TODO: */ + #else + /* TODO: read /etc/resolv.conf???? */ + #endif + + sio = qse_sio_open (qse_httpd_getmmgr(httpd), 0, QSE_T("/etc/resolv.conf"), QSE_SIO_READ); + if (sio) + { + qse_mchar_t buf[128]; + qse_ssize_t len; + qse_mcstr_t tok; + qse_mchar_t* ptr; + qse_mchar_t* end; + + while (1) + { + len = qse_sio_getmbsn (sio, buf, QSE_COUNTOF(buf)); + if (len <= 0) break; + + end = buf + len; + ptr = buf; + + ptr = qse_mbsxtok (ptr, end - ptr, QSE_MT(" \t"), &tok); + if (ptr && qse_mbsxcmp (tok.ptr, tok.len, QSE_MT("nameserver")) == 0) + { + ptr = qse_mbsxtok (ptr, end - ptr, QSE_MT(" \t"), &tok); + if (qse_mbsntonwad (tok.ptr, tok.len, &nwad) >= 0) break; + } + } + qse_sio_close (sio); + } + } + + if (qse_getnwadport(&nwad) == 0) + qse_setnwadport (&nwad, qse_hton16(QSE_HTTPD_DNSSTD_DEFAULT_PORT)); + + dc->skadlen = qse_nwadtoskad (&nwad, &dc->skad); + if (dc->skadlen <= -1) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); + goto oops; + } + + if (httpd->opt.trait & QSE_HTTPD_LOGACT) + { + qse_httpd_act_t msg; + qse_size_t pos; + msg.code = QSE_HTTPD_CATCH_MDBGMSG; + pos = qse_mbsxcpy (msg.u.mdbgmsg, QSE_COUNTOF(msg.u.mdbgmsg), "nameserver set to "); + qse_nwadtombs (&nwad, &msg.u.mdbgmsg[pos], QSE_COUNTOF(msg.u.mdbgmsg) - pos, QSE_NWADTOMBS_ALL); + httpd->opt.rcb.logact (httpd, &msg); + } + + fd = socket (qse_skadfamily(&dc->skad), SOCK_DGRAM, IPPROTO_UDP); + if (!is_valid_socket(fd)) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } + + #if defined(FD_CLOEXEC) + flag = fcntl (fd, F_GETFD); + if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); + #endif + + #if defined(SO_REUSEADDR) + flag = 1; + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, QSE_SIZEOF(flag)); + #endif + + #if defined(SO_REUSEPORT) + flag = 1; + setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (void*)&flag, QSE_SIZEOF(flag)); + #endif + + if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; + + dns->handle.i = fd; + dns->ctx = dc; + + return 0; + +oops: + if (is_valid_socket(fd)) close_socket (fd); + if (dc) qse_httpd_freemem (httpd, dc); + return -1; + +#endif +} + +static void dns_remove_tmr_tmout (dns_req_t* req) +{ + if (req->tmr_tmout != QSE_TMR_INVALID_INDEX) + { + qse_httpd_removetimerevent (req->dc->httpd, req->tmr_tmout); + req->tmr_tmout = QSE_TMR_INVALID_INDEX; + } +} + +static void dns_close (qse_httpd_t* httpd, qse_httpd_dns_t* dns) +{ + dns_ctx_t* dc = (dns_ctx_t*)dns->ctx; + qse_size_t i; + + for (i = 0; i < QSE_COUNTOF(dc->reqs); i++) + { + dns_req_t* next_req; + while (dc->reqs[i]) + { + next_req = dc->reqs[i]->next; + + dns_remove_tmr_tmout (dc->reqs[i]); + qse_httpd_freemem (httpd, dc->reqs[i]); + + dc->reqs[i] = next_req; + dc->req_count--; + } + } + + QSE_ASSERT (dc->req_count == 0); + + for (i = 0; i < QSE_COUNTOF(dc->anss); i++) + { + dns_ans_t* next_ans; + while (dc->anss[i]) + { + next_ans = dc->anss[i]->next; + qse_httpd_freemem (httpd, dc->anss[i]); + dc->anss[i] = next_ans; + } + } + + close_socket (dns->handle.i); + qse_httpd_freemem (httpd, dns->ctx); +} + + + +static void dns_cache_answer (dns_ctx_t* dc, dns_req_t* req, const qse_nwad_t* nwad, qse_uint32_t ttl) +{ + dns_ans_t* ans, * prv, * cur; + qse_size_t hid; + qse_ntime_t now; + +/* TODO: implement the maximum number of entries in cache... */ + + /* i use the given request as a space to hold an answer. + * the following assertion must be met for this to work */ + QSE_ASSERT (QSE_SIZEOF(dns_req_t) >= QSE_SIZEOF(dns_ans_t)); + + qse_gettime (&now); + + ans = (dns_ans_t*)req; /* shadow the request with an answer */ + + /* reuse the data fields of the request except the name field. + * from here downwards, the data fields of the request are invalid. */ + + if (nwad) ans->nwad = *nwad; /* positive */ + else ans->nwad.type = QSE_NWAD_NX; /* negative */ + ans->age = now.sec; /* the granularity of a second should be good enough */ + + if (ttl < DNS_MIN_TTL) ttl = DNS_MIN_TTL; /* TODO: use configured value */ + else if (ttl > DNS_MAX_TTL) ttl = DNS_MAX_TTL; + + ans->ttl = ttl; + hid = hash_string (req->name) % QSE_COUNTOF(dc->anss); + + prv = QSE_NULL; + cur = dc->anss[hid]; + while (cur) + { + if (qse_mbscasecmp(cur->name, ans->name) == 0) + { + ans->next = cur->next; + if (prv) prv->next = ans; + else dc->anss[hid] = ans; + qse_httpd_freemem (dc->httpd, cur); + return; + } + + prv = cur; + cur = cur->next; + } + ans->next = dc->anss[hid]; + dc->anss[hid] = ans; +} + +static dns_ans_t* dns_get_answer_from_cache (dns_ctx_t* dc, const qse_mchar_t* name) +{ + dns_ans_t* prv, * cur; + qse_size_t hid; + qse_ntime_t now; + + hid = hash_string(name) % QSE_COUNTOF(dc->anss); + + qse_gettime (&now); + + prv = QSE_NULL; + cur = dc->anss[hid]; + while (cur) + { + if (qse_mbscasecmp(cur->name, name) == 0) + { + if (cur->age + cur->ttl < now.sec) + { + /* entry expired. evict the entry from the cache */ + if (prv) prv->next = cur->next; + else dc->anss[hid] = cur->next; + qse_httpd_freemem (dc->httpd, cur); + break; + } + + return cur; + } + + prv = cur; + cur = cur->next; + } + + return QSE_NULL; +} + +static int dns_recv (qse_httpd_t* httpd, qse_httpd_dns_t* dns) +{ + dns_ctx_t* dc = (dns_ctx_t*)dns->ctx; + httpd_xtn_t* httpd_xtn; + + qse_skad_t fromaddr; + socklen_t fromlen; + + qse_uint8_t buf[DNS_MAX_MSG_LEN]; + qse_ssize_t len; + dns_hdr_t* hdr; + + qse_uint16_t id, qdcount, ancount, i; + qse_uint8_t* plptr; + qse_size_t pllen; + dns_qdtrail_t* qdtrail; + dns_antrail_t* antrail; + qse_uint16_t anlen; + + dns_req_t* req = QSE_NULL; + qse_uint16_t xid = QSE_COUNTOF(dc->reqs); + qse_nwad_t nwad; + qse_nwad_t* resolved_nwad = QSE_NULL; + int cache_ttl = 0; + +printf ("DNS_RECV....\n"); + + httpd_xtn = qse_httpd_getxtn (httpd); + + fromlen = QSE_SIZEOF(fromaddr); + len = recvfrom (dns->handle.i, buf, QSE_SIZEOF(buf), 0, (struct sockaddr*)&fromaddr, &fromlen); + +/* TODO: check if fromaddr matches the dc->skad... */ + + if (len < QSE_SIZEOF(*hdr)) goto done; /* packet too small */ + + hdr = (dns_hdr_t*)buf; + qdcount = qse_ntoh16(hdr->qdcount); + + if (!hdr->qr || hdr->opcode != DNS_OPCODE_QUERY || qdcount <= 0) + { + /* not a response to a query */ + goto done; + } + + ancount = qse_ntoh16(hdr->ancount); + id = qse_ntoh16(hdr->id); + xid = (id >= DNS_SEQ_RANGE_SIZE)? (id - DNS_SEQ_RANGE_SIZE): id; + xid = xid % QSE_COUNTOF(dc->reqs); + + plptr = (qse_uint8_t*)(hdr + 1); + pllen = len - QSE_SIZEOF(*hdr); + + /* inspect the question section */ + for (i = 0; i < qdcount; i++) + { + qse_size_t reclen; + qse_uint8_t dnlen; + + dnlen = dn_length (plptr, pllen); + if (dnlen <= 0) goto done; /* invalid dn name */ + + reclen = dnlen + QSE_SIZEOF(dns_qdtrail_t); + if (pllen < reclen) goto done; /* weird packet */ + + if (!req) + { + qdtrail = (dns_qdtrail_t*)(plptr + dnlen); + + if (qdtrail->qclass == dc->n_qcin && + (qdtrail->qtype == dc->n_qta || qdtrail->qtype == dc->n_qtaaaa)) + { + for (req = dc->reqs[xid]; req; req = req->next) + { + if ((id == req->seqa || id == req->seqaaaa) && + req->dnlen == dnlen && QSE_MEMCMP (req->dn, plptr, req->dnlen) == 0) + { + /* found a match. note that the test here is a bit loose + * in that it doesn't really check if the original question + * was A or AAAA. it is possible that it can process an AAAA answer + * for an A question and vice versa. i don't care if someone + * exploits this and sends a fake response*/ + break; + } + } + } + } + + plptr += reclen; + pllen -= reclen; + } + + if (!req) goto done; /* no matching request for the question */ + if (hdr->rcode != DNS_RCODE_NOERROR || ancount <= 0) goto done; /* no good answers */ + + /* inspect the answer section */ + for (i = 0; i < ancount; i++) + { + qse_size_t reclen; + qse_uint8_t dnlen; + + if (pllen < 1) goto done; /* weird length */ + + if (*plptr > 63) dnlen = 2; + else + { + dnlen = dn_length (plptr, pllen); + if (dnlen <= 0) return 0; /* invalid dn name */ + } + + reclen = dnlen + QSE_SIZEOF(dns_antrail_t); + if (pllen < reclen) goto done; + + antrail = (dns_antrail_t*)(plptr + dnlen); + reclen += qse_ntoh16(antrail->dlen); + if (pllen < reclen) goto done; + + anlen = qse_ntoh16(antrail->dlen); + + if (antrail->qclass == dc->n_qcin) + { + nwad.type = QSE_NWAD_NX; + + if (antrail->qtype == dc->n_qta && anlen == 4) + { + QSE_MEMSET (&nwad, 0, QSE_SIZEOF(nwad)); + nwad.type = QSE_NWAD_IN4; + QSE_MEMCPY (&nwad.u.in4.addr, antrail + 1, 4); + } + else if (antrail->qtype == dc->n_qtaaaa && anlen == 16) + { + QSE_MEMSET (&nwad, 0, QSE_SIZEOF(nwad)); + nwad.type = QSE_NWAD_IN6; + QSE_MEMCPY (&nwad.u.in6.addr, antrail + 1, 16); + } + + if (nwad.type != QSE_NWAD_NX) + { + cache_ttl = httpd_xtn->dns.cache_ttl; + if (cache_ttl > qse_ntoh32(antrail->ttl)) cache_ttl = qse_ntoh32(antrail->ttl); + if (cache_ttl < httpd_xtn->dns.cache_minttl) cache_ttl = httpd_xtn->dns.cache_minttl; + + resolved_nwad = &nwad; + goto resolved; + } + } + + plptr += reclen; + pllen -= reclen; + } + + /* no good answer have been found */ + if (id == req->seqa) req->flags |= DNS_REQ_A_NX; + else if (id == req->seqaaaa) req->flags |= DNS_REQ_AAAA_NX; + + if ((req->flags & (DNS_REQ_A_NX | DNS_REQ_AAAA_NX)) == (DNS_REQ_A_NX | DNS_REQ_AAAA_NX)) + { + /* both ipv4 and ipv6 address are unresolvable */ + cache_ttl = httpd_xtn->dns.cache_negttl; + resolved_nwad = QSE_NULL; + goto resolved; + } + +done: + /* is there anything to do here? */ + return 0; + +resolved: + QSE_ASSERT (req != QSE_NULL); + QSE_ASSERT (xid >= 0 && xid < QSE_COUNTOF(dc->reqs)); + + dns_remove_tmr_tmout (req); + req->resol (httpd, req->name, resolved_nwad, req->ctx); + + /* detach the request off dc->reqs */ + if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; + else req->prev->next = req->next; + if (req->next) req->next->prev = req->prev; + + /* cache the negative answer instead of destroying it */ + dns_cache_answer (dc, req, resolved_nwad, cache_ttl); + dc->req_count--; + + return 0; +} + +static void tmr_dns_tmout_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse_tmr_index_t new_index, void* ctx) +{ + dns_req_t* req = (dns_req_t*)ctx; + +printf (">>tmr_dns_tmout_updated req->>%p\n", req); +printf (">>tmr_dns_tmout_updated existing->%d, old->%d new->%d\n", (int)req->tmr_tmout, (int)old_index, (int)new_index); + QSE_ASSERT (req->tmr_tmout == old_index); + req->tmr_tmout = new_index; +} + +static void tmr_dns_tmout_handle (qse_tmr_t* tmr, const qse_ntime_t* now, void* ctx) +{ + /* destory the unanswered request if timed out */ + + dns_req_t* req = (dns_req_t*)ctx; + dns_ctx_t* dc = req->dc; + qse_uint16_t xid; + +printf (">>tmr_dns_tmout_handle req->>%p\n", req); + /* when this handler is called, the event must be removed from the timer */ + QSE_ASSERT (req->tmr_tmout == QSE_TMR_INVALID_INDEX); + + /* --------------------------------------------------------------- + * resend + *---------------------------------------------------------------- */ + if (req->resends > 0) + { + httpd_xtn_t* httpd_xtn; + qse_tmr_event_t tmout_event; + + httpd_xtn = qse_httpd_getxtn (dc->httpd); + + qse_gettime (&tmout_event.when); + qse_addtime (&tmout_event.when, &httpd_xtn->dns.tmout, &tmout_event.when); + tmout_event.ctx = req; + tmout_event.handler = tmr_dns_tmout_handle; + tmout_event.updater = tmr_dns_tmout_update; + + if ((!(req->flags & DNS_REQ_A_NX) && req->qalen > 0 && sendto (dc->dns->handle.i, req->qa, req->qalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qalen) || + (!(req->flags & DNS_REQ_AAAA_NX) && req->qaaaalen > 0 && sendto (dc->dns->handle.i, req->qaaaa, req->qaaaalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qaaaalen)) + { + /* resend failed. fall thru and destroy the request*/ + } + else + { + QSE_ASSERT (tmr == dc->httpd->tmr); + if (qse_httpd_inserttimerevent (dc->httpd, &tmout_event, &req->tmr_tmout) >= 0) + { + req->resends--; + return; /* resend ok */ + } + } + } + + /* --------------------------------------------------------------- + * dns timed out + no resend + *---------------------------------------------------------------- */ + + /* it's safe to use req->seqa to find the hash index + * because seqa is always set regardless of A or AAAA */ + xid = req->seqa % QSE_COUNTOF(dc->reqs); + + /* detach the request off dc->reqs */ + if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; + else req->prev->next = req->next; + if (req->next) req->next->prev = req->prev; + + /* dns timed out. report that name resolution failed */ + req->resol (dc->httpd, req->name, QSE_NULL, req->ctx); + + /* i don't cache the items that have timed out */ + qse_httpd_freemem (dc->httpd, req); + + /* decrement the number of pending requests */ + dc->req_count--; +} + +static int dns_send (qse_httpd_t* httpd, qse_httpd_dns_t* dns, const qse_mchar_t* name, qse_httpd_resol_t resol, void* ctx) +{ + dns_ctx_t* dc = (dns_ctx_t*)dns->ctx; + httpd_xtn_t* httpd_xtn; + + qse_uint32_t seq; + qse_uint16_t xid; + dns_req_t* req = QSE_NULL; + qse_size_t name_len; + dns_ans_t* ans; + qse_tmr_event_t tmout_event; + + httpd_xtn = qse_httpd_getxtn (httpd); + +printf ("DNS REALLY SENING>>>>>>>>>>>>>>>>>>>>>>>\n"); + + ans = dns_get_answer_from_cache (dc, name); + if (ans) + { + resol (httpd, name, ((ans->nwad.type == QSE_NWAD_NX)? QSE_NULL: &ans->nwad), ctx); + return 0; + } + + if (dc->req_count >= QSE_COUNTOF(dc->reqs)) + { + /* too many pending requests */ + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOBUF); + goto oops; + } + + seq = ((qse_uint32_t)dc->seq + 1) % DNS_SEQ_RANGE_SIZE; + dc->seq = seq; + + xid = seq % QSE_COUNTOF(dc->reqs); + + name_len = qse_mbslen(name); + + /* dn is at most as long as the source length + 2. + * a.bb.ccc => 1a2bb3ccc0 => +2 + * a.bb.ccc. => 1a2bb3ccc0 => +1 */ + req = qse_httpd_callocmem (httpd, QSE_SIZEOF(*req) + (name_len + 1) + (name_len + 2)); + if (req == QSE_NULL) goto oops; + + req->tmr_tmout = QSE_TMR_INVALID_INDEX; + + /* seqa is between 0 and DNS_SEQ_RANGE_SIZE - 1 inclusive. + * seqaaaa is between DNS_SEQ_RANGE_SIZE and DNS_SEQ_RANGE_SIZE * 2 - 1 inclusive. */ + req->seqa = seq; + req->seqaaaa = seq + DNS_SEQ_RANGE_SIZE; /* this must not go beyond QSE_TYPE_MAX(qse_uint16_t) */ + req->name = (qse_mchar_t*)(req + 1); + req->dn = (qse_uint8_t*)(req->name + name_len + 1); + + qse_mbscpy (req->name, name); + req->dnlen = to_dn (name, req->dn, name_len + 2); + if (req->dnlen <= 0) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); + goto oops; + } + req->resol = resol; + req->ctx = ctx; + + if (!(httpd->opt.trait & QSE_HTTPD_DNSNOA)) + req->qalen = init_dns_query (req->qa, QSE_SIZEOF(req->qa), name, DNS_QTYPE_A, req->seqa); + else + req->flags |= DNS_REQ_A_NX; + + if (!(httpd->opt.trait & QSE_HTTPD_DNSNOAAAA)) + req->qaaaalen = init_dns_query (req->qaaaa, QSE_SIZEOF(req->qaaaa), name, DNS_QTYPE_AAAA, req->seqaaaa); + else + req->flags |= DNS_REQ_AAAA_NX; + + if (req->qalen <= -1 || req->qaaaalen <= -1) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); + goto oops; + } + + req->resends = httpd_xtn->dns.resends; + + qse_gettime (&tmout_event.when); + qse_addtime (&tmout_event.when, &httpd_xtn->dns.tmout, &tmout_event.when); + tmout_event.ctx = req; + tmout_event.handler = tmr_dns_tmout_handle; + tmout_event.updater = tmr_dns_tmout_update; + if (qse_httpd_inserttimerevent (httpd, &tmout_event, &req->tmr_tmout) <= -1) goto oops; + + if ((req->qalen > 0 && sendto (dns->handle.i, req->qa, req->qalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qalen) || + (req->qaaaalen > 0 && sendto (dns->handle.i, req->qaaaa, req->qaaaalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qaaaalen)) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } + + /* NOTE: + * if the sequence number is repeated before it timed out or resolved, + * the newer request gets chained together with the older one. + * it may not be so easy to determine which request to match an incoming + * response. + */ + req->dc = dc; + + /* link the request to the front of the chain */ + if (dc->reqs[xid]) dc->reqs[xid]->prev = req; + req->next = dc->reqs[xid]; + dc->reqs[xid] = req; + + /* increment the number of pending requests */ + dc->req_count++; + +printf ("DNS REALLY SENT>>>>>>>>>>>>>>>>>>>>>>>\n"); + return 0; + +oops: + if (req) + { + dns_remove_tmr_tmout (req); + qse_httpd_freemem (httpd, req); + } + return -1; +} diff --git a/qse/lib/http/httpd-std-urs.h b/qse/lib/http/httpd-std-urs.h new file mode 100644 index 00000000..faeccb3e --- /dev/null +++ b/qse/lib/http/httpd-std-urs.h @@ -0,0 +1,411 @@ +/* + * $Id$ + * + Copyright 2006-2014 Chung, Hyung-Hwan. + This file is part of QSE. + + QSE is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + QSE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with QSE. If not, see . + */ + +/* + * This file holds url rewriting support code and is included by httpd-std.c + */ + +#define URS_SEQ_RANGE_SIZE (QSE_TYPE_MAX(qse_uint16_t) - 2) +#define URS_MAX_URL_LEN 50000 + +typedef struct urs_hdr_t urs_hdr_t; +typedef struct urs_pkt_t urs_pkt_t; +typedef struct urs_ctx_t urs_ctx_t; +typedef struct urs_req_t urs_req_t; + +#include +struct urs_hdr_t +{ + qse_uint16_t seq; /* in network-byte order */ + qse_uint16_t rcode; /* response code */ + qse_uint32_t qusum;/* checksum of url in the request */ + qse_uint16_t len; /* url length in network-byte order */ +}; + +struct urs_pkt_t +{ + struct urs_hdr_t hdr; + qse_mchar_t url[1]; +}; +#include + +struct urs_ctx_t +{ + qse_httpd_t* httpd; + qse_httpd_urs_t* urs; + + qse_skad_t skad; + int skadlen; + + qse_uint16_t seq; /* TODO: change to uint32_t??? */ + urs_req_t* reqs[1024]; /* TOOD: choose the right size */ + qse_uint16_t req_count; + + qse_uint8_t rcvbuf[URS_MAX_URL_LEN + QSE_SIZEOF(urs_pkt_t)]; + qse_uint8_t fmtbuf[URS_MAX_URL_LEN + QSE_SIZEOF(urs_pkt_t)]; +}; + +struct urs_req_t +{ + qse_uint16_t seq; /* in host-byte order */ + qse_uint32_t pktlen; + urs_pkt_t* pkt; + + qse_httpd_rewrite_t rewrite; + void* ctx; + + urs_ctx_t* dc; + qse_tmr_index_t tmr_tmout; + int resends; + + urs_req_t* prev; + urs_req_t* next; +}; + + +static int urs_open (qse_httpd_t* httpd, qse_httpd_urs_t* urs) +{ +#if defined(__DOS__) + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); + return -1; +#else + sock_t fd = SOCK_INIT; + int flag; + qse_nwad_t nwad; + urs_ctx_t* dc; + httpd_xtn_t* httpd_xtn; + + httpd_xtn = qse_httpd_getxtn (httpd); + + dc = (urs_ctx_t*) qse_httpd_callocmem (httpd, QSE_SIZEOF(urs_ctx_t)); + if (dc == NULL) goto oops; + + dc->httpd = httpd; + dc->urs = urs; + + nwad = httpd_xtn->urs.nwad; + if (nwad.type == QSE_NWAD_NX) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); + goto oops; + } + + if (qse_getnwadport(&nwad) == 0) + qse_setnwadport (&nwad, qse_hton16(QSE_HTTPD_URSSTD_DEFAULT_PORT)); + + dc->skadlen = qse_nwadtoskad (&nwad, &dc->skad); + if (dc->skadlen <= -1) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); + goto oops; + } + + if (httpd->opt.trait & QSE_HTTPD_LOGACT) + { + qse_httpd_act_t msg; + qse_size_t pos; + msg.code = QSE_HTTPD_CATCH_MDBGMSG; + pos = qse_mbsxcpy (msg.u.mdbgmsg, QSE_COUNTOF(msg.u.mdbgmsg), "ursserver set to "); + qse_nwadtombs (&nwad, &msg.u.mdbgmsg[pos], QSE_COUNTOF(msg.u.mdbgmsg) - pos, QSE_NWADTOMBS_ALL); + httpd->opt.rcb.logact (httpd, &msg); + } + + fd = socket (qse_skadfamily(&dc->skad), SOCK_DGRAM, IPPROTO_UDP); + if (!is_valid_socket(fd)) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } + +/* TODO: set socket send/recv buffer size. it's needed as urs may be long */ + + #if defined(FD_CLOEXEC) + flag = fcntl (fd, F_GETFD); + if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); + #endif + + #if defined(SO_REUSEADDR) + flag = 1; + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, QSE_SIZEOF(flag)); + #endif + + #if defined(SO_REUSEPORT) + flag = 1; + setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (void*)&flag, QSE_SIZEOF(flag)); + #endif + + if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; + + urs->handle.i = fd; + urs->ctx = dc; + return 0; + +oops: + if (is_valid_socket(fd)) close_socket (fd); + if (dc) qse_httpd_freemem (httpd, dc); + return -1; + +#endif +} + +static void urs_remove_tmr_tmout (urs_req_t* req) +{ + if (req->tmr_tmout != QSE_TMR_INVALID_INDEX) + { + qse_httpd_removetimerevent (req->dc->httpd, req->tmr_tmout); + req->tmr_tmout = QSE_TMR_INVALID_INDEX; + } +} + +static void urs_close (qse_httpd_t* httpd, qse_httpd_urs_t* urs) +{ + urs_ctx_t* dc = (urs_ctx_t*)urs->ctx; + qse_size_t i; + + for (i = 0; i < QSE_COUNTOF(dc->reqs); i++) + { + urs_req_t* next_req; + while (dc->reqs[i]) + { + next_req = dc->reqs[i]->next; + urs_remove_tmr_tmout (dc->reqs[i]); + qse_httpd_freemem (httpd, dc->reqs[i]); + dc->reqs[i] = next_req; + dc->req_count--; + } + } + + QSE_ASSERT (dc->req_count == 0); + + close_socket (urs->handle.i); + qse_httpd_freemem (httpd, urs->ctx); +} + + +static int urs_recv (qse_httpd_t* httpd, qse_httpd_urs_t* urs) +{ + urs_ctx_t* dc = (urs_ctx_t*)urs->ctx; + httpd_xtn_t* httpd_xtn; + + qse_skad_t fromaddr; + socklen_t fromlen; + + qse_uint16_t xid; + qse_ssize_t len; + urs_pkt_t* pkt; + urs_req_t* req; + +printf ("URS_RECV....\n"); + + httpd_xtn = qse_httpd_getxtn (httpd); + + fromlen = QSE_SIZEOF(fromaddr); + len = recvfrom (urs->handle.i, dc->rcvbuf, QSE_SIZEOF(dc->rcvbuf) - 1, 0, (struct sockaddr*)&fromaddr, &fromlen); + +/* TODO: check if fromaddr matches the dc->skad... */ + + pkt = (urs_pkt_t*)dc->rcvbuf; + if (len >= QSE_SIZEOF(pkt->hdr) && len >= QSE_SIZEOF(pkt->hdr) + qse_ntoh16(pkt->hdr.len)) + { + xid = qse_ntoh16(pkt->hdr.seq) % QSE_COUNTOF(dc->reqs); + + for (req = dc->reqs[xid]; req; req = req->next) + { + if (req->pkt->hdr.seq == pkt->hdr.seq && req->pkt->hdr.qusum == pkt->hdr.qusum) + { + pkt->url[qse_ntoh16(pkt->hdr.len)] = QSE_MT('\0'); + + urs_remove_tmr_tmout (req); + req->rewrite (httpd, req->pkt->url, pkt->url, req->ctx); + + /* detach the request off dc->reqs */ + if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; + else req->prev->next = req->next; + if (req->next) req->next->prev = req->prev; + + qse_httpd_freemem (httpd, req); + dc->req_count--; + + break; + } + } + } + + return 0; +} + +static void tmr_urs_tmout_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse_tmr_index_t new_index, void* ctx) +{ + urs_req_t* req = (urs_req_t*)ctx; + +printf (">>tmr_urs_tmout_updated existing=%d old=%d new=%d\n", (int)req->tmr_tmout, (int)old_index, (int)new_index); + QSE_ASSERT (req->tmr_tmout == old_index); + req->tmr_tmout = new_index; +} + +static void tmr_urs_tmout_handle (qse_tmr_t* tmr, const qse_ntime_t* now, void* ctx) +{ + /* destory the unanswered request if timed out */ + + urs_req_t* req = (urs_req_t*)ctx; + urs_ctx_t* dc = req->dc; + qse_uint16_t xid; + + /* when this handler is called, the event should be removed from the timer */ + QSE_ASSERT (req->tmr_tmout == QSE_TMR_INVALID_INDEX); + + /* --------------------------------------------------------------- + * resend + *---------------------------------------------------------------- */ + if (req->resends > 0) + { + httpd_xtn_t* httpd_xtn; + qse_tmr_event_t tmout_event; + + httpd_xtn = qse_httpd_getxtn (dc->httpd); + + qse_gettime (&tmout_event.when); + qse_addtime (&tmout_event.when, &httpd_xtn->urs.tmout, &tmout_event.when); + tmout_event.ctx = req; + tmout_event.handler = tmr_urs_tmout_handle; + tmout_event.updater = tmr_urs_tmout_update; + + if (sendto (dc->urs->handle.i, req->pkt, req->pktlen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->pktlen) + { + /* error. fall thru */ + } + else + { + QSE_ASSERT (tmr == dc->httpd->tmr); + if (qse_httpd_inserttimerevent (dc->httpd, &tmout_event, &req->tmr_tmout) >= 0) + { + req->resends--; + return; /* resend ok */ + } + } + } + +printf ("urs timed out....\n"); + /* --------------------------------------------------------------- + * timed out + no resend + *---------------------------------------------------------------- */ + xid = req->seq % QSE_COUNTOF(dc->reqs); + + /* detach the request off dc->reqs */ + if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; + else req->prev->next = req->next; + if (req->next) req->next->prev = req->prev; + + /* urs timed out. report that name resolution failed */ + req->rewrite (dc->httpd, req->pkt->url, QSE_NULL, req->ctx); + + /* i don't cache the items that have timed out */ + qse_httpd_freemem (dc->httpd, req); + dc->req_count--; +} + +static int urs_send (qse_httpd_t* httpd, qse_httpd_urs_t* urs, const qse_mchar_t* url, qse_httpd_rewrite_t rewrite, void* ctx) +{ + urs_ctx_t* dc = (urs_ctx_t*)urs->ctx; + httpd_xtn_t* httpd_xtn; + + qse_uint16_t xid; + qse_uint32_t seq; + urs_req_t* req = QSE_NULL; + qse_size_t url_len; + qse_tmr_event_t tmout_event; + + httpd_xtn = qse_httpd_getxtn (httpd); + +printf ("URS REALLY SENING>>>>>>>>>>>>>>>>>>>>>>>\n"); + + if (dc->req_count >= QSE_COUNTOF(dc->reqs)) + { + /* too many pending requests */ + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOBUF); + goto oops; + } + + url_len = qse_mbslen(url); + if (url_len > URS_MAX_URL_LEN) /* TODO: change the limit */ + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); + goto oops; + } + + seq = ((qse_uint32_t)dc->seq + 1) % URS_SEQ_RANGE_SIZE; + dc->seq = seq; + + xid = seq % QSE_COUNTOF(dc->reqs); + + req = qse_httpd_callocmem (httpd, QSE_SIZEOF(*req) + url_len + QSE_SIZEOF(urs_pkt_t)); + if (req == QSE_NULL) goto oops; + + req->tmr_tmout = QSE_TMR_INVALID_INDEX; + req->seq = seq; + req->pkt = (urs_pkt_t*)(req + 1); + + req->pkt->hdr.seq = qse_hton16(seq); + req->pkt->hdr.len = qse_hton16(url_len); + req->pkt->hdr.qusum = hash_string (url); + qse_mbscpy (req->pkt->url, url); + + /* -1 to exclude the terminating '\0' as urs_pkt_t has url[1]. */ + req->pktlen = QSE_SIZEOF(urs_pkt_t) + url_len - 1; + + req->rewrite = rewrite; + req->ctx = ctx; + req->resends = httpd_xtn->urs.resends; + + qse_gettime (&tmout_event.when); + qse_addtime (&tmout_event.when, &httpd_xtn->urs.tmout, &tmout_event.when); + tmout_event.ctx = req; + tmout_event.handler = tmr_urs_tmout_handle; + tmout_event.updater = tmr_urs_tmout_update; + if (qse_httpd_inserttimerevent (httpd, &tmout_event, &req->tmr_tmout) <= -1) goto oops; + + if (sendto (urs->handle.i, req->pkt, req->pktlen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->pktlen) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } + + req->dc = dc; + + /* link the request to the front of the chain */ + if (dc->reqs[xid]) dc->reqs[xid]->prev = req; + req->next = dc->reqs[xid]; + dc->reqs[xid] = req; + + /* increment the number of pending requests */ + dc->req_count++; + +printf ("URS REALLY SENT>>>>>>>>>>>>>>>>>>>>>>>\n"); + return 0; + +oops: + if (req) + { + urs_remove_tmr_tmout (req); + qse_httpd_freemem (httpd, req); + } + return -1; +} + diff --git a/qse/lib/http/httpd-std.c b/qse/lib/http/httpd-std.c index 4d8d66c5..8e90f980 100644 --- a/qse/lib/http/httpd-std.c +++ b/qse/lib/http/httpd-std.c @@ -334,42 +334,39 @@ static qse_httpd_errnum_t fioerr_to_errnum (qse_fio_errnum_t e) static qse_httpd_errnum_t direrr_to_errnum (qse_dir_errnum_t e) { - switch (e) - { - case QSE_DIR_ENOMEM: - return QSE_HTTPD_ENOMEM; + switch (e) + { + case QSE_DIR_ENOMEM: + return QSE_HTTPD_ENOMEM; - case QSE_DIR_EINVAL: - return QSE_HTTPD_EINVAL; + case QSE_DIR_EINVAL: + return QSE_HTTPD_EINVAL; - case QSE_DIR_EACCES: - return QSE_HTTPD_EACCES; + case QSE_DIR_EACCES: + return QSE_HTTPD_EACCES; - case QSE_DIR_ENOENT: - return QSE_HTTPD_ENOENT; + case QSE_DIR_ENOENT: + return QSE_HTTPD_ENOENT; - case QSE_DIR_EEXIST: - return QSE_HTTPD_EEXIST; + case QSE_DIR_EEXIST: + return QSE_HTTPD_EEXIST; - case QSE_DIR_EINTR: - return QSE_HTTPD_EINTR; + case QSE_DIR_EINTR: + return QSE_HTTPD_EINTR; - case QSE_DIR_EPIPE: - return QSE_HTTPD_EPIPE; + case QSE_DIR_EPIPE: + return QSE_HTTPD_EPIPE; - case QSE_DIR_EAGAIN: - return QSE_HTTPD_EAGAIN; + case QSE_DIR_EAGAIN: + return QSE_HTTPD_EAGAIN; - default: - return QSE_HTTPD_ESYSERR; - } + default: + return QSE_HTTPD_ESYSERR; + } } - - /* ------------------------------------------------------------------- */ - static QSE_INLINE qse_ssize_t __send_file ( qse_httpd_t* httpd, int out_fd, qse_ubi_t in_fd, qse_foff_t* offset, qse_size_t count) @@ -2010,1321 +2007,6 @@ static void client_closed (qse_httpd_t* httpd, qse_httpd_client_t* client) } } -/* ------------------------------------------------------------------- */ - -static qse_size_t hash_string (const qse_mchar_t *str) -{ - qse_size_t h = 0; - while (*str) h = ((h << 5) + h) ^ *str++; - return h; -} - -/* ------------------------------------------------------------------- */ -#define DNS_MAX_DN_LEN 255 /* full domain name length in binary form (i.e. 3xyz2eu0) */ -#define DNS_MAX_MSG_LEN 512 /* basic dns only. no EDNS0. so 512 at most */ - -#define DNS_MIN_TTL 10 -#define DNS_MAX_TTL 120 /* TODO: make these configurable... */ - -#define DNS_SEQ_RANGE_SIZE ((QSE_TYPE_MAX(qse_uint16_t) / 2) - 2) - -typedef struct dns_ctx_t dns_ctx_t; -typedef struct dns_req_t dns_req_t; -typedef struct dns_ans_t dns_ans_t; -typedef struct dns_hdr_t dns_hdr_t; -typedef struct dns_qdtrail_t dns_qdtrail_t; -typedef struct dns_antrail_t dns_antrail_t; - -enum -{ - DNS_OPCODE_QUERY = 0, - DNS_OPCODE_IQUERY = 1, - DNS_OPCODE_STATUS = 2, - DNS_OPCODE_NOTIFY = 4, - DNS_OPCODE_UPDATE = 5, - - DNS_RCODE_NOERROR = 0, - DNS_RCODE_FORMERR = 1, - DNS_RCODE_SERVFAIL = 2, - DNS_RCODE_NXDOMAIN = 3, - DNS_RCODE_NOTIMPL = 4, - DNS_RCODE_REFUSED = 5, - - DNS_QTYPE_A = 1, - DNS_QTYPE_NS = 2, - DNS_QTYPE_CNAME = 5, - DNS_QTYPE_SOA = 6, - DNS_QTYPE_PTR = 12, - DNS_QTYPE_MX = 15, - DNS_QTYPE_TXT = 16, - DNS_QTYPE_AAAA = 28, - DNS_QTYPE_OPT = 41, - DNS_QTYPE_ANY = 255, - - DNS_QCLASS_IN = 1, /* internet */ - DNS_QCLASS_CH = 3, /* chaos */ - DNS_QCLASS_HS = 4, /* hesiod */ - DNS_QCLASS_NONE = 254, - DNS_QCLASS_ANY = 255 -}; - -#include -struct dns_hdr_t -{ - qse_uint16_t id; - -#if defined(QSE_ENDIAN_BIG) - qse_uint16_t qr: 1; /* question or response */ - qse_uint16_t opcode: 4; - qse_uint16_t aa: 1; /* authoritative answer */ - qse_uint16_t tc: 1; /* truncated message */ - qse_uint16_t rd: 1; /* recursion desired */ - - qse_uint16_t ra: 1; /* recursion available */ - qse_uint16_t z: 1; - qse_uint16_t ad: 1; - qse_uint16_t cd: 1; - qse_uint16_t rcode: 4; -#else - qse_uint16_t rd: 1; - qse_uint16_t tc: 1; - qse_uint16_t aa: 1; - qse_uint16_t opcode: 4; - qse_uint16_t qr: 1; - - qse_uint16_t rcode: 4; - qse_uint16_t cd: 1; - qse_uint16_t ad: 1; - qse_uint16_t z: 1; - qse_uint16_t ra: 1; -#endif - - qse_uint16_t qdcount; /* questions */ - qse_uint16_t ancount; /* answers */ - qse_uint16_t nscount; /* name servers */ - qse_uint16_t arcount; /* additional resource */ -}; - -struct dns_qdtrail_t -{ - qse_uint16_t qtype; - qse_uint16_t qclass; -}; - -struct dns_antrail_t -{ - qse_uint16_t qtype; - qse_uint16_t qclass; - qse_uint32_t ttl; - qse_uint16_t dlen; /* data length */ -}; -#include - -struct dns_ctx_t -{ - qse_httpd_t* httpd; - qse_httpd_dns_t* dns; - - qse_skad_t skad; - int skadlen; - - qse_uint16_t seq; - dns_req_t* reqs[2048]; /* TOOD: choose the right size or make it configurable. must be < DNS_SEQ_RANGE_SIZE */ - dns_ans_t* anss[2048]; - qse_uint16_t req_count; /* the number of pending requests */ - - qse_uint16_t n_qcin; /* DNS_QCLASS_IN in network byte order */ - qse_uint16_t n_qta; /* DNS_QTYPE_A in network byte order */ - qse_uint16_t n_qtaaaa; /* DNS_QTYPE_AAAA in network byte order */ -}; - -struct dns_req_t -{ - qse_mchar_t* name; - -#define DNS_REQ_A_NX (1 << 0) -#define DNS_REQ_AAAA_NX (1 << 1) - int flags; - qse_uint16_t seqa, seqaaaa; - - qse_uint8_t* dn; - qse_size_t dnlen; - - qse_httpd_resol_t resol; - void* ctx; - - qse_uint8_t qa[DNS_MAX_DN_LEN + QSE_SIZEOF(dns_hdr_t) + QSE_SIZEOF(dns_qdtrail_t)]; - qse_uint8_t qaaaa[DNS_MAX_DN_LEN + QSE_SIZEOF(dns_hdr_t) + QSE_SIZEOF(dns_qdtrail_t)]; - int qalen; - int qaaaalen; - - dns_ctx_t* dc; - qse_size_t tmr_tmout; - int resends; - - dns_req_t* next; - dns_req_t* prev; -}; - -struct dns_ans_t -{ - /* the name part must be the same as dns_req_t */ - qse_mchar_t* name; - - /* the total size of data fields below must not be greater than - * the total size of data fields of dns_req_t excluding name. - * this condition is required for reusing the dns_req_t chunk - * when caching an answer without allocating another chunk. */ - qse_nwad_t nwad; - qse_int64_t age; - qse_uint32_t ttl; - dns_ans_t* next; -}; - -#define DN_AT_END(ptr) (ptr[0] == QSE_MT('\0') || (ptr[0] == QSE_MT('.') && ptr[1] == QSE_MT('\0'))) - -static qse_size_t to_dn (const qse_mchar_t* str, qse_uint8_t* buf, qse_size_t bufsz) -{ - qse_uint8_t* bp = buf, * be = buf + bufsz; - - QSE_ASSERT (QSE_SIZEOF(qse_uint8_t) == QSE_SIZEOF(qse_mchar_t)); - - if (!DN_AT_END(str)) - { - qse_uint8_t* lp; - qse_size_t len; - const qse_mchar_t* seg; - const qse_mchar_t* cur = str - 1; - - do - { - if (bp < be) lp = bp++; - else lp = QSE_NULL; - - seg = ++cur; - while (*cur != QSE_MT('\0') && *cur != QSE_MT('.')) - { - if (bp < be) *bp++ = *cur; - cur++; - } - len = cur - seg; - if (len <= 0 || len > 63) return 0; - - if (lp) *lp = (qse_uint8_t)len; - } - while (!DN_AT_END(cur)); - } - - if (bp < be) *bp++ = 0; - return bp - buf; -} - -static qse_size_t dn_length (qse_uint8_t* ptr, qse_size_t len) -{ - qse_uint8_t* curptr; - qse_size_t curlen, seglen; - - curptr = ptr; - curlen = len; - - do - { - if (curlen <= 0) return 0; - - seglen = *curptr++; - curlen = curlen - 1; - if (seglen == 0) break; - else if (seglen > curlen || seglen > 63) return 0; - - curptr += seglen; - curlen -= seglen; - } - while (1); - - return curptr - ptr; -} - -int init_dns_query (qse_uint8_t* buf, qse_size_t len, const qse_mchar_t* name, int qtype, qse_uint16_t seq) -{ - dns_hdr_t* hdr; - dns_qdtrail_t* qdtrail; - qse_size_t x; - - if (len < QSE_SIZEOF(*hdr)) return -1; - - QSE_MEMSET (buf, 0, len); - hdr = (dns_hdr_t*)buf; - hdr->id = qse_hton16(seq); - hdr->opcode = DNS_OPCODE_QUERY; - hdr->rd = 1; /* recursion desired*/ - hdr->qdcount = qse_hton16(1); /* 1 question */ - - len -= QSE_SIZEOF(*hdr); - - x = to_dn (name, (qse_uint8_t*)(hdr + 1), len); - if (x <= 0) return -1; - len -= x; - - if (len < QSE_SIZEOF(*qdtrail)) return -1; - qdtrail = (dns_qdtrail_t*)((qse_uint8_t*)(hdr + 1) + x); - - qdtrail->qtype = qse_hton16(qtype); - qdtrail->qclass = qse_hton16(DNS_QCLASS_IN); - return QSE_SIZEOF(*hdr) + x + QSE_SIZEOF(*qdtrail); -} - -static int dns_open (qse_httpd_t* httpd, qse_httpd_dns_t* dns) -{ -#if defined(__DOS__) - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); - return -1; -#else - sock_t fd = SOCK_INIT; - int flag; - qse_nwad_t nwad; - dns_ctx_t* dc; - httpd_xtn_t* httpd_xtn; - - httpd_xtn = qse_httpd_getxtn (httpd); - - dc = (dns_ctx_t*) qse_httpd_callocmem (httpd, QSE_SIZEOF(dns_ctx_t)); - if (dc == NULL) goto oops; - - dc->httpd = httpd; - dc->dns = dns; - dc->n_qcin = qse_hton16(DNS_QCLASS_IN); - dc->n_qta = qse_hton16(DNS_QTYPE_A); - dc->n_qtaaaa = qse_hton16(DNS_QTYPE_AAAA); - -/* TODO: add static cache entries from /etc/hosts */ - - nwad = httpd_xtn->dns.nwad; - if (nwad.type == QSE_NWAD_NX) - { - qse_sio_t* sio; - #if defined(_WIN32) - /* TODO: windns.h dnsapi.lib DnsQueryConfig... */ - #elif defined(__OS2__) - /* TODO: */ - #else - /* TODO: read /etc/resolv.conf???? */ - #endif - - sio = qse_sio_open (qse_httpd_getmmgr(httpd), 0, QSE_T("/etc/resolv.conf"), QSE_SIO_READ); - if (sio) - { - qse_mchar_t buf[128]; - qse_ssize_t len; - qse_mcstr_t tok; - qse_mchar_t* ptr; - qse_mchar_t* end; - - while (1) - { - len = qse_sio_getmbsn (sio, buf, QSE_COUNTOF(buf)); - if (len <= 0) break; - - end = buf + len; - ptr = buf; - - ptr = qse_mbsxtok (ptr, end - ptr, QSE_MT(" \t"), &tok); - if (ptr && qse_mbsxcmp (tok.ptr, tok.len, QSE_MT("nameserver")) == 0) - { - ptr = qse_mbsxtok (ptr, end - ptr, QSE_MT(" \t"), &tok); - if (qse_mbsntonwad (tok.ptr, tok.len, &nwad) >= 0) break; - } - } - qse_sio_close (sio); - } - } - - if (qse_getnwadport(&nwad) == 0) - qse_setnwadport (&nwad, qse_hton16(QSE_HTTPD_DNSSTD_DEFAULT_PORT)); - - dc->skadlen = qse_nwadtoskad (&nwad, &dc->skad); - if (dc->skadlen <= -1) - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); - goto oops; - } - - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - { - qse_httpd_act_t msg; - qse_size_t pos; - msg.code = QSE_HTTPD_CATCH_MDBGMSG; - pos = qse_mbsxcpy (msg.u.mdbgmsg, QSE_COUNTOF(msg.u.mdbgmsg), "nameserver set to "); - qse_nwadtombs (&nwad, &msg.u.mdbgmsg[pos], QSE_COUNTOF(msg.u.mdbgmsg) - pos, QSE_NWADTOMBS_ALL); - httpd->opt.rcb.logact (httpd, &msg); - } - - fd = socket (qse_skadfamily(&dc->skad), SOCK_DGRAM, IPPROTO_UDP); - if (!is_valid_socket(fd)) - { - qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); - goto oops; - } - - #if defined(FD_CLOEXEC) - flag = fcntl (fd, F_GETFD); - if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); - #endif - - #if defined(SO_REUSEADDR) - flag = 1; - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, QSE_SIZEOF(flag)); - #endif - - #if defined(SO_REUSEPORT) - flag = 1; - setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (void*)&flag, QSE_SIZEOF(flag)); - #endif - - if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; - - dns->handle.i = fd; - dns->ctx = dc; - - return 0; - -oops: - if (is_valid_socket(fd)) close_socket (fd); - if (dc) qse_httpd_freemem (httpd, dc); - return -1; - -#endif -} - -static void dns_remove_tmr_tmout (dns_req_t* req) -{ - if (req->tmr_tmout != QSE_TMR_INVALID) - { - qse_tmr_remove (req->dc->httpd->tmr, req->tmr_tmout); - req->tmr_tmout = QSE_TMR_INVALID; - } -} - -static void dns_close (qse_httpd_t* httpd, qse_httpd_dns_t* dns) -{ - dns_ctx_t* dc = (dns_ctx_t*)dns->ctx; - qse_size_t i; - - for (i = 0; i < QSE_COUNTOF(dc->reqs); i++) - { - dns_req_t* next_req; - while (dc->reqs[i]) - { - next_req = dc->reqs[i]->next; - dns_remove_tmr_tmout (dc->reqs[i]); - qse_httpd_freemem (httpd, dc->reqs[i]); - dc->reqs[i] = next_req; - dc->req_count--; - } - } - - QSE_ASSERT (dc->req_count == 0); - - for (i = 0; i < QSE_COUNTOF(dc->anss); i++) - { - dns_ans_t* next_ans; - while (dc->anss[i]) - { - next_ans = dc->anss[i]->next; - qse_httpd_freemem (httpd, dc->anss[i]); - dc->anss[i] = next_ans; - } - } - - close_socket (dns->handle.i); - qse_httpd_freemem (httpd, dns->ctx); -} - - - -static void dns_cache_answer (dns_ctx_t* dc, dns_req_t* req, const qse_nwad_t* nwad, qse_uint32_t ttl) -{ - dns_ans_t* ans, * prv, * cur; - qse_size_t hid; - qse_ntime_t now; - -/* TODO: implement the maximum number of entries in cache... */ - - /* i use the given request as a space to hold an answer. - * the following assertion must be met for this to work */ - QSE_ASSERT (QSE_SIZEOF(dns_req_t) >= QSE_SIZEOF(dns_ans_t)); - - qse_gettime (&now); - - ans = (dns_ans_t*)req; /* shadow the request with an answer */ - - /* reuse the data fields of the request except the name field. - * from here downwards, the data fields of the request are invalid. */ - - if (nwad) ans->nwad = *nwad; /* positive */ - else ans->nwad.type = QSE_NWAD_NX; /* negative */ - ans->age = now.sec; /* the granularity of a second should be good enough */ - - if (ttl < DNS_MIN_TTL) ttl = DNS_MIN_TTL; /* TODO: use configured value */ - else if (ttl > DNS_MAX_TTL) ttl = DNS_MAX_TTL; - - ans->ttl = ttl; - hid = hash_string (req->name) % QSE_COUNTOF(dc->anss); - - prv = QSE_NULL; - cur = dc->anss[hid]; - while (cur) - { - if (qse_mbscasecmp(cur->name, ans->name) == 0) - { - ans->next = cur->next; - if (prv) prv->next = ans; - else dc->anss[hid] = ans; - qse_httpd_freemem (dc->httpd, cur); - return; - } - - prv = cur; - cur = cur->next; - } - ans->next = dc->anss[hid]; - dc->anss[hid] = ans; -} - -static dns_ans_t* dns_get_answer_from_cache (dns_ctx_t* dc, const qse_mchar_t* name) -{ - dns_ans_t* prv, * cur; - qse_size_t hid; - qse_ntime_t now; - - hid = hash_string(name) % QSE_COUNTOF(dc->anss); - - qse_gettime (&now); - - prv = QSE_NULL; - cur = dc->anss[hid]; - while (cur) - { - if (qse_mbscasecmp(cur->name, name) == 0) - { - if (cur->age + cur->ttl < now.sec) - { - /* entry expired. evict the entry from the cache */ - if (prv) prv->next = cur->next; - else dc->anss[hid] = cur->next; - qse_httpd_freemem (dc->httpd, cur); - break; - } - - return cur; - } - - prv = cur; - cur = cur->next; - } - - return QSE_NULL; -} - -static int dns_recv (qse_httpd_t* httpd, qse_httpd_dns_t* dns) -{ - dns_ctx_t* dc = (dns_ctx_t*)dns->ctx; - httpd_xtn_t* httpd_xtn; - - qse_skad_t fromaddr; - socklen_t fromlen; - - qse_uint8_t buf[DNS_MAX_MSG_LEN]; - qse_ssize_t len; - dns_hdr_t* hdr; - - qse_uint16_t id, qdcount, ancount, i; - qse_uint8_t* plptr; - qse_size_t pllen; - dns_qdtrail_t* qdtrail; - dns_antrail_t* antrail; - qse_uint16_t anlen; - - dns_req_t* req = QSE_NULL; - qse_uint16_t xid = QSE_COUNTOF(dc->reqs); - qse_nwad_t nwad; - qse_nwad_t* resolved_nwad = QSE_NULL; - int cache_ttl = 0; - -printf ("DNS_RECV....\n"); - - httpd_xtn = qse_httpd_getxtn (httpd); - - fromlen = QSE_SIZEOF(fromaddr); - len = recvfrom (dns->handle.i, buf, QSE_SIZEOF(buf), 0, (struct sockaddr*)&fromaddr, &fromlen); - -/* TODO: check if fromaddr matches the dc->skad... */ - - if (len < QSE_SIZEOF(*hdr)) goto done; /* packet too small */ - - hdr = (dns_hdr_t*)buf; - qdcount = qse_ntoh16(hdr->qdcount); - - if (!hdr->qr || hdr->opcode != DNS_OPCODE_QUERY || qdcount <= 0) - { - /* not a response to a query */ - goto done; - } - - ancount = qse_ntoh16(hdr->ancount); - id = qse_ntoh16(hdr->id); - xid = (id >= DNS_SEQ_RANGE_SIZE)? (id - DNS_SEQ_RANGE_SIZE): id; - xid = xid % QSE_COUNTOF(dc->reqs); - - plptr = (qse_uint8_t*)(hdr + 1); - pllen = len - QSE_SIZEOF(*hdr); - - /* inspect the question section */ - for (i = 0; i < qdcount; i++) - { - qse_size_t reclen; - qse_uint8_t dnlen; - - dnlen = dn_length (plptr, pllen); - if (dnlen <= 0) goto done; /* invalid dn name */ - - reclen = dnlen + QSE_SIZEOF(dns_qdtrail_t); - if (pllen < reclen) goto done; /* weird packet */ - - if (!req) - { - qdtrail = (dns_qdtrail_t*)(plptr + dnlen); - - if (qdtrail->qclass == dc->n_qcin && - (qdtrail->qtype == dc->n_qta || qdtrail->qtype == dc->n_qtaaaa)) - { - for (req = dc->reqs[xid]; req; req = req->next) - { - if ((id == req->seqa || id == req->seqaaaa) && - req->dnlen == dnlen && QSE_MEMCMP (req->dn, plptr, req->dnlen) == 0) - { - /* found a match. note that the test here is a bit loose - * in that it doesn't really check if the original question - * was A or AAAA. it is possible that it can process an AAAA answer - * for an A question and vice versa. i don't care if someone - * exploits this and sends a fake response*/ - break; - } - } - } - } - - plptr += reclen; - pllen -= reclen; - } - - if (!req) goto done; /* no matching request for the question */ - if (hdr->rcode != DNS_RCODE_NOERROR || ancount <= 0) goto done; /* no good answers */ - - /* inspect the answer section */ - for (i = 0; i < ancount; i++) - { - qse_size_t reclen; - qse_uint8_t dnlen; - - if (pllen < 1) goto done; /* weird length */ - - if (*plptr > 63) dnlen = 2; - else - { - dnlen = dn_length (plptr, pllen); - if (dnlen <= 0) return 0; /* invalid dn name */ - } - - reclen = dnlen + QSE_SIZEOF(dns_antrail_t); - if (pllen < reclen) goto done; - - antrail = (dns_antrail_t*)(plptr + dnlen); - reclen += qse_ntoh16(antrail->dlen); - if (pllen < reclen) goto done; - - anlen = qse_ntoh16(antrail->dlen); - - if (antrail->qclass == dc->n_qcin) - { - nwad.type = QSE_NWAD_NX; - - if (antrail->qtype == dc->n_qta && anlen == 4) - { - QSE_MEMSET (&nwad, 0, QSE_SIZEOF(nwad)); - nwad.type = QSE_NWAD_IN4; - QSE_MEMCPY (&nwad.u.in4.addr, antrail + 1, 4); - } - else if (antrail->qtype == dc->n_qtaaaa && anlen == 16) - { - QSE_MEMSET (&nwad, 0, QSE_SIZEOF(nwad)); - nwad.type = QSE_NWAD_IN6; - QSE_MEMCPY (&nwad.u.in6.addr, antrail + 1, 16); - } - - if (nwad.type != QSE_NWAD_NX) - { - cache_ttl = httpd_xtn->dns.cache_ttl; - if (cache_ttl > qse_ntoh32(antrail->ttl)) cache_ttl = qse_ntoh32(antrail->ttl); - if (cache_ttl < httpd_xtn->dns.cache_minttl) cache_ttl = httpd_xtn->dns.cache_minttl; - - resolved_nwad = &nwad; - goto resolved; - } - } - - plptr += reclen; - pllen -= reclen; - } - - /* no good answer have been found */ - if (id == req->seqa) req->flags |= DNS_REQ_A_NX; - else if (id == req->seqaaaa) req->flags |= DNS_REQ_AAAA_NX; - - if ((req->flags & (DNS_REQ_A_NX | DNS_REQ_AAAA_NX)) == (DNS_REQ_A_NX | DNS_REQ_AAAA_NX)) - { - /* both ipv4 and ipv6 address are unresolvable */ - cache_ttl = httpd_xtn->dns.cache_negttl; - resolved_nwad = QSE_NULL; - goto resolved; - } - -done: - /* is there anything to do here? */ - return 0; - -resolved: - QSE_ASSERT (req != QSE_NULL); - QSE_ASSERT (xid >= 0 && xid < QSE_COUNTOF(dc->reqs)); - - dns_remove_tmr_tmout (req); - req->resol (httpd, req->name, resolved_nwad, req->ctx); - - /* detach the request off dc->reqs */ - if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; - else req->prev->next = req->next; - if (req->next) req->next->prev = req->prev; - - /* cache the negative answer instead of destroying it */ - dns_cache_answer (dc, req, resolved_nwad, cache_ttl); - dc->req_count--; - - return 0; -} - -static void tmr_dns_tmout_update (qse_tmr_t* tmr, qse_size_t old_index, qse_size_t new_index, void* ctx) -{ - dns_req_t* req = (dns_req_t*)ctx; - -printf (">>tmr_dns_tmout_updated %d %d\n", (int)req->tmr_tmout, (int)old_index); - QSE_ASSERT (req->tmr_tmout == old_index); - req->tmr_tmout = new_index; -} - -static void tmr_dns_tmout_handle (qse_tmr_t* tmr, const qse_ntime_t* now, void* ctx) -{ - /* destory the unanswered request if timed out */ - - dns_req_t* req = (dns_req_t*)ctx; - dns_ctx_t* dc = req->dc; - qse_uint16_t xid; - - /* when this handler is called, the event must be removed from the timer */ - QSE_ASSERT (req->tmr_tmout == QSE_TMR_INVALID); - - /* --------------------------------------------------------------- - * resend - *---------------------------------------------------------------- */ - if (req->resends > 0) - { - httpd_xtn_t* httpd_xtn; - qse_tmr_event_t tmout_event; - - httpd_xtn = qse_httpd_getxtn (dc->httpd); - - qse_gettime (&tmout_event.when); - qse_addtime (&tmout_event.when, &httpd_xtn->dns.tmout, &tmout_event.when); - tmout_event.ctx = req; - tmout_event.handler = tmr_dns_tmout_handle; - tmout_event.updater = tmr_dns_tmout_update; - - if ((!(req->flags & DNS_REQ_A_NX) && req->qalen > 0 && sendto (dc->dns->handle.i, req->qa, req->qalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qalen) || - (!(req->flags & DNS_REQ_AAAA_NX) && req->qaaaalen > 0 && sendto (dc->dns->handle.i, req->qaaaa, req->qaaaalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qaaaalen)) - { - /* resend failed. fall thru and destroy the request*/ - } - else - { - req->tmr_tmout = qse_tmr_insert (dc->httpd->tmr, &tmout_event); - if (req->tmr_tmout != QSE_TMR_INVALID) - { - req->resends--; - return; /* resend ok */ - } - } - } - - /* --------------------------------------------------------------- - * dns timed out + no resend - *---------------------------------------------------------------- */ - - /* it's safe to use req->seqa to find the hash index - * because seqa is always set regardless of A or AAAA */ - xid = req->seqa % QSE_COUNTOF(dc->reqs); - - /* detach the request off dc->reqs */ - if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; - else req->prev->next = req->next; - if (req->next) req->next->prev = req->prev; - - /* dns timed out. report that name resolution failed */ - req->resol (dc->httpd, req->name, QSE_NULL, req->ctx); - - /* i don't cache the items that have timed out */ - qse_httpd_freemem (dc->httpd, req); - - /* decrement the number of pending requests */ - dc->req_count--; -} - -static int dns_send (qse_httpd_t* httpd, qse_httpd_dns_t* dns, const qse_mchar_t* name, qse_httpd_resol_t resol, void* ctx) -{ - dns_ctx_t* dc = (dns_ctx_t*)dns->ctx; - httpd_xtn_t* httpd_xtn; - - qse_uint32_t seq; - qse_uint16_t xid; - dns_req_t* req; - qse_size_t name_len; - dns_ans_t* ans; - qse_tmr_event_t tmout_event; - - httpd_xtn = qse_httpd_getxtn (httpd); - -printf ("DNS REALLY SENING>>>>>>>>>>>>>>>>>>>>>>>\n"); - - ans = dns_get_answer_from_cache (dc, name); - if (ans) - { - resol (httpd, name, ((ans->nwad.type == QSE_NWAD_NX)? QSE_NULL: &ans->nwad), ctx); - return 0; - } - - if (dc->req_count >= QSE_COUNTOF(dc->reqs)) - { - /* too many pending requests */ - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOBUF); - return -1; - } - - seq = ((qse_uint32_t)dc->seq + 1) % DNS_SEQ_RANGE_SIZE; - dc->seq = seq; - - xid = seq % QSE_COUNTOF(dc->reqs); - - name_len = qse_mbslen(name); - - /* dn is at most as long as the source length + 2. - * a.bb.ccc => 1a2bb3ccc0 => +2 - * a.bb.ccc. => 1a2bb3ccc0 => +1 */ - req = qse_httpd_callocmem (httpd, QSE_SIZEOF(*req) + (name_len + 1) + (name_len + 2)); - if (req == QSE_NULL) return -1; - - /* seqa is between 0 and DNS_SEQ_RANGE_SIZE - 1 inclusive. - * seqaaaa is between DNS_SEQ_RANGE_SIZE and DNS_SEQ_RANGE_SIZE * 2 - 1 inclusive. */ - req->seqa = seq; - req->seqaaaa = seq + DNS_SEQ_RANGE_SIZE; /* this must not go beyond QSE_TYPE_MAX(qse_uint16_t) */ - req->name = (qse_mchar_t*)(req + 1); - req->dn = (qse_uint8_t*)(req->name + name_len + 1); - - qse_mbscpy (req->name, name); - req->dnlen = to_dn (name, req->dn, name_len + 2); - if (req->dnlen <= 0) - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); - qse_httpd_freemem (httpd,req); - return -1; - } - req->resol = resol; - req->ctx = ctx; - - if (!(httpd->opt.trait & QSE_HTTPD_DNSNOA)) - req->qalen = init_dns_query (req->qa, QSE_SIZEOF(req->qa), name, DNS_QTYPE_A, req->seqa); - else - req->flags |= DNS_REQ_A_NX; - - if (!(httpd->opt.trait & QSE_HTTPD_DNSNOAAAA)) - req->qaaaalen = init_dns_query (req->qaaaa, QSE_SIZEOF(req->qaaaa), name, DNS_QTYPE_AAAA, req->seqaaaa); - else - req->flags |= DNS_REQ_AAAA_NX; - - if (req->qalen <= -1 || req->qaaaalen <= -1) - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); - qse_httpd_freemem (httpd,req); - return -1; - } - - qse_gettime (&tmout_event.when); - qse_addtime (&tmout_event.when, &httpd_xtn->dns.tmout, &tmout_event.when); - tmout_event.ctx = req; - tmout_event.handler = tmr_dns_tmout_handle; - tmout_event.updater = tmr_dns_tmout_update; - - req->tmr_tmout = qse_tmr_insert (httpd->tmr, &tmout_event); - if (req->tmr_tmout == QSE_TMR_INVALID) - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM); - qse_httpd_freemem (httpd, req); - return -1; - } - req->resends = httpd_xtn->dns.resends; - - if ((req->qalen > 0 && sendto (dns->handle.i, req->qa, req->qalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qalen) || - (req->qaaaalen > 0 && sendto (dns->handle.i, req->qaaaa, req->qaaaalen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->qaaaalen)) - { - qse_tmr_remove (httpd->tmr, req->tmr_tmout); - req->tmr_tmout = QSE_TMR_INVALID; - qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); - qse_httpd_freemem (httpd, req); - return -1; - } - - /* NOTE: - * if the sequence number is repeated before it timed out or resolved, - * the newer request gets chained together with the older one. - * it may not be so easy to determine which request to match an incoming - * response. - */ - req->dc = dc; - - /* link the request to the front of the chain */ - if (dc->reqs[xid]) dc->reqs[xid]->prev = req; - req->next = dc->reqs[xid]; - dc->reqs[xid] = req; - - /* increment the number of pending requests */ - dc->req_count++; - -printf ("DNS REALLY SENT>>>>>>>>>>>>>>>>>>>>>>>\n"); - return 0; -} - -/* ------------------------------------------------------------------- */ - -#define URS_SEQ_RANGE_SIZE (QSE_TYPE_MAX(qse_uint16_t) - 2) -#define URS_MAX_URL_LEN 50000 - -typedef struct urs_hdr_t urs_hdr_t; -typedef struct urs_pkt_t urs_pkt_t; -typedef struct urs_ctx_t urs_ctx_t; -typedef struct urs_req_t urs_req_t; - -#include -struct urs_hdr_t -{ - qse_uint16_t seq; /* in network-byte order */ - qse_uint16_t rcode; /* response code */ - qse_uint32_t qusum;/* checksum of url in the request */ - qse_uint16_t len; /* url length in network-byte order */ -}; - -struct urs_pkt_t -{ - struct urs_hdr_t hdr; - qse_mchar_t url[1]; -}; -#include - -struct urs_ctx_t -{ - qse_httpd_t* httpd; - qse_httpd_urs_t* urs; - - qse_skad_t skad; - int skadlen; - - qse_uint16_t seq; /* TODO: change to uint32_t??? */ - urs_req_t* reqs[1024]; /* TOOD: choose the right size */ - qse_uint16_t req_count; - - qse_uint8_t rcvbuf[URS_MAX_URL_LEN + QSE_SIZEOF(urs_pkt_t)]; -}; - -struct urs_req_t -{ - qse_uint16_t seq; /* in host-byte order */ - qse_uint32_t pktlen; - urs_pkt_t* pkt; - - qse_httpd_rewrite_t rewrite; - void* ctx; - - urs_ctx_t* dc; - qse_size_t tmr_tmout; - int resends; - - urs_req_t* prev; - urs_req_t* next; -}; - -#if 0 -int init_urs_query (qse_uint8_t* buf, qse_size_t len, const qse_mchar_t* name, qse_uint16_t seq) -{ - urs_hdr_t* hdr; - urs_qdtrail_t* qdtrail; - qse_size_t x; - - if (len < QSE_SIZEOF(*hdr)) return -1; - - QSE_MEMSET (buf, 0, len); - hdr = (urs_hdr_t*)buf; - hdr->id = qse_hton16(seq); - hdr->opcode = URS_OPCODE_QUERY; - hdr->rd = 1; /* recursion desired*/ - hdr->qdcount = qse_hton16(1); /* 1 question */ - - len -= QSE_SIZEOF(*hdr); - - x = to_dn (name, (qse_uint8_t*)(hdr + 1), len); - if (x <= 0) return -1; - len -= x; - - if (len < QSE_SIZEOF(*qdtrail)) return -1; - qdtrail = (urs_qdtrail_t*)((qse_uint8_t*)(hdr + 1) + x); - - qdtrail->qtype = qse_hton16(qtype); - qdtrail->qclass = qse_hton16(URS_QCLASS_IN); - return QSE_SIZEOF(*hdr) + x + QSE_SIZEOF(*qdtrail); -} -#endif - -static int urs_open (qse_httpd_t* httpd, qse_httpd_urs_t* urs) -{ -#if defined(__DOS__) - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); - return -1; -#else - sock_t fd = SOCK_INIT; - int flag; - qse_nwad_t nwad; - urs_ctx_t* dc; - httpd_xtn_t* httpd_xtn; - - httpd_xtn = qse_httpd_getxtn (httpd); - - dc = (urs_ctx_t*) qse_httpd_callocmem (httpd, QSE_SIZEOF(urs_ctx_t)); - if (dc == NULL) goto oops; - - dc->httpd = httpd; - dc->urs = urs; - - nwad = httpd_xtn->urs.nwad; - if (nwad.type == QSE_NWAD_NX) - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); - goto oops; - } - - if (qse_getnwadport(&nwad) == 0) - qse_setnwadport (&nwad, qse_hton16(QSE_HTTPD_URSSTD_DEFAULT_PORT)); - - dc->skadlen = qse_nwadtoskad (&nwad, &dc->skad); - if (dc->skadlen <= -1) - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); - goto oops; - } - - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - { - qse_httpd_act_t msg; - qse_size_t pos; - msg.code = QSE_HTTPD_CATCH_MDBGMSG; - pos = qse_mbsxcpy (msg.u.mdbgmsg, QSE_COUNTOF(msg.u.mdbgmsg), "ursserver set to "); - qse_nwadtombs (&nwad, &msg.u.mdbgmsg[pos], QSE_COUNTOF(msg.u.mdbgmsg) - pos, QSE_NWADTOMBS_ALL); - httpd->opt.rcb.logact (httpd, &msg); - } - - fd = socket (qse_skadfamily(&dc->skad), SOCK_DGRAM, IPPROTO_UDP); - if (!is_valid_socket(fd)) - { - qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); - goto oops; - } - -/* TODO: set socket send/recv buffer size. it's needed as urs may be long */ - - #if defined(FD_CLOEXEC) - flag = fcntl (fd, F_GETFD); - if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); - #endif - - #if defined(SO_REUSEADDR) - flag = 1; - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, QSE_SIZEOF(flag)); - #endif - - #if defined(SO_REUSEPORT) - flag = 1; - setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (void*)&flag, QSE_SIZEOF(flag)); - #endif - - if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; - - urs->handle.i = fd; - urs->ctx = dc; - return 0; - -oops: - if (is_valid_socket(fd)) close_socket (fd); - if (dc) qse_httpd_freemem (httpd, dc); - return -1; - -#endif -} - -static void urs_remove_tmr_tmout (urs_req_t* req) -{ - - if (req->tmr_tmout != QSE_TMR_INVALID) - { - qse_tmr_remove (req->dc->httpd->tmr, req->tmr_tmout); - req->tmr_tmout = QSE_TMR_INVALID; - } -} - -static void urs_close (qse_httpd_t* httpd, qse_httpd_urs_t* urs) -{ - urs_ctx_t* dc = (urs_ctx_t*)urs->ctx; - qse_size_t i; - - for (i = 0; i < QSE_COUNTOF(dc->reqs); i++) - { - urs_req_t* next_req; - while (dc->reqs[i]) - { - next_req = dc->reqs[i]->next; - urs_remove_tmr_tmout (dc->reqs[i]); - qse_httpd_freemem (httpd, dc->reqs[i]); - dc->reqs[i] = next_req; - dc->req_count--; - } - } - - QSE_ASSERT (dc->req_count == 0); - - close_socket (urs->handle.i); - qse_httpd_freemem (httpd, urs->ctx); -} - - -static int urs_recv (qse_httpd_t* httpd, qse_httpd_urs_t* urs) -{ - urs_ctx_t* dc = (urs_ctx_t*)urs->ctx; - httpd_xtn_t* httpd_xtn; - - qse_skad_t fromaddr; - socklen_t fromlen; - - qse_uint16_t xid; - qse_ssize_t len; - urs_pkt_t* pkt; - urs_req_t* req; - -printf ("URS_RECV....\n"); - - httpd_xtn = qse_httpd_getxtn (httpd); - - fromlen = QSE_SIZEOF(fromaddr); - len = recvfrom (urs->handle.i, dc->rcvbuf, QSE_SIZEOF(dc->rcvbuf) - 1, 0, (struct sockaddr*)&fromaddr, &fromlen); - -/* TODO: check if fromaddr matches the dc->skad... */ - - pkt = (urs_pkt_t*)dc->rcvbuf; - if (len >= QSE_SIZEOF(pkt->hdr) && len >= QSE_SIZEOF(pkt->hdr) + qse_ntoh16(pkt->hdr.len)) - { - xid = qse_ntoh16(pkt->hdr.seq) % QSE_COUNTOF(dc->reqs); - - for (req = dc->reqs[xid]; req; req = req->next) - { - if (req->pkt->hdr.seq == pkt->hdr.seq && req->pkt->hdr.qusum == pkt->hdr.qusum) - { - pkt->url[qse_ntoh16(pkt->hdr.len)] = QSE_MT('\0'); - - urs_remove_tmr_tmout (req); - req->rewrite (httpd, req->pkt->url, pkt->url, req->ctx); - - /* detach the request off dc->reqs */ - if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; - else req->prev->next = req->next; - if (req->next) req->next->prev = req->prev; - - qse_httpd_freemem (httpd, req); - dc->req_count--; - - break; - } - } - } - - return 0; -} - -static void tmr_urs_tmout_updated (qse_tmr_t* tmr, qse_size_t old_index, qse_size_t new_index, void* ctx) -{ - urs_req_t* req = (urs_req_t*)ctx; - -printf (">>tmr_urs_tmout_updated %d %d\n", (int)req->tmr_tmout, (int)old_index); - QSE_ASSERT (req->tmr_tmout == old_index); - req->tmr_tmout = new_index; -} - -static void tmr_urs_tmout_handle (qse_tmr_t* tmr, const qse_ntime_t* now, void* ctx) -{ - /* destory the unanswered request if timed out */ - - urs_req_t* req = (urs_req_t*)ctx; - urs_ctx_t* dc = req->dc; - qse_uint16_t xid; - - /* when this handler is called, the event should be removed from the timer */ - QSE_ASSERT (req->tmr_tmout == QSE_TMR_INVALID); - - /* --------------------------------------------------------------- - * resend - *---------------------------------------------------------------- */ - if (req->resends > 0) - { - httpd_xtn_t* httpd_xtn; - qse_tmr_event_t tmout_event; - - httpd_xtn = qse_httpd_getxtn (dc->httpd); - - qse_gettime (&tmout_event.when); - qse_addtime (&tmout_event.when, &httpd_xtn->urs.tmout, &tmout_event.when); - tmout_event.ctx = req; - tmout_event.handler = tmr_urs_tmout_handle; - tmout_event.updater = tmr_urs_tmout_updated; - - if (sendto (dc->urs->handle.i, req->pkt, req->pktlen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->pktlen) - { - /* error. fall thru */ - } - else - { - req->tmr_tmout = qse_tmr_insert (dc->httpd->tmr, &tmout_event); - if (req->tmr_tmout != QSE_TMR_INVALID) - { - req->resends--; - return; /* resend ok */ - } - } - } - -printf ("urs timed out....\n"); - /* --------------------------------------------------------------- - * timed out + no resend - *---------------------------------------------------------------- */ - xid = req->seq % QSE_COUNTOF(dc->reqs); - - /* detach the request off dc->reqs */ - if (req == dc->reqs[xid]) dc->reqs[xid] = req->next; - else req->prev->next = req->next; - if (req->next) req->next->prev = req->prev; - - /* urs timed out. report that name resolution failed */ - req->rewrite (dc->httpd, req->pkt->url, QSE_NULL, req->ctx); - - /* i don't cache the items that have timed out */ - qse_httpd_freemem (dc->httpd, req); - dc->req_count--; -} - -static int urs_send (qse_httpd_t* httpd, qse_httpd_urs_t* urs, const qse_mchar_t* url, qse_httpd_rewrite_t rewrite, void* ctx) -{ - urs_ctx_t* dc = (urs_ctx_t*)urs->ctx; - httpd_xtn_t* httpd_xtn; - - qse_uint16_t xid; - qse_uint32_t seq; - urs_req_t* req; - qse_size_t url_len; - qse_tmr_event_t tmout_event; - - httpd_xtn = qse_httpd_getxtn (httpd); - -printf ("URS REALLY SENING>>>>>>>>>>>>>>>>>>>>>>>\n"); - - if (dc->req_count >= QSE_COUNTOF(dc->reqs)) - { - /* too many pending requests */ - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOBUF); - return -1; - } - - url_len = qse_mbslen(url); - if (url_len > URS_MAX_URL_LEN) /* TODO: change the limit */ - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); - return -1; - } - - seq = ((qse_uint32_t)dc->seq + 1) % URS_SEQ_RANGE_SIZE; - dc->seq = seq; - - xid = seq % QSE_COUNTOF(dc->reqs); - - req = qse_httpd_callocmem (httpd, QSE_SIZEOF(*req) + url_len + QSE_SIZEOF(urs_pkt_t)); - if (req == QSE_NULL) return -1; - - req->seq = seq; - req->pkt = (urs_pkt_t*)(req + 1); - req->pkt->hdr.seq = qse_hton16(seq); - req->pkt->hdr.len = qse_hton16(url_len); - req->pkt->hdr.qusum = hash_string (url); - qse_mbscpy (req->pkt->url, url); - req->pktlen = QSE_SIZEOF(urs_pkt_t) + url_len - 1; /* to exclude the terminating '\0' */ - - req->rewrite = rewrite; - req->ctx = ctx; - - qse_gettime (&tmout_event.when); - qse_addtime (&tmout_event.when, &httpd_xtn->urs.tmout, &tmout_event.when); - tmout_event.ctx = req; - tmout_event.handler = tmr_urs_tmout_handle; - tmout_event.updater = tmr_urs_tmout_updated; - - req->tmr_tmout = qse_tmr_insert (httpd->tmr, &tmout_event); - if (req->tmr_tmout == QSE_TMR_INVALID) - { - qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM); - qse_httpd_freemem (httpd, req); - return -1; - } - req->resends = httpd_xtn->urs.resends; - - if (sendto (urs->handle.i, req->pkt, req->pktlen, 0, (struct sockaddr*)&dc->skad, dc->skadlen) != req->pktlen) - { - qse_tmr_remove (httpd->tmr, req->tmr_tmout); - req->tmr_tmout = QSE_TMR_INVALID; - qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); - qse_httpd_freemem (httpd, req); - return -1; - } - - req->dc = dc; - - /* link the request to the front of the chain */ - if (dc->reqs[xid]) dc->reqs[xid]->prev = req; - req->next = dc->reqs[xid]; - dc->reqs[xid] = req; - - /* increment the number of pending requests */ - dc->req_count++; - -printf ("URS REALLY SENT>>>>>>>>>>>>>>>>>>>>>>>\n"); - return 0; -} /* ------------------------------------------------------------------- */ #if 0 @@ -3586,6 +2268,18 @@ static void logact_httpd (qse_httpd_t* httpd, const qse_httpd_act_t* act) /* do nothing */ } +/* ------------------------------------------------------------------- */ + +static qse_size_t hash_string (const qse_mchar_t *str) +{ + qse_size_t h = 0; + while (*str) h = ((h << 5) + h) ^ *str++; + return h; +} + +#include "httpd-std-dns.h" +#include "httpd-std-urs.h" +/* ------------------------------------------------------------------- */ static qse_httpd_scb_t httpd_system_callbacks = { @@ -3629,7 +2323,6 @@ static qse_httpd_scb_t httpd_system_callbacks = dir_close, dir_read }, - /* client connection */ { client_close, @@ -3640,7 +2333,6 @@ static qse_httpd_scb_t httpd_system_callbacks = client_accepted, client_closed }, - /* dns */ { dns_open, dns_close, @@ -3946,6 +2638,7 @@ static int make_resource ( target->type = QSE_HTTPD_RSRC_PROXY; target->u.proxy.flags |= QSE_HTTPD_RSRC_PROXY_RAW; + target->u.proxy.host = QSE_NULL; if (qse_mbstonwad (qse_htre_getqpath(req), &target->u.proxy.dst.nwad) <= -1) { @@ -3959,7 +2652,7 @@ static int make_resource ( target->u.proxy.src.nwad.type = target->u.proxy.dst.nwad.type; } - /* pseudonym for raw proxying should not be useful. set it for consistency */ + /* pseudonym for raw proxying should not be useful. but set it for consistency */ if (server_xtn->query (httpd, client->server, QSE_NULL, QSE_NULL, QSE_HTTPD_SERVERSTD_PSEUDONYM, &target->u.proxy.pseudonym) <= -1) target->u.proxy.pseudonym = QSE_NULL; @@ -3976,7 +2669,6 @@ static int make_resource ( /* TODO: check if proxying is allowed.... */ qse_mchar_t* host, * slash; - /*host = tmp.qpath + 6;*/ host = tmp.qpath + 6; slash = qse_mbschr (host, QSE_MT('/')); @@ -3985,15 +2677,16 @@ static int make_resource ( target->type = QSE_HTTPD_RSRC_PROXY; target->u.proxy.flags = 0; - if (qse_mbsntonwad (host, slash - host, &target->u.proxy.dst.nwad) <= -1) - { /* TODO: refrain from manipulating the request like this */ + QSE_MEMMOVE (host - 1, host, slash - host); + slash[-1] = QSE_MT('\0'); + host = host - 1; + target->u.proxy.host = host; - QSE_MEMMOVE (host - 1, host, slash - host); - slash[-1] = QSE_MT('\0'); - + if (qse_mbstonwad (host, &target->u.proxy.dst.nwad) <= -1) + { target->u.proxy.flags |= QSE_HTTPD_RSRC_PROXY_DST_STR; - target->u.proxy.dst.str = host - 1; + target->u.proxy.dst.str = host; } else { @@ -4009,6 +2702,12 @@ static int make_resource ( /* TODO: refrain from manipulating the request like this */ req->u.q.path = slash; /* TODO: use setqpath or something... */ + +/******************************************************************/ +/*TODO: load this from configuration. reamove this after debugging */ +//target->u.proxy.flags |= QSE_HTTPD_RSRC_PROXY_URS; +/******************************************************************/ + /* mark that this request is going to be proxied. */ req->attr.flags |= QSE_HTRE_ATTR_PROXIED; return 0; @@ -4022,7 +2721,8 @@ static int make_resource ( /* proxy the request */ target->type = QSE_HTTPD_RSRC_PROXY; target->u.proxy.flags = 0; - + target->u.proxy.host = QSE_NULL; + /* transparent proxy may do the following target->u.proxy.dst = client->orgdst_addr; target->u.proxy.src = client->remote_addr; @@ -4585,7 +3285,7 @@ void* qse_httpd_getserverstdxtn (qse_httpd_t* httpd, qse_httpd_server_t* server) /* ------------------------------------------------------------------- */ -int qse_httpd_loopstd (qse_httpd_t* httpd, const qse_httpd_dnsstd_t* dns) +int qse_httpd_loopstd (qse_httpd_t* httpd, const qse_httpd_dnsstd_t* dns, const qse_httpd_ursstd_t* urs) { httpd_xtn_t* httpd_xtn = qse_httpd_getxtn (httpd); @@ -4604,5 +3304,17 @@ int qse_httpd_loopstd (qse_httpd_t* httpd, const qse_httpd_dnsstd_t* dns) httpd_xtn->dns.cache_negttl = QSE_HTTPD_DNSSTD_DEFAULT_CACHE_NEGTTL; } + if (urs) + { + httpd_xtn->urs = *urs; + } + else + { + httpd_xtn->urs.nwad.type = QSE_NWAD_NX; + httpd_xtn->urs.tmout.sec = QSE_HTTPD_URSSTD_DEFAULT_TMOUT; + httpd_xtn->urs.tmout.nsec = 0; + httpd_xtn->urs.resends = QSE_HTTPD_URSSTD_DEFAULT_RESENDS; + } + return qse_httpd_loop (httpd); } diff --git a/qse/lib/http/httpd-text.c b/qse/lib/http/httpd-text.c index e0a9db27..215ae877 100644 --- a/qse/lib/http/httpd-text.c +++ b/qse/lib/http/httpd-text.c @@ -65,6 +65,7 @@ static int task_main_text ( if (ctx->left <= 0) return 0; ctx->ptr += n; } + return 1; /* more work to do */ } diff --git a/qse/lib/http/httpd.c b/qse/lib/http/httpd.c index 2c176c6e..27da972a 100644 --- a/qse/lib/http/httpd.c +++ b/qse/lib/http/httpd.c @@ -382,7 +382,7 @@ static qse_htrd_recbs_t htrd_recbs = }; /* ----------------------------------------------------------------------- */ -static void tmr_idle_update (qse_tmr_t* tmr, qse_size_t old_index, qse_size_t new_index, void* ctx); +static void tmr_idle_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse_tmr_index_t new_index, void* ctx); static void tmr_idle_handle (qse_tmr_t* tmr, const qse_ntime_t* now, void* ctx); static void mark_bad_client (qse_httpd_client_t* client) @@ -398,22 +398,22 @@ static void mark_bad_client (qse_httpd_client_t* client) static qse_httpd_client_t* new_client (qse_httpd_t* httpd, qse_httpd_client_t* tmpl) { - qse_httpd_client_t* client; + qse_httpd_client_t* client = QSE_NULL; qse_tmr_event_t idle_event; htrd_xtn_t* xtn; client = qse_httpd_allocmem (httpd, QSE_SIZEOF(*client)); - if (client == QSE_NULL) return QSE_NULL; + if (client == QSE_NULL) goto oops; QSE_MEMSET (client, 0, QSE_SIZEOF(*client)); + client->tmr_idle = QSE_TMR_INVALID_INDEX; client->type = QSE_HTTPD_CLIENT; client->htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(*xtn)); if (client->htrd == QSE_NULL) { httpd->errnum = QSE_HTTPD_ENOMEM; - qse_httpd_freemem (httpd, client); - return QSE_NULL; + goto oops; } qse_gettime (&idle_event.when); @@ -422,15 +422,7 @@ static qse_httpd_client_t* new_client (qse_httpd_t* httpd, qse_httpd_client_t* t idle_event.handler = tmr_idle_handle; idle_event.updater = tmr_idle_update; - client->tmr_idle = qse_tmr_insert (httpd->tmr, &idle_event); - if (client->tmr_idle == QSE_TMR_INVALID) - { - httpd->errnum = QSE_HTTPD_ENOMEM; - qse_htrd_close (client->htrd); - qse_httpd_freemem (httpd, client); - return QSE_NULL; - } -printf ("tmr_insert %d\n", (int)client->tmr_idle); + if (qse_httpd_inserttimerevent (httpd, &idle_event, &client->tmr_idle) <= -1) goto oops; qse_htrd_setoption (client->htrd, QSE_HTRD_REQUEST | QSE_HTRD_TRAILERS | QSE_HTRD_CANONQPATH); @@ -446,13 +438,27 @@ printf ("tmr_insert %d\n", (int)client->tmr_idle); client->server = tmpl->server; client->initial_ifindex = tmpl->initial_ifindex; - xtn = (htrd_xtn_t*)qse_htrd_getxtn (client->htrd); + xtn = (htrd_xtn_t*)qse_htrd_getxtn (client->htrd); xtn->httpd = httpd; xtn->client = client; qse_htrd_setrecbs (client->htrd, &htrd_recbs); return client; + +oops: + if (client) + { + if (client->tmr_idle != QSE_TMR_INVALID_INDEX) + { + qse_httpd_removetimerevent (httpd, client->tmr_idle); + /* cleint->tmr_idle = QSE_TMR_INVALID_INDEX; */ + } + if (client->htrd) qse_htrd_close (client->htrd); + qse_httpd_freemem (httpd, client); + } + + return QSE_NULL; } static void free_client ( @@ -474,6 +480,12 @@ qse_printf (QSE_T("Debug: CLOSING SOCKET %d\n"), client->handle.i); client->status &= ~CLIENT_HANDLE_RW_IN_MUX; } + if (client->tmr_idle != QSE_TMR_INVALID_INDEX) + { + qse_httpd_removetimerevent (httpd, client->tmr_idle); + client->tmr_idle = QSE_TMR_INVALID_INDEX; + } + /* note that client.closed is not a counterpart to client.accepted. * so it is called even if client.close() failed. */ if (httpd->opt.scb.client.closed) @@ -481,12 +493,6 @@ qse_printf (QSE_T("Debug: CLOSING SOCKET %d\n"), client->handle.i); httpd->opt.scb.client.close (httpd, client); - if (client->tmr_idle != QSE_TMR_INVALID) - { - qse_tmr_remove (httpd->tmr, client->tmr_idle); - client->tmr_idle = QSE_TMR_INVALID; - } - qse_httpd_freemem (httpd, client); } @@ -618,7 +624,7 @@ printf ("MUX ADDHND CLIENT READ %d\n", client->handle.i); } -static void tmr_idle_update (qse_tmr_t* tmr, qse_size_t old_index, qse_size_t new_index, void* ctx) +static void tmr_idle_update (qse_tmr_t* tmr, qse_tmr_index_t old_index, qse_tmr_index_t new_index, void* ctx) { qse_httpd_client_t* client = (qse_httpd_client_t*)ctx; printf ("tmr_idle updated old_index %d new_index %d tmr_idle %d\n", (int)old_index, (int)new_index, (int)client->tmr_idle); @@ -645,6 +651,9 @@ printf ("client is idle purging....\n"); { qse_tmr_event_t idle_event; +printf ("client is NOT idle....\n"); + QSE_ASSERT (client->server->httpd->tmr == tmr); + /*qse_gettime (&idle_event.when);*/ idle_event.when = *now; qse_addtime (&idle_event.when, &client->server->httpd->opt.idle_limit, &idle_event.when); @@ -653,11 +662,9 @@ printf ("client is idle purging....\n"); idle_event.updater = tmr_idle_update; /* the timer must have been deleted when this callback is called. */ - QSE_ASSERT (client->tmr_idle == QSE_TMR_INVALID); - client->tmr_idle = qse_tmr_insert (tmr, &idle_event); - if (client->tmr_idle == QSE_TMR_INVALID) mark_bad_client (client); - -printf ("client is not idle rescheduling.....%d\n", (int)client->tmr_idle); + QSE_ASSERT (client->tmr_idle == QSE_TMR_INVALID_INDEX); + if (qse_httpd_inserttimerevent (client->server->httpd, &idle_event, &client->tmr_idle) <= -1) + mark_bad_client (client); } } } @@ -1730,12 +1737,16 @@ printf ("DNS_SEND.........................\n"); return httpd->opt.scb.dns.send (httpd, &httpd->dns, name, resol, ctx); } -#if 0 int qse_httpd_rewriteurl (qse_httpd_t* httpd, const qse_mchar_t* url, qse_httpd_rewrite_t rewrite, void* ctx) { - return httpd->opt.scb.urs.send (httpd, &httpd->dns, name, resol, ctx); + if (!httpd->ursactive) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOURS); + return -1; + } + + return httpd->opt.scb.urs.send (httpd, &httpd->urs, url, rewrite, ctx); } -#endif int qse_httpd_activatetasktrigger (qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { @@ -1770,3 +1781,20 @@ int qse_httpd_inactivatetasktrigger (qse_httpd_t* httpd, qse_httpd_client_t* cli return update_mux_for_current_task (httpd, client, task); } +int qse_httpd_inserttimerevent (qse_httpd_t* httpd, const qse_tmr_event_t* event, qse_tmr_index_t* index) +{ + qse_tmr_index_t tmp = qse_tmr_insert (httpd->tmr, event); + if (tmp == QSE_TMR_INVALID_INDEX) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM); + return -1; + } + + *index = tmp; + return 0; +} + +void qse_httpd_removetimerevent (qse_httpd_t* httpd, qse_tmr_index_t index) +{ + qse_tmr_remove (httpd->tmr, index); +} diff --git a/qse/lib/http/httpd.h b/qse/lib/http/httpd.h index c3d35dd4..38dda1de 100644 --- a/qse/lib/http/httpd.h +++ b/qse/lib/http/httpd.h @@ -161,6 +161,17 @@ int qse_httpd_inactivatetasktrigger ( qse_httpd_task_t* task ); +int qse_httpd_inserttimerevent ( + qse_httpd_t* httpd, + const qse_tmr_event_t* event, + qse_tmr_index_t* index +); + +void qse_httpd_removetimerevent ( + qse_httpd_t* httpd, + qse_tmr_index_t index +); + #ifdef __cplusplus } #endif