X-Git-Url: http://git.indexdata.com/?p=yazproxy-moved-to-github.git;a=blobdiff_plain;f=src%2Fyaz-proxy.cpp;h=930f7c221d1f0b7777ea48f438cfac7b1a80b420;hp=9915853e6e7e0a027d1a4894ea0e696e65119d14;hb=36b087dfdcfd7d098d170ea72b29a1b0690032d2;hpb=32e20aac80d955f4eaa2b78dfd99788bbc02edb2 diff --git a/src/yaz-proxy.cpp b/src/yaz-proxy.cpp index 9915853..930f7c2 100644 --- a/src/yaz-proxy.cpp +++ b/src/yaz-proxy.cpp @@ -1,7 +1,5 @@ -/* $Id: yaz-proxy.cpp,v 1.36 2005-09-12 20:09:14 adam Exp $ - Copyright (c) 1998-2005, Index Data. - -This file is part of the yaz-proxy. +/* This file is part of YAZ proxy + Copyright (C) 1998-2009 Index Data YAZ proxy is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -14,10 +12,9 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with YAZ proxy; see the file LICENSE. If not, write to the -Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. - */ +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifdef WIN32 #define HAVE_SYS_STAT_H 1 @@ -51,14 +48,22 @@ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include #include #include +#include #include "msg-thread.h" +#if YAZ_VERSIONL >= 0x03001D +#define YAZ_HAS_MK_SURROGATE 1 +#else +#define YAZ_HAS_MK_SURROGATE 0 +#endif + using namespace yazpp_1; -#define USE_AUTH_MSG 1 +#ifdef WIN32 +#define strncasecmp _strnicmp +#endif -#if USE_AUTH_MSG -class Auth_Msg : public IMsg_Thread { +class YAZ_EXPORT Auth_Msg : public IMsg_Thread { public: int m_ret; IMsg_Thread *handle(); @@ -80,10 +85,9 @@ Auth_Msg::~Auth_Msg() { nmem_destroy(m_nmem); } - + IMsg_Thread *Auth_Msg::handle() { - yaz_log(YLOG_LOG, "Auth_Msg:handle begin"); ODR decode = odr_createmem(ODR_DECODE); Z_APDU *apdu; @@ -97,34 +101,28 @@ IMsg_Thread *Auth_Msg::handle() { m_ret = m_proxy->handle_authentication(apdu); } - yaz_log(YLOG_LOG, "Auth_Msg:handle end"); odr_destroy(decode); return this; } void Auth_Msg::result() { - yaz_log(YLOG_LOG, "Auth_Msg:result proxy ok buf=%p len=%d", - m_apdu_buf, m_apdu_len); - odr_setbuf(m_proxy->odr_decode(), m_apdu_buf, m_apdu_len, 0); - Z_APDU *apdu = 0; - int r = z_APDU(m_proxy->odr_decode(), &apdu, 0, 0); - if (r) - yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU OK"); - else - yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU failed"); if (m_proxy->dec_ref()) + { yaz_log(YLOG_LOG, "Auth_Msg::proxy deleted meanwhile"); + } else { - yaz_log(YLOG_LOG, "Auth_Msg::proxy still alive"); + odr_setbuf(m_proxy->odr_decode(), m_apdu_buf, m_apdu_len, 0); + Z_APDU *apdu = 0; + int r = z_APDU(m_proxy->odr_decode(), &apdu, 0, 0); + if (!r) + yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU failed"); m_proxy->result_authentication(apdu, m_ret); } delete this; } -#endif - void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret) { if (apdu == 0 || ret == 0) @@ -135,7 +133,16 @@ void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret) dec_ref(); } else + { + if (apdu->which == Z_APDU_initRequest) + { + Yaz_ProxyConfig *cfg = check_reconfigure(); + if (cfg) + cfg->target_authentication(m_default_target, odr_encode(), + apdu->u.initRequest); + } handle_incoming_Z_PDU_2(apdu); + } } static const char *apdu_name(Z_APDU *apdu) @@ -192,9 +199,10 @@ static const char *gdu_name(Z_GDU *gdu) Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable, ISocketObservable *the_socket_observable, - Yaz_Proxy *parent) + Yaz_Proxy *parent) : - Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60) + Z_Assoc(the_PDU_Observable), + m_bw_stat(60), m_pdu_stat(60), m_search_stat(60) { m_PDU_Observable = the_PDU_Observable; m_socket_observable = the_socket_observable; @@ -208,20 +216,26 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable, m_default_target = 0; m_proxy_negotiation_charset = 0; m_proxy_negotiation_lang = 0; + m_proxy_negotiation_default_charset = 0; m_charset_converter = new Yaz_CharsetConverter; m_max_clients = 150; m_log_mask = 0; m_seed = time(0); m_client_idletime = 600; m_target_idletime = 600; - m_optimize = xstrdup ("1"); + m_max_sockets = 1024; + m_optimize = xstrdup("1"); strcpy(m_session_str, "0 "); m_session_no = 0; m_bytes_sent = 0; m_bytes_recv = 0; - m_bw_hold_PDU = 0; m_bw_max = 0; m_pdu_max = 0; + m_search_max = 0; + m_max_connect = 0; + m_max_connect_period = 0; + m_limit_connect = 0; + m_limit_connect_period = 0; m_timeout_mode = timeout_normal; m_timeout_gdu = 0; m_max_record_retrieve = 0; @@ -241,7 +255,7 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable, m_schema = 0; m_backend_type = 0; m_backend_charset = 0; - m_frontend_type = 0; + m_frontend_type[0] = -1; m_initRequest_apdu = 0; m_initRequest_mem = 0; m_initRequest_preferredMessageSize = 0; @@ -279,7 +293,9 @@ Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable, low_socket_open(); m_my_thread = 0; m_ref_count = 1; + m_main_ptr_dec = false; m_peername = 0; + m_num_msg_threads = 0; } void Yaz_Proxy::inc_ref() @@ -299,21 +315,22 @@ Yaz_Proxy::~Yaz_Proxy() xfree(m_default_target); xfree(m_proxy_negotiation_charset); xfree(m_proxy_negotiation_lang); + xfree(m_proxy_negotiation_default_charset); delete m_charset_converter; xfree(m_optimize); -#if HAVE_XSLT +#if YAZ_HAVE_XSLT if (m_stylesheet_xsp) xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp); #endif - xfree (m_time_tv); - - xfree (m_peername); - xfree (m_schema); - xfree (m_backend_type); - xfree (m_backend_charset); - xfree (m_usemarcon_ini_stage1); - xfree (m_usemarcon_ini_stage2); + xfree(m_time_tv); + + xfree(m_peername); + xfree(m_schema); + xfree(m_backend_type); + xfree(m_backend_charset); + xfree(m_usemarcon_ini_stage1); + xfree(m_usemarcon_ini_stage2); delete m_usemarcon; if (m_s2z_odr_init) odr_destroy(m_s2z_odr_init); @@ -339,30 +356,42 @@ int Yaz_Proxy::set_config(const char *config) m_config_fname = xstrdup(config); int r = m_config->read_xml(config); if (!r) - m_config->get_generic_info(&m_log_mask, &m_max_clients); + { + int period = 60; + m_config->get_generic_info(&m_log_mask, &m_max_clients, + &m_max_connect, &m_limit_connect, &period, + &m_num_msg_threads); + m_connect.set_period(period); + } return r; } void Yaz_Proxy::set_default_target(const char *target) { - xfree (m_default_target); + xfree(m_default_target); m_default_target = 0; if (target) - m_default_target = (char *) xstrdup (target); + m_default_target = (char *) xstrdup(target); } -void Yaz_Proxy::set_proxy_negotiation (const char *charset, const char *lang) +void Yaz_Proxy::set_proxy_negotiation(const char *charset, const char *lang, + const char *default_charset) { - yaz_log(YLOG_LOG, "%sSet the proxy negotiation: charset to '%s', " - "language to '%s'", m_session_str, charset?charset:"none", + yaz_log(YLOG_DEBUG, "%sSet the proxy negotiation: charset to '%s', " + "default charset to '%s', language to '%s'", m_session_str, + charset?charset:"none", + default_charset?default_charset:"none", lang?lang:"none"); - xfree (m_proxy_negotiation_charset); - xfree (m_proxy_negotiation_lang); + xfree(m_proxy_negotiation_charset); + xfree(m_proxy_negotiation_lang); m_proxy_negotiation_charset = m_proxy_negotiation_lang = 0; if (charset) - m_proxy_negotiation_charset = (char *) xstrdup (charset); + m_proxy_negotiation_charset = (char *) xstrdup(charset); if (lang) - m_proxy_negotiation_lang = (char *) xstrdup (lang); + m_proxy_negotiation_lang = (char *) xstrdup(lang); + if (default_charset) + m_proxy_negotiation_default_charset = + (char *) xstrdup(default_charset); } Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure() @@ -374,7 +403,6 @@ Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure() if (m_reconfig_flag) { yaz_log(YLOG_LOG, "reconfigure"); - yaz_log_reopen(); if (m_config_fname && cfg) { yaz_log(YLOG_LOG, "reconfigure config %s", m_config_fname); @@ -384,7 +412,11 @@ Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure() else { m_log_mask = 0; - cfg->get_generic_info(&m_log_mask, &m_max_clients); + int period = 60; + cfg->get_generic_info(&m_log_mask, &m_max_clients, + &m_max_connect, &m_limit_connect, + &period, &m_num_msg_threads); + m_connect.set_period(period); } } else @@ -398,8 +430,25 @@ IPDU_Observer *Yaz_Proxy::sessionNotify(IPDU_Observable *the_PDU_Observable, int fd) { check_reconfigure(); + + char session_str[200]; + const char *peername = the_PDU_Observable->getpeername(); + if (!peername) + peername = "nullpeer"; + + if (m_log_mask & PROXY_LOG_IP_CLIENT) + sprintf(session_str, "%ld:%d %.80s %d ", + (long) time(0), m_session_no, peername, 0); + else + sprintf(session_str, "%ld:%d %d ", + (long) time(0), m_session_no, 0); + m_session_no++; + + yaz_log(YLOG_LOG, "%sNew session %s", session_str, peername); + Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable, m_socket_observable, this); + new_proxy->m_config = 0; new_proxy->m_config_fname = 0; new_proxy->timeout(m_client_idletime); @@ -407,52 +456,51 @@ IPDU_Observer *Yaz_Proxy::sessionNotify(IPDU_Observable new_proxy->set_default_target(m_default_target); new_proxy->m_max_clients = m_max_clients; new_proxy->m_log_mask = m_log_mask; + new_proxy->m_session_no = m_session_no; + new_proxy->m_num_msg_threads = m_num_msg_threads; + +#if 0 + // in case we want to watch a particular client.. + if (!strcmp(peername, "tcp:163.121.19.82")) // NIS GROUP + new_proxy->m_log_mask = 255; +#endif + new_proxy->set_APDU_log(get_APDU_log()); - if (m_log_mask & PROXY_LOG_APDU_CLIENT) + if (new_proxy->m_log_mask & PROXY_LOG_APDU_CLIENT) new_proxy->set_APDU_yazlog(1); else new_proxy->set_APDU_yazlog(0); - sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no); - m_session_no++; - new_proxy->m_peername = xstrdup(the_PDU_Observable->getpeername()); - yaz_log (YLOG_LOG, "%sNew session %s", new_proxy->m_session_str, - new_proxy->m_peername); + strcpy(new_proxy->m_session_str, session_str); + new_proxy->m_peername = xstrdup(peername); new_proxy->set_proxy_negotiation(m_proxy_negotiation_charset, - m_proxy_negotiation_lang); + m_proxy_negotiation_lang, m_proxy_negotiation_default_charset); // create thread object the first time we get an incoming connection - if (!m_my_thread) - m_my_thread = new Msg_Thread(m_socket_observable, 1); + if (!m_my_thread && m_num_msg_threads > 0) + { + yaz_log(YLOG_LOG, "%sStarting message thread management. number=%d", + session_str, m_num_msg_threads); + m_my_thread = new Msg_Thread(m_socket_observable, m_num_msg_threads); + } new_proxy->m_my_thread = m_my_thread; return new_proxy; } char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo) { - int oid[OID_SIZE]; - Z_OtherInformationUnit *oi; - struct oident ent; - ent.proto = PROTO_Z3950; - ent.oclass = CLASS_USERINFO; - ent.value = (oid_value) VAL_COOKIE; - assert (oid_ent_to_oid (&ent, oid)); - - if (oid_ent_to_oid (&ent, oid) && - (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) && - oi->which == Z_OtherInfo_characterInfo) + Z_OtherInformationUnit *oi = + update_otherInformation(otherInfo, 0, yaz_oid_userinfo_cookie, 1, 1); + + if (oi && oi->which == Z_OtherInfo_characterInfo) return oi->information.characterInfo; return 0; } + char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo) { - int oid[OID_SIZE]; - Z_OtherInformationUnit *oi; - struct oident ent; - ent.proto = PROTO_Z3950; - ent.oclass = CLASS_USERINFO; - ent.value = (oid_value) VAL_PROXY; - if (oid_ent_to_oid (&ent, oid) && - (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) && - oi->which == Z_OtherInfo_characterInfo) + Z_OtherInformationUnit *oi = + update_otherInformation(otherInfo, 0, yaz_oid_userinfo_proxy, 1, 1); + + if (oi && oi->which == Z_OtherInfo_characterInfo) return oi->information.characterInfo; return 0; } @@ -493,7 +541,7 @@ const char *Yaz_Proxy::load_balance(const char **url) min_use = zurl_in_use[i]; spare_for_min = zurl_in_spare[i]; } - if (max_spare < zurl_in_spare[i]) + if (max_spare < zurl_in_spare[i]) { ret_spare = url[i]; max_spare = zurl_in_spare[i]; @@ -502,13 +550,32 @@ const char *Yaz_Proxy::load_balance(const char **url) return ret_min; } +int Yaz_Proxy::get_number_of_connections() +{ + int no_connections = 0; + Yaz_ProxyClient *c; + + for (c = m_parent->m_clientPool; c; c = c->m_next) + { + assert(c->m_prev); + assert(*c->m_prev == c); + if (!strcmp(m_proxyTarget, c->get_hostname())) + { + no_connections++; + } + } + yaz_log(YLOG_LOG, "%sExisting %s connections: %d", m_session_str, m_proxyTarget, + no_connections); + return no_connections; +} + Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, - const char *proxy_host) + const char *proxy_host, int *http_code) { - assert (m_parent); + assert(m_parent); Yaz_Proxy *parent = m_parent; Yaz_ProxyClient *c = m_client; - + if (!m_proxyTarget) { const char *url[MAX_ZURL_PLEX]; @@ -530,6 +597,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, const char *negotiation_charset = 0; const char *negotiation_lang = 0; const char *query_charset = 0; + const char *default_client_query_charset = 0; url[0] = m_default_target; url[1] = 0; if (cfg) @@ -537,7 +605,9 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, int pre_init = 0; cfg->get_target_info(proxy_host, url, &m_bw_max, &m_pdu_max, &m_max_record_retrieve, + &m_search_max, &m_target_idletime, &client_idletime, + &m_max_sockets, &parent->m_max_clients, &m_keepalive_limit_bw, &m_keepalive_limit_pdu, @@ -545,19 +615,26 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, &cql2rpn_fname, &negotiation_charset, &negotiation_lang, - &query_charset); + &query_charset, + &default_client_query_charset); } if (client_idletime != -1) { m_client_idletime = client_idletime; timeout(m_client_idletime); } + + // get those FILE descriptors available + m_parent->low_socket_close(); if (cql2rpn_fname) m_cql2rpn.set_pqf_file(cql2rpn_fname); - if (negotiation_charset || negotiation_lang) + // reserve them again + m_parent->low_socket_open(); + + if (negotiation_charset || negotiation_lang || default_client_query_charset) { set_proxy_negotiation(negotiation_charset, - negotiation_lang); + negotiation_lang, default_client_query_charset); } m_charset_converter->set_target_query_charset(query_charset); if (!url[0]) @@ -577,8 +654,8 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, { // search in sessions with a cookie for (c = parent->m_clientPool; c; c = c->m_next) { - assert (c->m_prev); - assert (*c->m_prev == c); + assert(c->m_prev); + assert(*c->m_prev == c); if (c->m_cookie && !strcmp(cookie,c->m_cookie) && !strcmp(m_proxyTarget, c->get_hostname())) { @@ -588,11 +665,11 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, // we have an initRequest we can safely do re-open if (c->m_waiting && apdu->which == Z_APDU_initRequest) { - yaz_log (YLOG_LOG, "%s REOPEN target=%s", m_session_str, + yaz_log(YLOG_LOG, "%s REOPEN target=%s", m_session_str, c->get_hostname()); c->close(); c->m_init_flag = 0; - + c->m_last_ok = 0; c->m_cache.clear(); c->m_last_resultCount = 0; @@ -605,22 +682,19 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, delete c; return 0; } - c->timeout(30); + c->timeout(30); } c->m_seqno = parent->m_seqno; if (c->m_server && c->m_server != this) c->m_server->m_client = 0; c->m_server = this; (parent->m_seqno)++; - yaz_log (YLOG_DEBUG, "get_client 1 %p %p", this, c); + yaz_log(YLOG_DEBUG, "get_client 1 %p %p", this, c); return c; } } } - else if (!c && - apdu->which == Z_APDU_initRequest && - apdu->u.initRequest->idAuthentication == 0 && - !ODR_MASK_GET(apdu->u.initRequest->options, Z_Options_negotiationModel)) + else if (!c && apdu->which == Z_APDU_initRequest ) { // anonymous sessions without cookie. // if authentication is set it is NOT anonymous se we can't share them. @@ -629,12 +703,13 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, { assert(c->m_prev); assert(*c->m_prev == c); - if (c->m_server == 0 && c->m_cookie == 0 && - c->m_waiting == 0 && - !strcmp(m_proxyTarget, c->get_hostname())) + if (c->m_server == 0 && c->m_cookie == 0 && c->m_waiting == 0 + && c->compare_idAuthentication(apdu) + && c->compare_charset(apdu) + && !strcmp(m_proxyTarget, c->get_hostname())) { // found it in cache - yaz_log (YLOG_LOG, "%sREUSE %d %s", + yaz_log(YLOG_LOG, "%sREUSE %d %s", m_session_str, parent->m_seqno, c->get_hostname()); c->m_seqno = parent->m_seqno; @@ -647,9 +722,9 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, c->set_APDU_yazlog(0); (parent->m_seqno)++; - + parent->pre_init(); - + return c; } } @@ -658,26 +733,28 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, { if (apdu->which != Z_APDU_initRequest) { - yaz_log (YLOG_LOG, "%sno init request as first PDU", m_session_str); + yaz_log(YLOG_LOG, "%sno init request as first PDU", m_session_str); + *http_code = 500; return 0; } - Z_InitRequest *initRequest = apdu->u.initRequest; - if (initRequest->idAuthentication) + int no_in_use = get_number_of_connections(); + if (no_in_use >= m_max_sockets) { - // the client uses authentication. We set the keepalive PDU - // to 0 so we don't cache it in releaseClient - m_keepalive_limit_pdu = 0; + yaz_log(YLOG_LOG, "%smax sockets reached %d", m_session_str, + m_max_sockets); + *http_code = 500; + return 0; } // go through list of clients - and find the lowest/oldest one. Yaz_ProxyClient *c_min = 0; int min_seq = -1; int no_of_clients = 0; if (parent->m_clientPool) - yaz_log (YLOG_DEBUG, "Existing sessions"); + yaz_log(YLOG_DEBUG, "Existing sessions"); for (c = parent->m_clientPool; c; c = c->m_next) { - yaz_log (YLOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno, + yaz_log(YLOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno, c->m_waiting, c->get_hostname(), c->m_cookie ? c->m_cookie : ""); no_of_clients++; @@ -692,18 +769,17 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, c = c_min; if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname())) { - yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Destroy %d", + yaz_log(YLOG_LOG, "%sMAXCLIENTS %d Destroy %d", m_session_str, parent->m_max_clients, c->m_seqno); if (c->m_server && c->m_server != this) - delete c->m_server; // PROBLEM: m_ref_count! - c->m_server = 0; + c->m_server->dec_ref(); } else { - yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s", + yaz_log(YLOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s", m_session_str, parent->m_max_clients, c->m_seqno, parent->m_seqno, c->get_hostname()); - xfree (c->m_cookie); + xfree(c->m_cookie); c->m_cookie = 0; if (cookie) c->m_cookie = xstrdup(cookie); @@ -711,12 +787,12 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, if (c->m_server && c->m_server != this) { c->m_server->m_client = 0; - delete c->m_server; // PROBLEM: m_ref_count! + c->m_server->dec_ref(); } (parent->m_seqno)++; c->m_target_idletime = m_target_idletime; c->timeout(m_target_idletime); - + if (parent->m_log_mask & PROXY_LOG_APDU_SERVER) c->set_APDU_yazlog(1); else @@ -727,7 +803,8 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, } else { - yaz_log (YLOG_LOG, "%sNEW %d %s", + + yaz_log(YLOG_LOG, "%sNEW %d %s", m_session_str, parent->m_seqno, m_proxyTarget); c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent); c->m_next = parent->m_clientPool; @@ -737,7 +814,7 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, c->m_prev = &parent->m_clientPool; } - xfree (c->m_cookie); + xfree(c->m_cookie); c->m_cookie = 0; if (cookie) c->m_cookie = xstrdup(cookie); @@ -763,8 +840,10 @@ Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie, c->set_APDU_yazlog(1); else c->set_APDU_yazlog(0); + + c->set_idAuthentication(apdu); } - yaz_log (YLOG_DEBUG, "get_client 3 %p %p", this, c); + yaz_log(YLOG_DEBUG, "get_client 3 %p %p", this, c); return c; } @@ -773,7 +852,6 @@ void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num) int i; for (i = 0; iwhich != Z_DiagRec_defaultFormat) @@ -783,19 +861,16 @@ void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num) } else r = p->u.defaultFormat; - if (!(ent = oid_getentbyoid(r->diagnosticSetId)) || - ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1) - yaz_log(YLOG_LOG, "%sError unknown diagnostic set", m_session_str); switch (r->which) { case Z_DefaultDiagFormat_v2Addinfo: - yaz_log(YLOG_LOG, "%sError %d %s:%s", + yaz_log(YLOG_LOG, "%sError " ODR_INT_PRINTF " %s:%s", m_session_str, *r->condition, diagbib1_str(*r->condition), r->u.v2Addinfo); break; case Z_DefaultDiagFormat_v3Addinfo: - yaz_log(YLOG_LOG, "%sError %d %s:%s", + yaz_log(YLOG_LOG, "%sError " ODR_INT_PRINTF " %s:%s", m_session_str, *r->condition, diagbib1_str(*r->condition), r->u.v3Addinfo); @@ -822,7 +897,7 @@ int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu) void Yaz_Proxy::convert_xsl_delay() { -#if HAVE_XSLT +#if YAZ_HAVE_XSLT Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset]; if (npr->which == Z_NamePlusRecord_databaseRecord) { @@ -836,7 +911,7 @@ void Yaz_Proxy::convert_xsl_delay() (char*) r->u.octet_aligned->buf, r->u.octet_aligned->len); - + yaz_log(YLOG_LOG, "%sXSLT convert %d", m_session_str, m_stylesheet_offset); res = xsltApplyStylesheet((xsltStylesheetPtr) m_stylesheet_xsp, @@ -846,12 +921,11 @@ void Yaz_Proxy::convert_xsl_delay() { xmlChar *out_buf; int out_len; - xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1); - + xmlDocDumpFormatMemory(res, &out_buf, &out_len, 1); m_stylesheet_nprl->records[m_stylesheet_offset]-> - u.databaseRecord = - z_ext_record(odr_encode(), VAL_TEXT_XML, - (char*) out_buf, out_len); + u.databaseRecord = + z_ext_record_oid(odr_encode(), yaz_oid_recsyn_xml, + (char*) out_buf, out_len); xmlFree(out_buf); xmlFreeDoc(res); } @@ -865,7 +939,7 @@ void Yaz_Proxy::convert_xsl_delay() { m_timeout_mode = timeout_normal; m_stylesheet_nprl = 0; -#if HAVE_XSLT +#if YAZ_HAVE_XSLT if (m_stylesheet_xsp) xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp); #endif @@ -879,7 +953,7 @@ void Yaz_Proxy::convert_xsl_delay() void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p) { - if (m_frontend_type != VAL_NONE) + if (m_frontend_type[0] != -1) { int i; for (i = 0; i < p->num_records; i++) @@ -890,60 +964,37 @@ void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p) Z_External *r = npr->u.databaseRecord; if (r->which == Z_External_octet) { -#if HAVE_USEMARCON +#if !HAVE_USEMARCON if (m_usemarcon_ini_stage1 && *m_usemarcon_ini_stage1) + yaz_log(YLOG_LOG, "%sError: USEMARCON requested but not available", + m_session_str); +#endif +#if HAVE_USEMARCON + yaz_log(YLOG_DEBUG, "%sUSEMARCON stage1=%s stage2=%s", + m_session_str, + m_usemarcon_ini_stage1 ? m_usemarcon_ini_stage1 : "(none)", + m_usemarcon_ini_stage2 ? m_usemarcon_ini_stage2 : "(none)"); + char *converted; + int convlen; + if (m_usemarcon->convert(m_usemarcon_ini_stage1, m_usemarcon_ini_stage2, + (char*) r->u.octet_aligned->buf, r->u.octet_aligned->len, + &converted, &convlen)) { - if (!m_usemarcon->m_stage1) - { - m_usemarcon->m_stage1 = new CDetails(); - } - m_usemarcon->m_stage1->SetIniFileName(m_usemarcon_ini_stage1); - m_usemarcon->m_stage1->SetMarcRecord((char*) r->u.octet_aligned->buf, r->u.octet_aligned->len); - int res = m_usemarcon->m_stage1->Start(); - if (res == 0) - { - char *converted; - int convlen; - m_usemarcon->m_stage1->GetMarcRecord(converted, convlen); - if (m_usemarcon_ini_stage2 && *m_usemarcon_ini_stage2) - { - if (!m_usemarcon->m_stage2) - { - m_usemarcon->m_stage2 = new CDetails(); - } - m_usemarcon->m_stage2->SetIniFileName(m_usemarcon_ini_stage2); - m_usemarcon->m_stage2->SetMarcRecord(converted, convlen); - res = m_usemarcon->m_stage2->Start(); - if (res == 0) - { - free(converted); - m_usemarcon->m_stage2->GetMarcRecord(converted, convlen); - } - else - { - yaz_log(YLOG_LOG, "%sUSEMARCON stage 2 error %d", m_session_str, res); - } - } - npr->u.databaseRecord = - z_ext_record(odr_encode(), + npr->u.databaseRecord = + z_ext_record_oid(odr_encode(), m_frontend_type, converted, strlen(converted)); - free(converted); - } - else - { - yaz_log(YLOG_LOG, "%sUSEMARCON stage 1 error %d", m_session_str, res); - } - continue; + free(converted); } + else #endif /* HAVE_USEMARCON */ npr->u.databaseRecord = - z_ext_record(odr_encode(), - m_frontend_type, - (char*) r->u.octet_aligned->buf, - r->u.octet_aligned->len); + z_ext_record_oid(odr_encode(), + m_frontend_type, + (char*) r->u.octet_aligned->buf, + r->u.octet_aligned->len); } } } @@ -953,7 +1004,6 @@ void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p) void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p, const char *backend_charset) { - yaz_log(YLOG_LOG, "%sconvert_to_marc", m_session_str); int sel = m_charset_converter->get_client_charset_selected(); const char *client_record_charset = m_charset_converter->get_client_query_charset(); @@ -972,36 +1022,36 @@ void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p, if (npr->which == Z_NamePlusRecord_databaseRecord) { Z_External *r = npr->u.databaseRecord; - oident *ent = oid_getentbyoid(r->direct_reference); - if (!ent || ent->value == VAL_NONE) + const Odr_oid *oid = r->direct_reference; + if (!oid) continue; - if (ent->value == VAL_SUTRS) + if (!oid_oidcmp(oid, yaz_oid_recsyn_sutrs)) { WRBUF w = wrbuf_alloc(); wrbuf_iconv_write(w, cd, (char*) r->u.octet_aligned->buf, r->u.octet_aligned->len); npr->u.databaseRecord = - z_ext_record(odr_encode(), ent->value, wrbuf_buf(w), - wrbuf_len(w)); - wrbuf_free(w, 1); + z_ext_record_oid(odr_encode(), oid, wrbuf_buf(w), + wrbuf_len(w)); + wrbuf_destroy(w); } - else if (ent->value == VAL_TEXT_XML) + else if (!oid_oidcmp(oid, yaz_oid_recsyn_xml)) { ; } else if (r->which == Z_External_octet) { - int rlen; - char *result; + size_t rlen; + const char *result; if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf, r->u.octet_aligned->len, &result, &rlen)) { npr->u.databaseRecord = - z_ext_record(odr_encode(), ent->value, result, rlen); + z_ext_record_oid(odr_encode(), oid, result, rlen); yaz_log(YLOG_LOG, "%sRecoding MARC record", m_session_str); } @@ -1012,10 +1062,6 @@ void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p, yaz_iconv_close(cd); yaz_marc_destroy(mt); } - else - { - yaz_log(YLOG_LOG, "%sSkipping marc convert", m_session_str); - } } void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p, @@ -1038,23 +1084,23 @@ void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p, { WRBUF w = wrbuf_alloc(); - yaz_display_OPAC(w, r->u.opac, 0); - npr->u.databaseRecord = z_ext_record( - odr_encode(), VAL_TEXT_XML, - wrbuf_buf(w), wrbuf_len(w) - ); - wrbuf_free(w, 1); + yaz_opac_decode_wrbuf(mt, r->u.opac, w); + npr->u.databaseRecord = z_ext_record_oid( + odr_encode(), yaz_oid_recsyn_xml, + wrbuf_buf(w), wrbuf_len(w)); + wrbuf_destroy(w); } else if (r->which == Z_External_octet) { - int rlen; - char *result; + size_t rlen; + const char *result; if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf, r->u.octet_aligned->len, &result, &rlen)) { npr->u.databaseRecord = - z_ext_record(odr_encode(), VAL_TEXT_XML, result, rlen); + z_ext_record_oid(odr_encode(), yaz_oid_recsyn_xml, + result, rlen); } } } @@ -1094,7 +1140,11 @@ int Yaz_Proxy::send_http_response(int code) z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive"); else timeout(0); - + if (code == 401) + z_HTTP_header_add(o, &hres->headers, "WWW-Authenticate", + "Basic realm=\"YAZ Proxy\""); + + if (m_log_mask & PROXY_LOG_REQ_CLIENT) { yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str, @@ -1105,14 +1155,17 @@ int Yaz_Proxy::send_http_response(int code) m_bytes_sent += len; m_bw_stat.add_bytes(len); logtime(); + + recv_GDU_more(true); + return r; } -int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu) +int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu, int http_code /* = 200 */) { ODR o = odr_encode(); const char *ctype = "text/xml"; - Z_GDU *gdu = z_get_HTTP_Response(o, 200); + Z_GDU *gdu = z_get_HTTP_Response(o, http_code); Z_HTTP_Response *hres = gdu->u.HTTP_Response; if (m_http_version) hres->version = odr_strdup(o, m_http_version); @@ -1121,18 +1174,20 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu) z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive"); else timeout(0); + if (http_code == 401) + z_HTTP_header_add(o, &hres->headers, "WWW-Authenticate", "Basic realm=\"YAZ Proxy\""); static Z_SOAP_Handler soap_handlers[2] = { -#if HAVE_XSLT - {"http://www.loc.gov/zing/srw/", 0, +#if YAZ_HAVE_XSLT + { (char*) "http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec}, #endif {0, 0, 0} }; - + Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP)); soap_package->which = Z_SOAP_generic; - soap_package->u.generic = + soap_package->u.generic = (Z_SOAP_Generic *) odr_malloc(o, sizeof(*soap_package->u.generic)); soap_package->u.generic->no = 0; soap_package->u.generic->ns = soap_handlers[0].ns; @@ -1143,28 +1198,28 @@ int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu) soap_handlers, 0, m_s2z_stylesheet); if (m_log_mask & PROXY_LOG_REQ_CLIENT) { - yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str, - gdu_name(gdu)); + yaz_log(YLOG_LOG, "%sSending %s to client", m_session_str, + gdu_name(gdu)); } int len; int r = send_GDU(gdu, &len); m_bytes_sent += len; m_bw_stat.add_bytes(len); logtime(); + + recv_GDU_more(true); + return r; } int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add) { ODR o = odr_encode(); - Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response); - Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response; - - srw_res->num_diagnostics = 1; - srw_res->diagnostics = (Z_SRW_diagnostic *) - odr_malloc(o, sizeof(*srw_res->diagnostics)); - yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add); - return send_srw_response(srw_pdu); + Z_SRW_diagnostic *diagnostic = (Z_SRW_diagnostic *) + odr_malloc(o, sizeof(*diagnostic)); + int num_diagnostic = 1; + yaz_mk_std_diagnostic(o, diagnostic, srw_error, add); + return send_srw_search_response(diagnostic, num_diagnostic, srw_error == 3 ? 401 : 200); } int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res, @@ -1177,11 +1232,42 @@ int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res, srw_res->diagnostics = (Z_SRW_diagnostic *) odr_malloc(o, sizeof(*srw_res->diagnostics)); yaz_mk_std_diagnostic(o, srw_res->diagnostics, - yaz_diag_bib1_to_srw(*ddf->condition), + yaz_diag_bib1_to_srw(*ddf->condition), ddf->u.v2Addinfo); return 0; } +#if YAZ_HAS_MK_SURROGATE +#else +static void yazproxy_mk_sru_surrogate(ODR o, Z_SRW_record *record, int pos, + int code, const char *details) +{ + const char *message = yaz_diag_srw_str(code); + int len = 200; + if (message) + len += strlen(message); + if (details) + len += strlen(details); + + record->recordData_buf = (char *) odr_malloc(o, len); + + sprintf(record->recordData_buf, "\n" + " info:srw/diagnostic/1/%d\n", code); + if (details) + sprintf(record->recordData_buf + strlen(record->recordData_buf), + "
%s
\n", details); + if (message) + sprintf(record->recordData_buf + strlen(record->recordData_buf), + " %s\n", message); + sprintf(record->recordData_buf + strlen(record->recordData_buf), + "
\n"); + record->recordData_len = strlen(record->recordData_buf); + record->recordPosition = odr_intdup(o, pos); + record->recordSchema = odr_strdup(o, "info:srw/schema/1/diagnostics-v1.1"); +} +#endif + int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start) { ODR o = odr_encode(); @@ -1201,31 +1287,40 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start) Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i]; if (npr->which != Z_NamePlusRecord_databaseRecord) { - srw_res->records[i].recordSchema = "diagnostic"; - srw_res->records[i].recordPacking = m_s2z_packing; - srw_res->records[i].recordData_buf = "67"; - srw_res->records[i].recordData_len = 2; - srw_res->records[i].recordPosition = odr_intdup(o, i+start); +#if YAZ_HAS_MK_SURROGATE + yaz_mk_sru_surrogate( + o, srw_res->records + i, i+start, + YAZ_SRW_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA, 0); +#else + yazproxy_mk_sru_surrogate( + o, srw_res->records + i, i+start, + YAZ_SRW_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA, 0); +#endif continue; } Z_External *r = npr->u.databaseRecord; - oident *ent = oid_getentbyoid(r->direct_reference); - if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML) + + if (r->which == Z_External_octet + && !oid_oidcmp(r->direct_reference, yaz_oid_recsyn_xml)) { srw_res->records[i].recordSchema = m_schema; srw_res->records[i].recordPacking = m_s2z_packing; - srw_res->records[i].recordData_buf = (char*) + srw_res->records[i].recordData_buf = (char*) r->u.octet_aligned->buf; srw_res->records[i].recordData_len = r->u.octet_aligned->len; srw_res->records[i].recordPosition = odr_intdup(o, i+start); } else { - srw_res->records[i].recordSchema = "diagnostic"; - srw_res->records[i].recordPacking = m_s2z_packing; - srw_res->records[i].recordData_buf = "67"; - srw_res->records[i].recordData_len = 2; - srw_res->records[i].recordPosition = odr_intdup(o, i+start); +#if YAZ_HAS_MK_SURROGATE + yaz_mk_sru_surrogate( + o, srw_res->records + i, i+start, + YAZ_SRW_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA, 0); +#else + yazproxy_mk_sru_surrogate( + o, srw_res->records + i, i+start, + YAZ_SRW_RECORD_NOT_AVAILABLE_IN_THIS_SCHEMA, 0); +#endif } } } @@ -1238,18 +1333,31 @@ int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start) return send_http_response(http_code); } return send_srw_response(srw_pdu); - + +} + +int Yaz_Proxy::send_srw_search_response(Z_SRW_diagnostic *diagnostics, + int num_diagnostics, int http_code /* = 200 */) +{ + ODR o = odr_encode(); + Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response); + Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response; + + srw_res->num_diagnostics = num_diagnostics; + srw_res->diagnostics = diagnostics; + return send_srw_response(srw_pdu, http_code); } int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics, int num_diagnostics) { + int http_status = 404; Yaz_ProxyConfig *cfg = check_reconfigure(); if (cfg) { int len; char *b = cfg->get_explain_doc(odr_encode(), 0 /* target */, - m_s2z_database, &len); + m_s2z_database, &len, &http_status); if (b) { Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response); @@ -1258,14 +1366,15 @@ int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics, er->record.recordData_buf = b; er->record.recordData_len = len; er->record.recordPacking = m_s2z_packing; - er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/"; + er->record.recordSchema = odr_strdup(odr_encode(), + "http://explain.z3950.org/dtd/2.0/"); er->diagnostics = diagnostics; er->num_diagnostics = num_diagnostics; return send_srw_response(res); } } - return send_http_response(404); + return send_http_response(http_status); } int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu) @@ -1299,9 +1408,9 @@ int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu) } else if (m_s2z_present_apdu && m_s2z_hit_count > 0) { - // adjust + // adjust Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest; - + if (*pr->resultSetStartPoint <= m_s2z_hit_count) { if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint @@ -1319,7 +1428,7 @@ int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu) } else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse) { - int start = + int start = *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint; m_s2z_present_apdu = 0; @@ -1331,7 +1440,7 @@ int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu) { int len = 0; if (m_log_mask & PROXY_LOG_REQ_CLIENT) - yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str, + yaz_log(YLOG_LOG, "%sSending %s to client", m_session_str, apdu_name(apdu)); int r = send_Z_PDU(apdu, &len); m_bytes_sent += len; @@ -1349,7 +1458,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) if (new_id) *new_id = m_referenceId; - + if (apdu->which == Z_APDU_searchResponse) { Z_SearchResponse *sr = apdu->u.searchResponse; @@ -1381,11 +1490,11 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) m_backend_charset); if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu)) return 0; - + } if (sr->resultCount) { - yaz_log(YLOG_LOG, "%s%d hits", m_session_str, + yaz_log(YLOG_LOG, "%s" ODR_INT_PRINTF " hits", m_session_str, *sr->resultCount); if (*sr->resultCount < 0) { @@ -1415,7 +1524,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) } if (p && p->which == Z_Records_DBOSD) { - if (m_backend_type + if (m_backend_type #if HAVE_USEMARCON || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2 #endif @@ -1436,10 +1545,10 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) //Get and check negotiation record //from init response. handle_charset_lang_negotiation(apdu); - + if (m_initRequest_options) { - Z_Options *nopt = + Z_Options *nopt = (Odr_bitmask *)odr_malloc(odr_encode(), sizeof(Odr_bitmask)); ODR_MASK_ZERO(nopt); @@ -1449,11 +1558,11 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) if (ODR_MASK_GET(m_initRequest_options, i) && ODR_MASK_GET(apdu->u.initResponse->options, i)) ODR_MASK_SET(nopt, i); - apdu->u.initResponse->options = nopt; + apdu->u.initResponse->options = nopt; } if (m_initRequest_version) { - Z_ProtocolVersion *nopt = + Z_ProtocolVersion *nopt = (Odr_bitmask *)odr_malloc(odr_encode(), sizeof(Odr_bitmask)); ODR_MASK_ZERO(nopt); @@ -1463,7 +1572,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) if (ODR_MASK_GET(m_initRequest_version, i) && ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i)) ODR_MASK_SET(nopt, i); - apdu->u.initResponse->protocolVersion = nopt; + apdu->u.initResponse->protocolVersion = nopt; } apdu->u.initResponse->preferredMessageSize = odr_intdup(odr_encode(), @@ -1478,7 +1587,7 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) m_initRequest_maximumRecordSize : m_client->m_initResponse_maximumRecordSize); } - + int r = send_PDU_convert(apdu); if (r) return r; @@ -1491,13 +1600,50 @@ int Yaz_Proxy::send_to_client(Z_APDU *apdu) return r; } +void Yaz_ProxyClient::set_idAuthentication(Z_APDU *apdu) +{ + Z_IdAuthentication *t = apdu->u.initRequest->idAuthentication; + + odr_reset(m_idAuthentication_odr); + z_IdAuthentication(m_idAuthentication_odr, &t, 1, 0); + m_idAuthentication_ber_buf = + odr_getbuf(m_idAuthentication_odr, + &m_idAuthentication_ber_size, 0); +} + +bool Yaz_ProxyClient::compare_charset(Z_APDU *apdu) +{ + return true; +} + +bool Yaz_ProxyClient::compare_idAuthentication(Z_APDU *apdu) +{ + Z_IdAuthentication *t = apdu->u.initRequest->idAuthentication; + ODR odr = odr_createmem(ODR_ENCODE); + + z_IdAuthentication(odr, &t, 1, 0); + int sz; + char *buf = odr_getbuf(odr, &sz, 0); + if (buf && m_idAuthentication_ber_buf + && sz == m_idAuthentication_ber_size + && !memcmp(m_idAuthentication_ber_buf, buf, sz)) + { + odr_destroy(odr); + return true; + } + odr_destroy(odr); + if (!buf && !m_idAuthentication_ber_buf) + return true; + return false; +} + int Yaz_ProxyClient::send_to_target(Z_APDU *apdu) { int len = 0; const char *apdu_name_tmp = apdu_name(apdu); int r = send_Z_PDU(apdu, &len); if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER) - yaz_log (YLOG_LOG, "%sSending %s to %s %d bytes", + yaz_log(YLOG_LOG, "%sSending %s to %s %d bytes", get_session_str(), apdu_name_tmp, get_hostname(), len); m_bytes_sent += len; @@ -1522,8 +1668,21 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) { Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse); new_apdu->u.presentResponse->records = - create_nonSurrogateDiagnostics(odr_encode(), 30, - pr->resultSetId); + create_nonSurrogateDiagnostics( + odr_encode(), + YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST, + pr->resultSetId); + send_to_client(new_apdu); + return 0; + } + if (start < 1 || toget < 0) + { + Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse); + new_apdu->u.presentResponse->records = + create_nonSurrogateDiagnostics( + odr_encode(), + YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, + 0); send_to_client(new_apdu); return 0; } @@ -1533,7 +1692,10 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) { Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse); new_apdu->u.presentResponse->records = - create_nonSurrogateDiagnostics(odr_encode(), 13, 0); + create_nonSurrogateDiagnostics( + odr_encode(), + YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, + 0); send_to_client(new_apdu); return 0; } @@ -1547,14 +1709,14 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) pr->preferredRecordSyntax, pr->recordComposition)) { - yaz_log (YLOG_LOG, "%sReturned cached records for present request", + yaz_log(YLOG_LOG, "%sReturned cached records for present request", m_session_str); Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse); new_apdu->u.presentResponse->referenceId = pr->referenceId; - + new_apdu->u.presentResponse->numberOfRecordsReturned = odr_intdup(odr_encode(), toget); - + new_apdu->u.presentResponse->records = (Z_Records*) odr_malloc(odr_encode(), sizeof(Z_Records)); new_apdu->u.presentResponse->records->which = Z_Records_DBOSD; @@ -1576,10 +1738,25 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) this_databases.set(sr->num_databaseNames, (const char **) sr->databaseNames); - + this_query->set_Z_Query(sr->query); - char query_str[120]; + // Check for non-negative piggyback params. + if (*sr->smallSetUpperBound < 0 + || *sr->largeSetLowerBound < 0 + || *sr->mediumSetPresentNumber < 0) + { + Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse); + // Not a present request.. But can't find better diagnostic + new_apdu->u.searchResponse->records = + create_nonSurrogateDiagnostics( + odr_encode(), + YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, 0); + send_to_client(new_apdu); + return 0; + } + + char query_str[4096]; this_query->print(query_str, sizeof(query_str)-1); yaz_log(YLOG_LOG, "%sSearch %s", m_session_str, query_str); @@ -1607,20 +1784,20 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) comp->which = Z_RecordComp_simple; comp->u.simple = sr->mediumSetElementSetNames; } - + if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget, sr->preferredRecordSyntax, comp)) { - yaz_log (YLOG_LOG, "%sReturned cached records for medium set", + yaz_log(YLOG_LOG, "%sReturned cached records for medium set", m_session_str); Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse); new_apdu->u.searchResponse->referenceId = sr->referenceId; new_apdu->u.searchResponse->resultCount = &m_client->m_last_resultCount; - + new_apdu->u.searchResponse->numberOfRecordsReturned = odr_intdup(odr_encode(), toget); - + new_apdu->u.searchResponse->presentStatus = odr_intdup(odr_encode(), Z_PresentStatus_success); new_apdu->u.searchResponse->records = (Z_Records*) @@ -1636,7 +1813,7 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) { // medium Set // send present request (medium size) - yaz_log (YLOG_LOG, "%sOptimizing search for medium set", + yaz_log(YLOG_LOG, "%sOptimizing search for medium set", m_session_str); Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest); @@ -1654,7 +1831,7 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) m_client->m_last_resultCount <= 0) { // large set. Return pseudo-search response immediately - yaz_log (YLOG_LOG, "%sOptimizing search for large set", + yaz_log(YLOG_LOG, "%sOptimizing search for large set", m_session_str); Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse); new_apdu->u.searchResponse->referenceId = sr->referenceId; @@ -1670,7 +1847,7 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) Z_RecordComposition *comp = 0; // small set // send a present request (small set) - + if (sr->smallSetElementSetNames) { comp = (Z_RecordComposition *) @@ -1682,16 +1859,16 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget, sr->preferredRecordSyntax, comp)) { - yaz_log (YLOG_LOG, "%sReturned cached records for small set", + yaz_log(YLOG_LOG, "%sReturned cached records for small set", m_session_str); Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse); new_apdu->u.searchResponse->referenceId = sr->referenceId; new_apdu->u.searchResponse->resultCount = &m_client->m_last_resultCount; - + new_apdu->u.searchResponse->numberOfRecordsReturned = odr_intdup(odr_encode(), toget); - + new_apdu->u.searchResponse->presentStatus = odr_intdup(odr_encode(), Z_PresentStatus_success); new_apdu->u.searchResponse->records = (Z_Records*) @@ -1705,7 +1882,7 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) } else { - yaz_log (YLOG_LOG, "%sOptimizing search for small set", + yaz_log(YLOG_LOG, "%sOptimizing search for small set", m_session_str); Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest); Z_PresentRequest *pr = new_apdu->u.presentRequest; @@ -1727,8 +1904,8 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) m_client->m_cache.clear(); m_client->m_resultSetStartPoint = 0; - xfree (m_client->m_last_resultSetId); - m_client->m_last_resultSetId = xstrdup (sr->resultSetName); + xfree(m_client->m_last_resultSetId); + m_client->m_last_resultSetId = xstrdup(sr->resultSetName); m_client->m_last_databases.set(sr->num_databaseNames, (const char **) sr->databaseNames); @@ -1739,9 +1916,13 @@ Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu) void Yaz_Proxy::inc_request_no() { - char *cp = strchr(m_session_str, ' '); m_request_no++; - if (cp) + char *cp = m_session_str + strlen(m_session_str)-1; + if (*cp == ' ') + cp--; + while (*cp && *cp != ' ') + cp--; + if (*cp) sprintf(cp+1, "%d ", m_request_no); } @@ -1750,39 +1931,133 @@ void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len) inc_request_no(); m_bytes_recv += len; - + if (m_log_mask & PROXY_LOG_REQ_CLIENT) - yaz_log (YLOG_LOG, "%sReceiving %s from client %d bytes", + yaz_log(YLOG_LOG, "%sReceiving %s from client %d bytes", m_session_str, gdu_name(apdu), len); - + +#if 0 + // try to make a _bad_ attribute set ID .. Don't enable this in prod. + if (apdu->which == Z_GDU_Z3950 + && apdu->u.z3950->which == Z_APDU_searchRequest) + { + Z_SearchRequest *req = apdu->u.z3950->u.searchRequest; + if (req->query && req->query->which == Z_Query_type_1) + { + Z_RPNQuery *rpnquery = req->query->u.type_1; + if (rpnquery->attributeSetId) + { + rpnquery->attributeSetId[0] = -2; + rpnquery->attributeSetId[1] = -1; + yaz_log(YLOG_WARN, "%sBAD FIXUP TEST", m_session_str); + } + } + } +#endif + #if HAVE_GETTIMEOFDAY gettimeofday((struct timeval *) m_time_tv, 0); #endif m_bw_stat.add_bytes(len); m_pdu_stat.add_bytes(1); - + GDU *gdu = new GDU(apdu); - m_in_queue.enqueue(gdu); + if (gdu->get() == 0) + { + delete gdu; + yaz_log(YLOG_LOG, "%sUnable to encode package", m_session_str); + m_in_queue.clear(); + dec_ref(); + return; + } + m_in_queue.enqueue(gdu); recv_GDU_more(false); } +void Yaz_Proxy::HTTP_Forwarded(Z_GDU *z_gdu) +{ + if (z_gdu->which == Z_GDU_HTTP_Request) + { + Z_HTTP_Request *hreq = z_gdu->u.HTTP_Request; + const char *x_forwarded_for = + z_HTTP_header_lookup(hreq->headers, "X-Forwarded-For"); + if (x_forwarded_for) + { + xfree(m_peername); + m_peername = (char*) xmalloc(strlen(x_forwarded_for)+5); + sprintf(m_peername, "tcp:%s", x_forwarded_for); + + yaz_log(YLOG_LOG, "%sHTTP Forwarded from %s", m_session_str, + m_peername); + if (m_log_mask & PROXY_LOG_IP_CLIENT) + sprintf(m_session_str, "%ld:%d %.80s %d ", + (long) time(0), m_session_no, m_peername, m_request_no); + else + sprintf(m_session_str, "%ld:%d %d ", + (long) time(0), m_session_no, m_request_no); + } + } +} + +void Yaz_Proxy::connect_stat(bool &block, int &reduce) +{ + + m_parent->m_connect.cleanup(false); + m_parent->m_connect.add_connect(m_peername); + + int connect_total = m_parent->m_connect.get_total(m_peername); + int max_connect = m_parent->m_max_connect; + + if (max_connect && connect_total > max_connect) + { + yaz_log(YLOG_LOG, "%sconnect not accepted total=%d max=%d", + m_session_str, connect_total, max_connect); + block = true; + } + else + block = false; + yaz_log(YLOG_LOG, "%sconnect accepted total=%d", m_session_str, + connect_total); + + int limit_connect = m_parent->m_limit_connect; + if (limit_connect) + reduce = connect_total / limit_connect; + else + reduce = 0; +} + void Yaz_Proxy::recv_GDU_reduce(GDU *gdu) { - int bw_total = m_bw_stat.get_total(); - int pdu_total = m_pdu_stat.get_total(); - int reduce = 0; + HTTP_Forwarded(gdu->get()); - assert(m_timeout_mode == timeout_busy); - assert(m_timeout_gdu == 0); + int reduce = 0; - if (m_bw_max) + if (m_request_no == 1) { - if (bw_total > m_bw_max) + bool block = false; + + connect_stat(block, reduce); + + if (block) { - reduce = (bw_total/m_bw_max); + m_timeout_mode = timeout_busy; + timeout(0); + return; } } + + int bw_total = m_bw_stat.get_total(); + int pdu_total = m_pdu_stat.get_total(); + int search_total = m_search_stat.get_total(); + + assert(m_timeout_mode == timeout_busy); + assert(m_timeout_gdu == 0); + + if (m_search_max) + reduce += search_total / m_search_max; + if (m_bw_max) + reduce += (bw_total/m_bw_max); if (m_pdu_max) { if (pdu_total > m_pdu_max) @@ -1797,19 +2072,17 @@ void Yaz_Proxy::recv_GDU_reduce(GDU *gdu) /* uncomment to force a big reduce */ m_timeout_mode = timeout_reduce; m_timeout_gdu = gdu; - // m_bw_hold_PDU = apdu; // save PDU and signal "on hold" timeout(3); // call us reduce seconds later return; -#endif - if (reduce) +#endif + if (reduce) { - yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d", - m_session_str, reduce, bw_total, pdu_total, - m_bw_max, m_pdu_max); - + yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d search=%d limit-bw=%d limit-pdu=%d limit-search=%d", + m_session_str, reduce, bw_total, pdu_total, search_total, + m_bw_max, m_pdu_max, m_search_max); + m_timeout_mode = timeout_reduce; m_timeout_gdu = gdu; - // m_bw_hold_PDU = apdu; // save PDU and signal "on hold" timeout(reduce); // call us reduce seconds later } else @@ -1824,14 +2097,17 @@ void Yaz_Proxy::recv_GDU_more(bool normal) while (m_timeout_mode == timeout_normal && (g = m_in_queue.dequeue())) { m_timeout_mode = timeout_busy; + inc_ref(); recv_GDU_reduce(g); + if (dec_ref()) + break; } } void Yaz_Proxy::recv_GDU_normal(GDU *gdu) { - Z_GDU *apdu = gdu->get(); - gdu->extract_odr_to(odr_decode()); + Z_GDU *apdu = 0; + gdu->move_away_gdu(odr_decode(), &apdu); delete gdu; if (apdu->which == Z_GDU_Z3950) @@ -1847,7 +2123,7 @@ void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu) if (apdu->which == Z_APDU_presentRequest) { Z_PresentRequest *pr = apdu->u.presentRequest; - if (pr->numberOfRecordsRequested && + if (pr->numberOfRecordsRequested && *pr->numberOfRecordsRequested > m_max_record_retrieve) *pr->numberOfRecordsRequested = m_max_record_retrieve; } @@ -1858,8 +2134,6 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) { if (apdu->which == Z_APDU_initRequest) { - yaz_log(YLOG_LOG, "%shandle_charset_lang_negotiation", - m_session_str); if (m_initRequest_options && !ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel) && (m_proxy_negotiation_charset || m_proxy_negotiation_lang)) @@ -1868,7 +2142,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) // client's side. OK. The proxy negotiation // in use, only. Z_InitRequest *initRequest = apdu->u.initRequest; - Z_OtherInformation **otherInfo; + Z_OtherInformation **otherInfo; Z_OtherInformationUnit *oi; get_otherInfoAPDU(apdu, &otherInfo); oi = update_otherInformation(otherInfo, 1, NULL, 0, 0); @@ -1876,7 +2150,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) { ODR_MASK_SET(initRequest->options, Z_Options_negotiationModel); - oi->which = Z_OtherInfo_externallyDefinedInfo; + oi->which = Z_OtherInfo_externallyDefinedInfo; oi->information.externallyDefinedInfo = yaz_set_proposal_charneg(odr_encode(), (const char**)&m_proxy_negotiation_charset, @@ -1896,7 +2170,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) m_charset_converter->get_target_query_charset()); Z_InitRequest *initRequest = apdu->u.initRequest; Z_CharSetandLanguageNegotiation *negotiation = - yaz_get_charneg_record (initRequest->otherInfo); + yaz_get_charneg_record (initRequest->otherInfo); if (negotiation && negotiation->which == Z_CharSetandLanguageNegotiation_proposal) { @@ -1936,7 +2210,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) m_charset_converter->set_client_charset_selected(selected); } nmem_destroy(nmem); - ODR_MASK_CLEAR(m_initRequest_options, + ODR_MASK_CLEAR(m_initRequest_options, Z_Options_negotiationModel); yaz_del_charneg_record(&initRequest->otherInfo); } @@ -1946,40 +2220,40 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) m_session_str); } } + else if (m_charset_converter->get_target_query_charset() && + m_proxy_negotiation_default_charset) + { + m_charset_converter-> + set_client_query_charset(m_proxy_negotiation_default_charset); + } } else if (apdu->which == Z_APDU_initResponse) { - Z_InitResponse *initResponse = apdu->u.initResponse; - Z_OtherInformation **otherInfo; + Z_InitResponse *initResponse = apdu->u.initResponse; + Z_OtherInformation **otherInfo; + get_otherInfoAPDU(apdu, &otherInfo); - if (ODR_MASK_GET(initResponse->options, Z_Options_negotiationModel)) + Z_CharSetandLanguageNegotiation *charneg = 0; + + if (otherInfo && *otherInfo && + ODR_MASK_GET(initResponse->options, Z_Options_negotiationModel) + && (charneg = yaz_get_charneg_record(*otherInfo))) { char *charset = 0; char *lang = 0; int selected = 0; - - get_otherInfoAPDU(apdu, &otherInfo); - - if (!otherInfo && !(*otherInfo)) - return; - - Z_CharSetandLanguageNegotiation *charneg = - yaz_get_charneg_record(*otherInfo); - - if (!charneg) - return; - + yaz_get_response_charneg(m_referenceId_mem, charneg, &charset, &lang, &selected); - + yaz_log(YLOG_LOG, "%sAccepted charset - '%s' and lang - '%s'", m_session_str, (charset)?charset:"none", (lang)?lang:"none"); - + if (m_initRequest_options && ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel)) { yaz_log(YLOG_LOG, "%sClient's negotiation record in use", - m_session_str); + m_session_str); } else if (m_proxy_negotiation_charset || m_proxy_negotiation_lang) { @@ -1991,7 +2265,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) // clear negotiation option. ODR_MASK_CLEAR(initResponse->options, Z_Options_negotiationModel); - + // Delete negotiation (charneg-3) entry. yaz_del_charneg_record(otherInfo); } @@ -2005,7 +2279,7 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) } else if (m_charset_converter->get_client_query_charset()) { - Z_OtherInformation **otherInfo; + Z_OtherInformation **otherInfo; Z_OtherInformationUnit *oi; get_otherInfoAPDU(apdu, &otherInfo); oi = update_otherInformation(otherInfo, 1, NULL, 0, 0); @@ -2013,9 +2287,10 @@ void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu) { ODR_MASK_SET(initResponse->options, Z_Options_negotiationModel); - ODR_MASK_SET(m_initRequest_options, - Z_Options_negotiationModel); - + if (m_initRequest_options) + ODR_MASK_SET(m_initRequest_options, + Z_Options_negotiationModel); + oi->which = Z_OtherInfo_externallyDefinedInfo; oi->information.externallyDefinedInfo = yaz_set_response_charneg( @@ -2035,7 +2310,7 @@ Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr, { Z_Records *rec = (Z_Records *) odr_malloc (odr, sizeof(*rec)); - int *err = (int *) + Odr_int *err = (Odr_int *) odr_malloc (odr, sizeof(*err)); Z_DiagRec *drec = (Z_DiagRec *) odr_malloc (odr, sizeof(*drec)); @@ -2044,8 +2319,7 @@ Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr, *err = error; rec->which = Z_Records_NSD; rec->u.nonSurrogateDiagnostic = dr; - dr->diagnosticSetId = - yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1); + dr->diagnosticSetId = odr_oiddup(odr, yaz_oid_diagset_bib_1); dr->condition = err; dr->which = Z_DefaultDiagFormat_v2Addinfo; dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : ""); @@ -2062,7 +2336,7 @@ Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu) Z_RPNQuery *rpnquery = 0; Z_SearchRequest *sr = apdu->u.searchRequest; char *addinfo = 0; - + yaz_log(YLOG_LOG, "%sCQL: %s", m_session_str, sr->query->u.type_104->u.cql); @@ -2181,9 +2455,44 @@ int Yaz_Proxy::handle_authentication(Z_APDU *apdu) else ret = cfg->client_authentication(m_default_target, 0, 0, 0, m_peername); + return ret; +} - cfg->target_authentication(m_default_target, odr_encode(), req); +int Yaz_Proxy::handle_global_authentication(Z_APDU *apdu) +{ + if (apdu->which != Z_APDU_initRequest) + return 1; // pass if no init request + Z_InitRequest *req = apdu->u.initRequest; + Yaz_ProxyConfig *cfg = check_reconfigure(); + if (!cfg) + return 1; // pass if no config + + int ret; + if (req->idAuthentication == 0) + { + ret = cfg->global_client_authentication(0, 0, 0, + m_peername); + } + else if (req->idAuthentication->which == Z_IdAuthentication_idPass) + { + ret = cfg->global_client_authentication( + req->idAuthentication->u.idPass->userId, + req->idAuthentication->u.idPass->groupId, + req->idAuthentication->u.idPass->password, + m_peername); + } + else if (req->idAuthentication->which == Z_IdAuthentication_open) + { + char user[64], pass[64]; + *user = '\0'; + *pass = '\0'; + sscanf(req->idAuthentication->u.open, "%63[^/]/%63s", user, pass); + ret = cfg->global_client_authentication(user, 0, pass, + m_peername); + } + else + ret = cfg->global_client_authentication(0, 0, 0, m_peername); return ret; } @@ -2206,13 +2515,9 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) } if (sr->preferredRecordSyntax) - { - struct oident *ent; - ent = oid_getentbyoid(sr->preferredRecordSyntax); - m_frontend_type = ent->value; - } + oid_oidcpy(m_frontend_type, sr->preferredRecordSyntax); else - m_frontend_type = VAL_NONE; + m_frontend_type[0] = -1; char *stylesheet_name = 0; if (cfg) @@ -2227,7 +2532,7 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) { m_parent->low_socket_close(); -#if HAVE_XSLT +#if YAZ_HAVE_XSLT if (m_stylesheet_xsp) xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp); m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*) @@ -2243,35 +2548,30 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) sr->smallSetElementSetNames = 0; sr->mediumSetElementSetNames = 0; m_marcxml_mode = marcxml; - if (m_backend_type) - { - - sr->preferredRecordSyntax = - yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, - m_backend_type); - } - else - sr->preferredRecordSyntax = - yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, - VAL_USMARC); + sr->preferredRecordSyntax = + yaz_string_to_oid_odr( + yaz_oid_std(), CLASS_RECSYN, + m_backend_type ? m_backend_type : "usmarc", + odr_encode()); } else if (err) { Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse); - + new_apdu->u.searchResponse->referenceId = sr->referenceId; new_apdu->u.searchResponse->records = create_nonSurrogateDiagnostics(odr_encode(), err, addinfo); *new_apdu->u.searchResponse->searchStatus = 0; - + send_to_client(new_apdu); - + return 0; } else if (m_backend_type) { sr->preferredRecordSyntax = - yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type); + yaz_string_to_oid_odr(yaz_oid_std(), CLASS_RECSYN, + m_backend_type, odr_encode()); } } else if (apdu->which == Z_APDU_presentRequest) @@ -2282,13 +2582,9 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) Yaz_ProxyConfig *cfg = check_reconfigure(); if (pr->preferredRecordSyntax) - { - struct oident *ent; - ent = oid_getentbyoid(pr->preferredRecordSyntax); - m_frontend_type = ent->value; - } + oid_oidcpy(m_frontend_type, pr->preferredRecordSyntax); else - m_frontend_type = VAL_NONE; + m_frontend_type[0] = -1; char *stylesheet_name = 0; if (cfg) @@ -2304,7 +2600,7 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) { m_parent->low_socket_close(); -#if HAVE_XSLT +#if YAZ_HAVE_XSLT if (m_stylesheet_xsp) xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp); m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*) @@ -2319,36 +2615,33 @@ Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu) { pr->recordComposition = 0; m_marcxml_mode = marcxml; - if (m_backend_type) - { - - pr->preferredRecordSyntax = - yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, - m_backend_type); - } - else - pr->preferredRecordSyntax = - yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, - VAL_USMARC); + + pr->preferredRecordSyntax = + yaz_string_to_oid_odr( + yaz_oid_std(), CLASS_RECSYN, + m_backend_type ? m_backend_type : "usmarc", + odr_encode()); } else if (err) { Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse); - + new_apdu->u.presentResponse->referenceId = pr->referenceId; new_apdu->u.presentResponse->records = create_nonSurrogateDiagnostics(odr_encode(), err, addinfo); *new_apdu->u.presentResponse->presentStatus = Z_PresentStatus_failure; - + send_to_client(new_apdu); - + return 0; } else if (m_backend_type) { pr->preferredRecordSyntax = - yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type); + yaz_string_to_oid_odr(yaz_oid_std(), + CLASS_RECSYN, m_backend_type, + odr_encode()); } } return apdu; @@ -2376,7 +2669,7 @@ void Yaz_Proxy::srw_get_client(const char *db, const char **backend_db) { releaseClient(); } - + if (t) { xfree(m_default_target); @@ -2390,39 +2683,41 @@ int Yaz_Proxy::file_access(Z_HTTP_Request *hreq) if (strcmp(hreq->method, "GET")) return 0; if (hreq->path[0] != '/') - { - yaz_log(YLOG_WARN, "Bad path: %s", hreq->path); return 0; - } const char *cp = hreq->path; while (*cp) { if (*cp == '/' && strchr("/.", cp[1])) - { - yaz_log(YLOG_WARN, "Bad path: %s", hreq->path); return 0; - } cp++; } + + Yaz_ProxyConfig *cfg = check_reconfigure(); + + if (!cfg->get_file_access_info(hreq->path+1)) + return 0; + const char *fname = hreq->path+1; if (stat(fname, &sbuf)) { - yaz_log(YLOG_WARN|YLOG_ERRNO, "%s: stat failed", fname); + yaz_log(YLOG_LOG|YLOG_ERRNO, "%sstat failed for %s", m_session_str, + fname); return 0; } if ((sbuf.st_mode & S_IFMT) != S_IFREG) { - yaz_log(YLOG_WARN, "%s: not a regular file", fname); + yaz_log(YLOG_LOG, "%sNot a regular file %s", m_session_str, fname); return 0; } if (sbuf.st_size > (off_t) 1000000) { - yaz_log(YLOG_WARN, "%s: too large for transfer", fname); + yaz_log(YLOG_WARN, "%sFile %s too large for transfer", m_session_str, + fname); return 0; } - + ODR o = odr_encode(); - Yaz_ProxyConfig *cfg = check_reconfigure(); + const char *ctype = cfg->check_mime_type(fname); Z_GDU *gdu = z_get_HTTP_Response(o, 200); Z_HTTP_Response *hres = gdu->u.HTTP_Response; @@ -2448,14 +2743,15 @@ int Yaz_Proxy::file_access(Z_HTTP_Request *hreq) } if (m_log_mask & PROXY_LOG_REQ_CLIENT) { - yaz_log (YLOG_LOG, "%sSending file %s to client", m_session_str, + yaz_log(YLOG_LOG, "%sSending file %s to client", m_session_str, fname); } int len; send_GDU(gdu, &len); + recv_GDU_more(true); return 1; } - + void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) { if (m_s2z_odr_init) @@ -2471,7 +2767,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) m_http_keepalive = 0; m_http_version = 0; - if (!strcmp(hreq->version, "1.0")) + if (!strcmp(hreq->version, "1.0")) { const char *v = z_HTTP_header_lookup(hreq->headers, "Connection"); if (v && !strcmp(v, "Keep-Alive")) @@ -2490,12 +2786,20 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) m_http_version = "1.1"; } + const char *a = z_HTTP_header_lookup(hreq->headers, "Authorization"); + char authorization_str[255]; + *authorization_str = '\0'; + if (a && strncasecmp(a, "Basic ", 6) == 0) + base64_decode(a + 6, authorization_str, 254); + Z_SRW_PDU *srw_pdu = 0; Z_SOAP *soap_package = 0; char *charset = 0; Z_SRW_diagnostic *diagnostic = 0; int num_diagnostic = 0; + yaz_log(YLOG_LOG, "%s%s %s", m_session_str, hreq->method, hreq->path); + if (file_access(hreq)) { return; @@ -2514,8 +2818,48 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) m_s2z_stylesheet = 0; + Z_IdAuthentication *auth = NULL; + if (srw_pdu->username && !srw_pdu->password) + { + yaz_log(YLOG_LOG, "username: %s\n", srw_pdu->username); + auth = (Z_IdAuthentication *) odr_malloc(m_s2z_odr_init, sizeof(Z_IdAuthentication)); + auth->which = Z_IdAuthentication_open; + auth->u.open = odr_strdup(m_s2z_odr_init, srw_pdu->username); + } + else if (srw_pdu->username && srw_pdu->password) + { + yaz_log(YLOG_LOG, "username/password: %s/%s\n", + srw_pdu->username, srw_pdu->password); + auth = (Z_IdAuthentication *) odr_malloc(m_s2z_odr_init, sizeof(Z_IdAuthentication)); + auth->which = Z_IdAuthentication_idPass; + auth->u.idPass = (Z_IdPass *) odr_malloc(m_s2z_odr_init, sizeof(Z_IdPass)); + auth->u.idPass->groupId = NULL; + auth->u.idPass->password = odr_strdup(m_s2z_odr_init, srw_pdu->password); + auth->u.idPass->userId = odr_strdup(m_s2z_odr_init, srw_pdu->username); + } + else + { + if (*authorization_str) + { + yaz_log(YLOG_LOG, "authorization_str present: %s\n", authorization_str); + auth = (Z_IdAuthentication *) odr_malloc(m_s2z_odr_init, sizeof(Z_IdAuthentication)); + auth->which = Z_IdAuthentication_idPass; + auth->u.idPass = (Z_IdPass *) odr_malloc(m_s2z_odr_init, sizeof(Z_IdPass)); + auth->u.idPass->groupId = NULL; + char *p = strchr(authorization_str, ':'); + if (p) + { + *p = '\0'; + p++; + auth->u.idPass->password = odr_strdup(m_s2z_odr_init, p); + } + auth->u.idPass->userId = odr_strdup(m_s2z_odr_init, authorization_str); + } + } + if (srw_pdu->which == Z_SRW_searchRetrieve_request) { + Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request; const char *backend_db = srw_req->database; @@ -2540,7 +2884,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) if (srw_req->stylesheet) m_s2z_stylesheet = odr_strdup(m_s2z_odr_init, srw_req->stylesheet); - + // set packing for response records .. if (srw_req->recordPacking && !strcmp(srw_req->recordPacking, "xml")) @@ -2554,7 +2898,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) yaz_srw_get(odr_encode(), Z_SRW_searchRetrieve_response); Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response; - + srw_res->diagnostics = diagnostic; srw_res->num_diagnostics = num_diagnostic; send_srw_response(srw_pdu); @@ -2572,23 +2916,23 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) odr_malloc(m_s2z_odr_search, sizeof(char *)); z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search, backend_db); - + // query transformation Z_Query *query = (Z_Query *) odr_malloc(m_s2z_odr_search, sizeof(Z_Query)); z_searchRequest->query = query; - + if (srw_req->query_type == Z_SRW_query_type_cql) { - Z_External *ext = (Z_External *) + Z_External *ext = (Z_External *) odr_malloc(m_s2z_odr_search, sizeof(*ext)); - ext->direct_reference = + ext->direct_reference = odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2"); ext->indirect_reference = 0; ext->descriptor = 0; ext->which = Z_External_CQL; ext->u.cql = srw_req->query.cql; - + query->which = Z_Query_type_104; query->u.type_104 = ext; } @@ -2596,9 +2940,9 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) { Z_RPNQuery *RPNquery; YAZ_PQF_Parser pqf_parser; - + pqf_parser = yaz_pqf_create (); - + RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search, srw_req->query.pqf); if (!RPNquery) @@ -2606,15 +2950,16 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) const char *pqf_msg; size_t off; int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off); - yaz_log(YLOG_LOG, "%*s^\n", off+4, ""); + int ioff = off; + yaz_log(YLOG_LOG, "%*s^\n", ioff+4, ""); yaz_log(YLOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code); - + send_to_srw_client_error(10, 0); return; } query->which = Z_Query_type_1; query->u.type_1 = RPNquery; - + yaz_pqf_destroy (pqf_parser); } else @@ -2642,8 +2987,8 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9 z_searchRequest->preferredRecordSyntax = - yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN, - VAL_TEXT_XML); + odr_oiddup(m_s2z_odr_search, yaz_oid_recsyn_xml); + if (srw_req->recordSchema) { z_searchRequest->smallSetElementSetNames = @@ -2654,23 +2999,23 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) } else // Z39.50 present { - m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, + m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, Z_APDU_presentRequest); - Z_PresentRequest *z_presentRequest = + Z_PresentRequest *z_presentRequest = m_s2z_present_apdu->u.presentRequest; *z_presentRequest->resultSetStartPoint = start; *z_presentRequest->numberOfRecordsRequested = max; + z_presentRequest->preferredRecordSyntax = - yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN, - VAL_TEXT_XML); + odr_oiddup(m_s2z_odr_search, yaz_oid_recsyn_xml); if (srw_req->recordSchema) { z_presentRequest->recordComposition = (Z_RecordComposition *) odr_malloc(m_s2z_odr_search, sizeof(Z_RecordComposition)); - z_presentRequest->recordComposition->which = - Z_RecordComp_simple; + z_presentRequest->recordComposition->which = + Z_RecordComp_simple; z_presentRequest->recordComposition->u.simple = mk_esn_from_schema(m_s2z_odr_search, srw_req->recordSchema); @@ -2681,7 +3026,9 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) { m_s2z_init_apdu = zget_APDU(m_s2z_odr_init, Z_APDU_initRequest); - + + m_s2z_init_apdu->u.initRequest->idAuthentication = auth; + // prevent m_initRequest_apdu memory from being grabbed // in Yaz_Proxy::handle_incoming_Z_PDU m_initRequest_apdu = m_s2z_init_apdu; @@ -2724,6 +3071,8 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) { m_s2z_init_apdu = zget_APDU(m_s2z_odr_init, Z_APDU_initRequest); + + m_s2z_init_apdu->u.initRequest->idAuthentication = auth; // prevent m_initRequest_apdu memory from being grabbed // in Yaz_Proxy::handle_incoming_Z_PDU @@ -2746,7 +3095,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) yaz_srw_get(odr_encode(), Z_SRW_scan_response); Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response; - + srw_res->diagnostics = diagnostic; srw_res->num_diagnostics = num_diagnostic; send_srw_response(srw_pdu); @@ -2764,6 +3113,7 @@ void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq) void Yaz_Proxy::handle_init(Z_APDU *apdu) { + Z_OtherInformation **oi; get_otherInfoAPDU(apdu, &oi); @@ -2780,10 +3130,10 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu) { if (m_initRequest_mem) nmem_destroy(m_initRequest_mem); - + m_initRequest_apdu = apdu; m_initRequest_mem = odr_extract_mem(odr_decode()); - + m_initRequest_preferredMessageSize = *apdu->u.initRequest-> preferredMessageSize; *apdu->u.initRequest->preferredMessageSize = 1024*1024; @@ -2793,13 +3143,13 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu) Z_CharSetandLanguageNegotiation *charSetandLangRecord = yaz_get_charneg_record(*oi); - + // Save proposal charsets and langs. if (ODR_MASK_GET(apdu->u.initRequest->options, - Z_Options_negotiationModel) + Z_Options_negotiationModel) && charSetandLangRecord) { - + yaz_get_proposal_charneg(m_referenceId_mem, charSetandLangRecord, &m_initRequest_oi_negotiation_charsets, @@ -2807,7 +3157,7 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu) &m_initRequest_oi_negotiation_langs, &m_initRequest_oi_negotiation_num_langs, &m_initRequest_oi_negotiation_selected); - + for (int i = 0; iu.initRequest->options; - - apdu->u.initRequest->options = + + apdu->u.initRequest->options = (Odr_bitmask *)nmem_malloc(m_initRequest_mem, sizeof(Odr_bitmask)); ODR_MASK_ZERO(apdu->u.initRequest->options); @@ -2844,14 +3194,15 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu) Z_Options_concurrentOperations); // make new version m_initRequest_version = apdu->u.initRequest->protocolVersion; - apdu->u.initRequest->protocolVersion = + apdu->u.initRequest->protocolVersion = (Odr_bitmask *)nmem_malloc(m_initRequest_mem, sizeof(Odr_bitmask)); ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion); - + for (i = 0; i<= 8; i++) ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i); } + handle_charset_lang_negotiation(apdu); if (m_client->m_init_flag) { if (handle_init_response_for_invalid_session(apdu)) @@ -2861,37 +3212,42 @@ void Yaz_Proxy::handle_init(Z_APDU *apdu) Z_APDU *apdu2 = m_client->m_initResponse; apdu2->u.initResponse->otherInfo = 0; if (m_client->m_cookie && *m_client->m_cookie) - set_otherInformationString(apdu2, VAL_COOKIE, 1, - m_client->m_cookie); + set_otherInformationString(apdu2, yaz_oid_userinfo_cookie, + 1, m_client->m_cookie); apdu2->u.initResponse->referenceId = apdu->u.initRequest->referenceId; apdu2->u.initResponse->options = m_client->m_initResponse_options; - apdu2->u.initResponse->protocolVersion = + apdu2->u.initResponse->protocolVersion = m_client->m_initResponse_version; - + + handle_charset_lang_negotiation(apdu2); + + if (m_timeout_mode == timeout_busy) + m_timeout_mode = timeout_normal; send_to_client(apdu2); - m_timeout_mode = timeout_normal; return; } } m_client->m_init_flag = 1; - -#if USE_AUTH_MSG - yaz_log(YLOG_LOG, "%suse_auth_msg", m_session_str); - Auth_Msg *m = new Auth_Msg; - m->m_proxy = this; - z_APDU(odr_encode(), &apdu, 0, "encode"); - char *apdu_buf = odr_getbuf(odr_encode(), &m->m_apdu_len, 0); - m->m_apdu_buf = (char*) nmem_malloc(m->m_nmem, m->m_apdu_len); - memcpy(m->m_apdu_buf, apdu_buf, m->m_apdu_len); - odr_reset(odr_encode()); - - inc_ref(); - m_my_thread->put(m); -#else - int ret = handle_authentication(apdu); - result_authentication(apdu, ret); -#endif + + if (m_num_msg_threads && m_my_thread) + { + Auth_Msg *m = new Auth_Msg; + m->m_proxy = this; + z_APDU(odr_encode(), &apdu, 0, "encode"); + char *apdu_buf = odr_getbuf(odr_encode(), &m->m_apdu_len, 0); + m->m_apdu_buf = (char*) nmem_malloc(m->m_nmem, m->m_apdu_len); + memcpy(m->m_apdu_buf, apdu_buf, m->m_apdu_len); + odr_reset(odr_encode()); + + inc_ref(); + m_my_thread->put(m); + } + else + { + int ret = handle_authentication(apdu); + result_authentication(apdu, ret); + } } void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu) @@ -2917,26 +3273,47 @@ void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu) m_mem_invalid_session = odr_extract_mem(odr_decode()); apdu = m_initRequest_apdu; // but throw an init to the target } - + + if (apdu->which == Z_APDU_searchRequest) + m_search_stat.add_bytes(1); + + // Handle global authentication + if (!handle_global_authentication(apdu)) + { + if (m_http_version) + { // HTTP. Send unauthorized + send_http_response(401); + return; + } + else + { + // Z39.50 just shutdown + timeout(0); + return; + } + return; + } + // Determine our client. Z_OtherInformation **oi; get_otherInfoAPDU(apdu, &oi); - m_client = get_client(apdu, get_cookie(oi), get_proxy(oi)); + int http_code = 404; + m_client = get_client(apdu, get_cookie(oi), get_proxy(oi), &http_code); if (!m_client) { if (m_http_version) { // HTTP. Send not found - send_http_response(404); + send_http_response(http_code); return; } else { // Z39.50 just shutdown - delete this; + timeout(0); return; } } - + m_client->m_server = this; if (apdu->which == Z_APDU_initRequest) @@ -2948,7 +3325,7 @@ void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu) void Yaz_Proxy::handle_incoming_Z_PDU_2(Z_APDU *apdu) { handle_max_record_retrieve(apdu); - + if (apdu) apdu = handle_syntax_validation(apdu); @@ -2966,15 +3343,11 @@ void Yaz_Proxy::handle_incoming_Z_PDU_2(Z_APDU *apdu) if (!apdu) { - m_client->timeout(m_target_idletime); // mark it active even + m_client->timeout(m_target_idletime); // mark it active even recv_GDU_more(true); // though we didn't use it return; } - // Add otherInformation entry in APDU if - // negotiation is in use. - if (apdu) - handle_charset_lang_negotiation(apdu); // delete other info construct completely if 0 elements Z_OtherInformation **oi; @@ -3009,7 +3382,7 @@ void Yaz_Proxy::releaseClient() m_proxyTarget = 0; m_flag_invalid_session = 0; // only keep if keep_alive flag is set... - if (m_client && + if (m_client && m_client->m_pdu_recv < m_keepalive_limit_pdu && m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw && m_client->m_waiting == 0) @@ -3028,7 +3401,7 @@ void Yaz_Proxy::releaseClient() } else if (m_client) { - yaz_log (YLOG_LOG, "%sShutdown (client to proxy) close %s", + yaz_log(YLOG_LOG, "%sShutdown (client to proxy) close %s", m_session_str, m_client->get_hostname()); assert (m_client->m_waiting != 2); @@ -3037,13 +3410,13 @@ void Yaz_Proxy::releaseClient() } else if (!m_parent) { - yaz_log (YLOG_LOG, "%sshutdown (client to proxy) bad state", + yaz_log(YLOG_LOG, "%sshutdown (client to proxy) bad state", m_session_str); assert (m_parent); } - else + else { - yaz_log (YLOG_LOG, "%sShutdown (client to proxy)", + yaz_log(YLOG_LOG, "%sShutdown (client to proxy)", m_session_str); } if (m_parent) @@ -3052,8 +3425,7 @@ void Yaz_Proxy::releaseClient() bool Yaz_Proxy::dec_ref() { - yaz_log(YLOG_LOG, "%sdec_ref count=%d", m_session_str, m_ref_count); - assert(m_ref_count > 0); + m_http_keepalive = 0; --m_ref_count; if (m_ref_count > 0) @@ -3065,7 +3437,7 @@ bool Yaz_Proxy::dec_ref() return true; } -const char *Yaz_ProxyClient::get_session_str() +const char *Yaz_ProxyClient::get_session_str() { if (!m_server) return "0 "; @@ -3074,12 +3446,12 @@ const char *Yaz_ProxyClient::get_session_str() void Yaz_ProxyClient::shutdown() { - yaz_log (YLOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(), + yaz_log(YLOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(), get_hostname()); if (m_server) { - m_waiting = 1; // ensure it's released from Proxy in releaseClient + m_waiting = 1; // ensure it's released from Yaz_Proxy::releaseClient m_server->dec_ref(); } else @@ -3089,17 +3461,36 @@ void Yaz_ProxyClient::shutdown() void Yaz_Proxy::failNotify() { inc_request_no(); - yaz_log (YLOG_LOG, "%sConnection closed by client", - get_session_str()); + yaz_log(YLOG_LOG, "%sConnection closed by client", get_session_str()); dec_ref(); } +void Yaz_Proxy::send_response_fail_client(const char *addr) +{ + if (m_http_version) + { + Z_SRW_diagnostic *diagnostic = 0; + int num_diagnostic = 0; + + yaz_add_srw_diagnostic(odr_encode(), + &diagnostic, &num_diagnostic, + YAZ_SRW_SYSTEM_TEMPORARILY_UNAVAILABLE, addr); + if (m_s2z_search_apdu) + send_srw_search_response(diagnostic, num_diagnostic); + else + send_srw_explain_response(diagnostic, num_diagnostic); + } +} + void Yaz_ProxyClient::failNotify() { if (m_server) m_server->inc_request_no(); - yaz_log (YLOG_LOG, "%sConnection closed by target %s", + yaz_log(YLOG_LOG, "%sConnection closed by target %s", get_session_str(), get_hostname()); + + if (m_server) + m_server->send_response_fail_client(get_hostname()); shutdown(); } @@ -3107,7 +3498,7 @@ void Yaz_ProxyClient::connectNotify() { const char *s = get_session_str(); const char *h = get_hostname(); - yaz_log (YLOG_LOG, "%sConnection accepted by %s timeout=%d", s, h, + yaz_log(YLOG_LOG, "%sConnection accepted by %s timeout=%d", s, h, m_target_idletime); timeout(m_target_idletime); if (!m_server) @@ -3128,16 +3519,17 @@ Yaz_ProxyClient::~Yaz_ProxyClient() m_next->m_prev = m_prev; m_waiting = 2; // for debugging purposes only. odr_destroy(m_init_odr); + odr_destroy(m_idAuthentication_odr); delete m_last_query; - xfree (m_last_resultSetId); - xfree (m_cookie); + xfree(m_last_resultSetId); + xfree(m_cookie); } void Yaz_ProxyClient::pre_init_client() { Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest); Z_InitRequest *req = apdu->u.initRequest; - + int i; for (i = 0; i<= 24; i++) ODR_MASK_SET(req->options, i); @@ -3164,8 +3556,9 @@ void Yaz_Proxy::pre_init() int i; const char *name = 0; const char *zurl_in_use[MAX_ZURL_PLEX]; - int limit_bw, limit_pdu, limit_req; + int limit_bw, limit_pdu, limit_req, limit_search; int target_idletime, client_idletime; + int max_sockets; int max_clients; int keepalive_limit_bw, keepalive_limit_pdu; int pre_init; @@ -3185,8 +3578,10 @@ void Yaz_Proxy::pre_init() for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use, &limit_bw, &limit_pdu, &limit_req, + &limit_search, &target_idletime, &client_idletime, - &max_clients, + &max_sockets, + &max_clients, &keepalive_limit_bw, &keepalive_limit_pdu, &pre_init, @@ -3194,6 +3589,7 @@ void Yaz_Proxy::pre_init() &authentication, &negotiation_charset, &negotiation_lang, + 0, 0) ; i++) { if (pre_init) @@ -3228,7 +3624,8 @@ void Yaz_Proxy::pre_init() "sparew=%d preinit=%d",m_session_str, name, zurl_in_use[j], in_use, other, spare, spare_waiting, pre_init); - if (spare + spare_waiting < pre_init) + if (spare + spare_waiting < pre_init + && in_use + spare + spare_waiting + other < max_sockets) { c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this); c->m_next = m_clientPool; @@ -3236,7 +3633,7 @@ void Yaz_Proxy::pre_init() c->m_next->m_prev = &c->m_next; m_clientPool = c; c->m_prev = &m_clientPool; - + if (m_log_mask & PROXY_LOG_APDU_SERVER) c->set_APDU_yazlog(1); else @@ -3269,7 +3666,7 @@ void Yaz_Proxy::timeoutNotify() case timeout_busy: inc_request_no(); m_in_queue.clear(); - yaz_log (YLOG_LOG, "%sTimeout (client to proxy)", m_session_str); + yaz_log(YLOG_LOG, "%sTimeout (client to proxy)", m_session_str); dec_ref(); break; case timeout_reduce: @@ -3303,19 +3700,17 @@ void Yaz_ProxyClient::timeoutNotify() if (m_server) m_server->inc_request_no(); - yaz_log (YLOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(), + yaz_log(YLOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(), get_hostname()); - m_waiting = 1; - m_root->pre_init(); - if (m_server && m_init_flag) - { - // target timed out in a session that was properly initialized - // server object stay alive but we mark it as invalid so it - // gets initialized again - m_server->markInvalid(); - m_server = 0; - } + + if (m_server) + m_server->send_response_fail_client(get_hostname()); + + Yaz_Proxy *proxy_root = m_root; + shutdown(); + + proxy_root->pre_init(); } Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable, @@ -3345,14 +3740,17 @@ Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable, m_seqno = 0; m_target_idletime = 600; m_root = parent; + m_idAuthentication_odr = odr_createmem(ODR_ENCODE); + m_idAuthentication_ber_buf = 0; + m_idAuthentication_ber_size = 0; } const char *Yaz_Proxy::option(const char *name, const char *value) { if (!strcmp (name, "optimize")) { if (value) { - xfree (m_optimize); - m_optimize = xstrdup (value); + xfree(m_optimize); + m_optimize = xstrdup(value); } return m_optimize; } @@ -3393,7 +3791,7 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) m_pdu_recv++; m_waiting = 0; if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER) - yaz_log (YLOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(), + yaz_log(YLOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(), apdu_name(apdu), get_hostname(), len); if (apdu->which == Z_APDU_initResponse) { @@ -3405,15 +3803,28 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) m_initResponse = apdu; m_initResponse_options = apdu->u.initResponse->options; m_initResponse_version = apdu->u.initResponse->protocolVersion; - m_initResponse_preferredMessageSize = + m_initResponse_preferredMessageSize = *apdu->u.initResponse->preferredMessageSize; - m_initResponse_maximumRecordSize = + m_initResponse_maximumRecordSize = *apdu->u.initResponse->maximumRecordSize; Z_InitResponse *ir = apdu->u.initResponse; - char *im0 = ir->implementationName; + + // apply YAZ Proxy version + char *imv0 = ir->implementationVersion; + char *imv1 = (char*) + odr_malloc(m_init_odr, 20 + (imv0 ? strlen(imv0) : 0)); + *imv1 = '\0'; + if (imv0) + strcat(imv1, imv0); +#ifdef VERSION + strcat(imv1, "/" VERSION); +#endif + ir->implementationVersion = imv1; - char *im1 = (char*) + // apply YAZ Proxy implementation name + char *im0 = ir->implementationName; + char *im1 = (char*) odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0)); *im1 = '\0'; if (im0) @@ -3437,7 +3848,7 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) if (status && (!sr->records || sr->records->which == Z_Records_DBOSD)) { m_last_ok = 1; - + if (sr->records && sr->records->which == Z_Records_DBOSD) { m_cache.add(odr_decode(), @@ -3461,7 +3872,7 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) sr->numberOfRecordsReturned = pr->numberOfRecordsReturned; apdu = new_apdu; } - if (pr->records && + if (pr->records && pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint) { m_cache.add(odr_decode(), @@ -3471,7 +3882,7 @@ void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len) } } if (m_cookie) - set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie); + set_otherInformationString(apdu, yaz_oid_userinfo_cookie, 1, m_cookie); Yaz_Proxy *server = m_server; // save it. send_to_client may destroy us @@ -3509,9 +3920,9 @@ int Yaz_Proxy::server(const char *addr) int r = Z_Assoc::server(addr); if (!r) { - yaz_log(YLOG_LOG, "%sStarted proxy " + yaz_log(YLOG_LOG, "%sStarted proxy " #ifdef VERSION - VERSION + VERSION #endif " on %s", m_session_str, addr); timeout(1); @@ -3519,9 +3930,51 @@ int Yaz_Proxy::server(const char *addr) return r; } +void Yaz_Proxy::base64_decode(const char *base64, char *buf, int buf_len) +{ + const char *base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int len = strlen(base64); + int buf_pos = 0; + int index = 1; + + for (int pos = 0; pos <= len; pos++) + { + if (base64[pos] == '=' || buf_pos + 1 >= buf_len) + break; + + const char *ch_ptr = strchr(base64_chars, base64[pos]); + if (!ch_ptr) + break; + char ch = (char) (ch_ptr - base64_chars); + switch (index) + { + case 1: + buf[buf_pos] = ch << 2; + break; + case 2: + buf[buf_pos++] += (ch & 0x30) >> 4; + buf[buf_pos] = (ch & 0x0f) << 4; + break; + case 3: + buf[buf_pos++] += (ch & 0x3c) >> 2; + buf[buf_pos] = (ch & 0x03) << 6; + break; + case 4: + buf[buf_pos++] += ch; + } + if (index < 4) + index++; + else + index = 1; + } + buf[buf_pos] = '\0'; +} + /* * Local variables: * c-basic-offset: 4 + * c-file-style: "Stroustrup" * indent-tabs-mode: nil * End: * vim: shiftwidth=4 tabstop=8 expandtab