2 * Copyright (c) 2004, Index Data.
3 * See the file LICENSE for details.
5 * $Id: zlint.cpp,v 1.3 2004-02-26 23:41:58 adam Exp $
8 #include <yaz/pquery.h>
9 #include <yaz/options.h>
10 #include <yaz/otherinfo.h>
11 #include <yaz/charneg.h>
12 #include <yaz/sortspec.h>
14 #include <yaz++/pdu-assoc.h>
15 #include <yaz++/socket-manager.h>
16 #include <yaz++/z-assoc.h>
18 #define REFID_BUF1 "zlint\000check1"
20 #define REFID_BUF2 "zlint\000check2"
32 virtual Zlint_code init(Zlint *z) = 0;
33 virtual Zlint_code recv_gdu(Zlint *z, Z_GDU *gdu) = 0;
34 virtual Zlint_code recv_fail(Zlint *z, int reason) = 0;
37 const char *try_syntax [] = {
47 const char *try_query[] = {
48 "@attr 1=4 petersson",
49 "@attr 1=1016 petersson",
51 "@attr 1=1016 kingdom",
59 "@attr 1=1016 computer",
67 const char *try_sort [] = {
74 const char *try_scan [] = {
81 #define TEST_STATE_FAIL 0
82 #define TEST_STATE_UNKNOWN 1
83 #define TEST_STATE_OK 2
84 #define TEST_STATE_NOT_APPLIC 3
86 class Zlint : public Yaz_Z_Assoc {
95 int m_record_syntax_no;
97 IYaz_PDU_Observable *m_PDU_Observable;
101 int m_timeout_connect;
102 int m_protocol_version;
103 char m_session_str[20];
105 int initResponseGetVersion(Z_InitResponse *init);
107 void recv_GDU(Z_GDU *apdu, int len);
108 Zlint(IYaz_PDU_Observable *the_PDU_Observable);
109 void args(int argc, char **argv);
110 void connectNotify();
112 void closeNextTest();
116 void timeoutNotify();
117 IYaz_PDU_Observer *sessionNotify(
118 IYaz_PDU_Observable *the_PDU_Observable, int fd);
120 Z_ReferenceId *mk_refid(const char *buf, int len);
123 int Zlint::initResponseGetVersion(Z_InitResponse *init)
128 for (i = 0; i<12; i++)
129 if (ODR_MASK_GET(init->protocolVersion, no))
133 yaz_log(LOG_WARN, "%sbad formatted version");
140 Z_ReferenceId *Zlint::mk_refid(const char *buf, int len)
143 (Z_ReferenceId *) odr_malloc(odr_encode(), sizeof(*id));
144 id->size = id->len = len;
145 id->buf = (unsigned char*) odr_malloc(odr_encode(), len);
146 memcpy(id->buf, buf, len);
150 void Zlint::recv_GDU(Z_GDU *gdu, int len)
152 if (gdu->which != Z_GDU_Z3950)
154 yaz_log(LOG_LOG, "%sreceived non-Z39.50 response", m_session_str);
157 if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
160 Z_InitResponse *init = gdu->u.z3950->u.initResponse;
161 int ver = initResponseGetVersion(init);
162 int result = init->result ? *init->result : 0;
164 yaz_log(LOG_WARN, "%sinit rejected");
168 if (ver > 3 || ver < 2)
169 yaz_log(LOG_WARN, "%sgot version %d, expected 2 or 3",
172 m_test_state = TEST_STATE_OK;
173 m_protocol_version = ver;
184 yaz_log(LOG_WARN, "%sgot version %d, expected 2",
187 m_test_state = TEST_STATE_OK;
191 if (ver < 2 || ver > 5)
192 yaz_log(LOG_WARN, "%sgot version %d, expected 2-5",
195 m_test_state = TEST_STATE_OK;
199 if (!init->referenceId)
200 yaz_log(LOG_WARN, "%smissing referenceID from init response",
202 else if (init->referenceId->len != REFID_LEN1
203 || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
204 yaz_log(LOG_WARN, "%sreference ID does not match");
206 m_test_state = TEST_STATE_OK;
210 if (m_subtst_no == 0)
212 if (!init->referenceId)
213 yaz_log(LOG_WARN, "%smissing referenceID from first init response",
215 else if (init->referenceId->len != REFID_LEN1
216 || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
217 yaz_log(LOG_WARN, "%sreference ID does not match");
222 if (!init->referenceId)
223 yaz_log(LOG_WARN, "%smissing referenceID from second init response",
225 else if (init->referenceId->len != REFID_LEN2
226 || memcmp(init->referenceId->buf, REFID_BUF2, REFID_LEN2))
227 yaz_log(LOG_WARN, "%sreference ID does not match");
229 m_test_state = TEST_STATE_OK;
239 for (i = 0; i <= 24; i++)
240 if (ODR_MASK_GET(init->options, i))
245 yaz_log(LOG_WARN, "%ssuspicuously few option bits set",
248 yaz_log(LOG_WARN, "%ssuspicuously many option bits set",
250 if (no_set >= 2 && no_reset)
251 m_test_state = TEST_STATE_OK;
256 if (ODR_MASK_GET(init->options, Z_Options_negotiationModel))
258 Z_CharSetandLanguageNegotiation *p =
259 yaz_get_charneg_record(init->otherInfo);
263 char *charset=NULL, *lang=NULL;
265 NMEM m = nmem_create();
267 yaz_get_response_charneg(m, p, &charset, &lang,
269 yaz_log(LOG_DEBUG, "%sAccepted character set : %s",
270 m_session_str, charset);
271 yaz_log(LOG_DEBUG, "%sAccepted code language : %s",
272 m_session_str, lang ? lang : "none");
273 yaz_log(LOG_DEBUG, "%sAccepted records in ...: %d",
274 m_session_str, selected );
276 m_test_state = TEST_STATE_OK;
280 m_test_state = TEST_STATE_NOT_APPLIC;
284 if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->maximumRecordSize)
286 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
289 else if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->preferredMessageSize)
291 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
294 else if (m_subtst_no < 3)
302 m_test_state = TEST_STATE_OK;
307 if (result && ODR_MASK_GET(init->options, Z_Options_scan))
311 m_test_state = TEST_STATE_NOT_APPLIC;
316 if (result && ODR_MASK_GET(init->options, Z_Options_sort))
320 m_test_state = TEST_STATE_NOT_APPLIC;
329 m_test_state = TEST_STATE_NOT_APPLIC;
334 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_searchResponse)
336 Z_SearchResponse *sr = gdu->u.z3950->u.searchResponse;
340 if (sr->records && (sr->records->which == Z_Records_NSD
342 sr->records->which == Z_Records_multipleNSD))
344 yaz_log(LOG_WARN, "%sSearch Error", m_session_str);
348 else if (!sr->resultCount || *sr->resultCount == 0)
355 yaz_log(LOG_DEBUG, "%sgot %d result count with %s",
356 m_session_str, *sr->resultCount,
357 try_query[m_query_no]);
358 m_got_result_set = 1;
363 if (sr->resultCount && *sr->resultCount > 0)
365 m_got_result_set = 1;
377 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_presentResponse)
379 Z_PresentResponse *sr = gdu->u.z3950->u.presentResponse;
383 if (sr->records && (sr->records->which == Z_Records_NSD
385 sr->records->which == Z_Records_multipleNSD))
387 yaz_log(LOG_LOG, "%spresent returned NSD for %s",
388 m_session_str, try_syntax[m_record_syntax_no]);
390 else if (sr->records && sr->records->which == Z_Records_DBOSD
391 && sr->records->u.databaseOrSurDiagnostics->num_records>0
392 && sr->records->u.databaseOrSurDiagnostics->records[0])
394 if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_databaseRecord)
396 Z_External *ext = sr->records->u.databaseOrSurDiagnostics->records[0]->u.databaseRecord;
397 Odr_oid *expectRecordSyntax =
398 yaz_str_to_z3950oid(odr_decode(), CLASS_RECSYN,
399 try_syntax[m_record_syntax_no]);
400 if (oid_oidcmp(expectRecordSyntax,
401 ext->direct_reference))
402 yaz_log(LOG_WARN, "%sGot Record in different syntax from that required %s",
404 try_syntax[m_record_syntax_no]);
407 yaz_log(LOG_DEBUG, "%spresent OK for %s", m_session_str,
408 try_syntax[m_record_syntax_no]);
409 m_test_state = TEST_STATE_OK;
412 else if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_surrogateDiagnostic)
413 yaz_log(LOG_DEBUG, "%spresent returned SD %s", m_session_str,
414 try_syntax[m_record_syntax_no]);
416 yaz_log(LOG_WARN, "%spresent returned fragment %s",
418 try_syntax[m_record_syntax_no]);
422 yaz_log(LOG_WARN, "%spresent returned no records or diagnostics", m_session_str);
425 m_record_syntax_no++;
429 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_scanResponse)
431 Z_ScanResponse *sr = gdu->u.z3950->u.scanResponse;
435 if (sr->entries->nonsurrogateDiagnostics)
437 yaz_log(LOG_LOG, "%sscan NSD for %s", m_session_str,
438 try_scan[m_scan_no]);
442 else if (sr->entries->entries && sr->entries->num_entries > 0)
444 yaz_log(LOG_DEBUG, "%sscan OK for %s", m_session_str,
445 try_scan[m_scan_no]);
446 m_test_state = TEST_STATE_OK;
451 yaz_log(LOG_WARN, "%sscan no entries/diagnostics for %s",
453 try_scan[m_scan_no]);
462 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_sortResponse)
464 Z_SortResponse *sr = gdu->u.z3950->u.sortResponse;
470 yaz_log(LOG_LOG, "%ssort NSD for %s", m_session_str,
471 try_sort[m_sort_no]);
477 yaz_log(LOG_DEBUG, "%ssort OK for %s", m_session_str,
478 try_sort[m_sort_no]);
479 m_test_state = TEST_STATE_OK;
491 Zlint::Zlint(IYaz_PDU_Observable *the_PDU_Observable) :
492 Yaz_Z_Assoc(the_PDU_Observable)
494 m_PDU_Observable = the_PDU_Observable;
497 m_timeout_connect = 30;
501 m_protocol_version = 0;
502 sprintf(m_session_str, "%d ", m_tst_no);
505 void Zlint::connectNotify()
507 Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
508 Z_InitRequest *init = apdu->u.initRequest;
510 Z_OtherInformation **oi;
512 timeout(m_timeout_init);
517 /* check if target properly negotiates to v3 .. */
518 ODR_MASK_ZERO(init->protocolVersion);
519 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
520 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
521 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
524 /* check if target properly negotiates to v2 .. */
525 ODR_MASK_ZERO(init->protocolVersion);
526 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
527 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
530 /* check latest version of target - up to v9 */
531 ODR_MASK_ZERO(init->protocolVersion);
533 for (i = 0; i< 9; i++)
534 ODR_MASK_SET(init->protocolVersion, i);
537 /* send refID in init request */
538 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
539 init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
542 /* send double init with differnet refID's */
543 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
544 ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
545 init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
546 send_Z_PDU(apdu, &len);
548 apdu = create_Z_PDU(Z_APDU_initRequest);
549 init = apdu->u.initRequest;
551 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
552 ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
554 init->referenceId = mk_refid(REFID_BUF2, REFID_LEN2);
557 /* set all options.. see what target really supports .. */
558 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
559 ODR_MASK_ZERO(init->options);
560 for (i = 0; i <= 24; i++)
561 ODR_MASK_SET(init->options, i);
564 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
565 yaz_oi_APDU(apdu, &oi);
568 Z_OtherInformationUnit *p0;
569 const char *negotiationCharset[] = {
578 if ((p0=yaz_oi_update(oi, odr_encode(), NULL, 0, 0))) {
579 ODR_MASK_SET(init->options, Z_Options_negotiationModel);
581 p0->which = Z_OtherInfo_externallyDefinedInfo;
582 p0->information.externallyDefinedInfo =
584 yaz_set_proposal_charneg(
586 negotiationCharset, 5,
587 (const char**)&yazLang, yazLang ? 1 : 0, 1);
592 *init->maximumRecordSize = m_subtst_no * m_subtst_no* 100000 + 2000;
593 *init->preferredMessageSize = m_subtst_no * m_subtst_no *100000 + 2000;
597 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
598 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
602 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
603 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
604 ODR_MASK_SET(init->options, Z_Options_scan);
608 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
609 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
610 ODR_MASK_SET(init->options, Z_Options_sort);
613 int r = send_Z_PDU(apdu, &len);
616 int Zlint::nextTest()
622 case TEST_STATE_FAIL:
623 yaz_log(LOG_LOG, "%sTest Failed", m_session_str);
626 yaz_log(LOG_LOG, "%sTest Passed", m_session_str);
628 case TEST_STATE_NOT_APPLIC:
629 yaz_log(LOG_LOG, "%sTest Not Applicable", m_session_str);
631 case TEST_STATE_UNKNOWN:
632 yaz_log(LOG_LOG, "%sTest Could not be performed", m_session_str);
635 m_test_state = TEST_STATE_FAIL;
641 sprintf(m_session_str, "%d ", m_tst_no);
645 yaz_log(LOG_LOG, "%sCheck for init v3",
649 yaz_log(LOG_LOG, "%sCheck for init v2",
653 yaz_log(LOG_LOG, "%sCheck for init protocol version negotiation",
657 yaz_log(LOG_LOG, "%sCheck for init reference ID",
661 yaz_log(LOG_LOG, "%sCheck for double init request",
665 yaz_log(LOG_LOG, "%sCheck for init options",
669 yaz_log(LOG_LOG, "%sCheck for character set negotiation",
673 yaz_log(LOG_LOG, "%sCheck for messages size negotiation",
677 yaz_log(LOG_LOG, "%sCheck for basic search and retrieve",
680 m_record_syntax_no = 0;
681 m_got_result_set = 0;
684 yaz_log(LOG_LOG, "%sCheck for scan", m_session_str);
688 yaz_log(LOG_LOG, "%sCheck for sort", m_session_str);
689 m_got_result_set = 0;
700 // current test failed badly - goto next or stop..
701 void Zlint::closeNextTest()
710 void Zlint::failNotify()
712 yaz_log(LOG_WARN, "%sconnection closed by foreign host", m_session_str);
716 void Zlint::timeoutNotify()
718 yaz_log(LOG_WARN, "%sconnection timed out", m_session_str);
722 void Zlint::testContinue()
728 if (m_got_result_set)
730 // must search again to establish.. keep query
731 m_got_result_set = 0;
732 m_record_syntax_no++;
746 if (m_got_result_set)
748 // if sort test fails during sort, we'll continue to next
749 m_got_result_set = 0;
758 void Zlint::sendTest()
764 if (!m_got_result_set)
766 apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
768 sr = apdu->u.searchRequest;
769 sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
770 if (try_query[m_query_no] && sr)
772 sr->query->which = Z_Query_type_1;
774 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
776 sr->databaseNames = &m_database;
777 sr->num_databaseNames = 1;
779 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
781 yaz_pqf_destroy (pqf_parser);
786 yaz_log(LOG_DEBUG, "%spqf: %s",
787 m_session_str, try_query[m_query_no]);
789 sr->query->u.type_1 = rpn;
790 send_Z_PDU(apdu, &len);
797 yaz_log(LOG_WARN, "%sunable to get any hit count",
802 else if (m_got_result_set && try_syntax[m_record_syntax_no])
805 apdu = zget_APDU(odr_encode(), Z_APDU_presentRequest);
806 Z_PresentRequest *pr = apdu->u.presentRequest;
807 *pr->numberOfRecordsRequested = 1;
808 *pr->resultSetStartPoint = 1;
810 pr->preferredRecordSyntax =
811 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
812 try_syntax[m_record_syntax_no]);
813 send_Z_PDU(apdu, &len);
819 apdu = zget_APDU(odr_encode(), Z_APDU_scanRequest);
820 if (apdu && try_scan[m_scan_no])
823 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
824 Z_ScanRequest *sr = apdu->u.scanRequest;
825 sr->termListAndStartPoint = yaz_pqf_scan(pqf_parser,
828 try_scan[m_scan_no]);
830 sr->databaseNames = &m_database;
831 sr->num_databaseNames = 1;
833 yaz_pqf_destroy (pqf_parser);
834 send_Z_PDU(apdu, &len);
840 if (!m_got_result_set)
842 apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
844 sr = apdu->u.searchRequest;
845 sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
846 if (try_query[m_query_no] && sr)
848 sr->query->which = Z_Query_type_1;
850 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
852 sr->databaseNames = &m_database;
853 sr->num_databaseNames = 1;
855 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
857 yaz_pqf_destroy (pqf_parser);
862 yaz_log(LOG_DEBUG, "%spqf: %s",
863 m_session_str, try_query[m_query_no]);
865 sr->query->u.type_1 = rpn;
866 send_Z_PDU(apdu, &len);
873 yaz_log(LOG_WARN, "%sunable to get any hit count",
880 apdu = zget_APDU(odr_encode(), Z_APDU_sortRequest);
881 if (apdu && try_sort[m_sort_no])
883 char *setstring = "default";
885 Z_SortRequest *sr = apdu->u.sortRequest;
887 sr->num_inputResultSetNames = 1;
888 sr->num_inputResultSetNames = 1;
889 sr->inputResultSetNames = (Z_InternationalString **)
890 odr_malloc (odr_encode(), sizeof(*sr->inputResultSetNames));
891 sr->inputResultSetNames[0] = odr_strdup (odr_encode(), setstring);
892 sr->sortedResultSetName = odr_strdup(odr_encode(), setstring);
893 sr->sortSequence = yaz_sort_spec(odr_encode(), try_sort[m_sort_no]);
894 send_Z_PDU(apdu, &len);
905 IYaz_PDU_Observer *Zlint::sessionNotify(
906 IYaz_PDU_Observable *the_PDU_Observable, int fd)
911 void Zlint::connect()
915 yaz_log(LOG_DEBUG, "%sconnecting to %s", m_session_str, m_host);
916 timeout(m_timeout_connect);
921 void Zlint::args(int argc, char **argv)
925 while ((ret = options("a:v:", argv, argc, &arg)) != -2)
930 yaz_log_init(yaz_log_mask_str(arg), "", 0);
939 m_host = xstrdup(arg);
940 cs_get_host_args(m_host, &basep);
941 if (!basep || !*basep)
943 m_database = xstrdup(basep);
950 int main(int argc, char **argv)
952 Yaz_SocketManager mySocketManager;
953 Zlint z(new Yaz_PDU_Assoc(&mySocketManager));
959 while (mySocketManager.processEvent() > 0)
964 class Zlint_test_01 : public Zlint_test {
968 Zlint_code init(Zlint *z);
969 Zlint_code recv_gdu(Zlint *z, Z_GDU *gdu);
970 Zlint_code recv_fail(Zlint *z, int reason);
973 Zlint_test_01::Zlint_test_01()
977 Zlint_test_01::~Zlint_test_01()
981 Zlint_code Zlint_test_01::init(Zlint *z)
984 Z_APDU *apdu = z->create_Z_PDU(Z_APDU_initRequest);
985 Z_InitRequest *init = apdu->u.initRequest;
987 ODR_MASK_ZERO(init->protocolVersion);
988 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
989 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
990 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
992 int r = z->send_Z_PDU(apdu, &len);
994 return TEST_FINISHED;
995 return TEST_CONTINUE;
998 Zlint_code Zlint_test_01::recv_gdu(Zlint *z, Z_GDU *gdu)
1000 if (gdu->which == Z_GDU_Z3950 &&
1001 gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
1003 Z_InitResponse *init = gdu->u.z3950->u.initResponse;
1004 int ver = z->initResponseGetVersion(init);
1005 int result = init->result ? *init->result : 0;
1006 if (ver > 3 || ver < 2)
1007 yaz_log(LOG_WARN, "got version %d, expected 2 or 3", ver);
1009 return TEST_FINISHED;
1011 return TEST_FINISHED;
1014 Zlint_code Zlint_test_01::recv_fail(Zlint *z, int reason)
1016 return TEST_FINISHED;