2 * Copyright (c) 2004, Index Data.
3 * See the file LICENSE for details.
5 * $Id: zlint.cpp,v 1.1 2004-02-18 22:11:41 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 void init_send(Zlint *z) = 0;
33 virtual test_code init_recv(Zlint *z, Z_InitResponse *ir) = 0;
34 virtual test_code other_recv(Zlint *z, Z_APDU *ir, Z_InitResponse *ir) = 0;
38 const char *try_syntax [] = {
48 const char *try_query[] = {
49 "@attr 1=4 petersson",
50 "@attr 1=1016 petersson",
52 "@attr 1=1016 kingdom",
60 "@attr 1=1016 computer",
68 const char *try_sort [] = {
75 const char *try_scan [] = {
82 class Zlint : public Yaz_Z_Assoc {
90 int m_record_syntax_no;
92 IYaz_PDU_Observable *m_PDU_Observable;
96 int m_timeout_connect;
97 int m_protocol_version;
98 char m_session_str[20];
99 int initResponseGetVersion(Z_InitResponse *init);
102 void recv_GDU(Z_GDU *apdu, int len);
103 Zlint(IYaz_PDU_Observable *the_PDU_Observable);
104 void args(int argc, char **argv);
105 void connectNotify();
107 void closeNextTest();
111 void timeoutNotify();
112 IYaz_PDU_Observer *sessionNotify(
113 IYaz_PDU_Observable *the_PDU_Observable, int fd);
115 Z_ReferenceId *mk_refid(const char *buf, int len);
118 int Zlint::initResponseGetVersion(Z_InitResponse *init)
123 for (i = 0; i<12; i++)
124 if (ODR_MASK_GET(init->protocolVersion, no))
128 yaz_log(LOG_WARN, "%sbad formatted version");
135 Z_ReferenceId *Zlint::mk_refid(const char *buf, int len)
138 (Z_ReferenceId *) odr_malloc(odr_encode(), sizeof(*id));
139 id->size = id->len = len;
140 id->buf = (unsigned char*) odr_malloc(odr_encode(), len);
141 memcpy(id->buf, buf, len);
145 void Zlint::recv_GDU(Z_GDU *gdu, int len)
147 yaz_log(LOG_LOG, "%sgot PDU", m_session_str);
148 if (gdu->which != Z_GDU_Z3950)
150 yaz_log(LOG_LOG, "%sreceived non-Z39.50 response", m_session_str);
153 if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_initResponse)
156 Z_InitResponse *init = gdu->u.z3950->u.initResponse;
157 int ver = initResponseGetVersion(init);
158 int result = init->result ? *init->result : 0;
160 yaz_log(LOG_WARN, "%sinit rejected");
164 if (ver > 3 || ver < 2)
165 yaz_log(LOG_WARN, "%sgot version %d, expected 2 or 3",
167 m_protocol_version = ver;
179 yaz_log(LOG_WARN, "%sgot version %d, expected 2",
184 if (ver < 2 || ver > 5)
185 yaz_log(LOG_WARN, "%sgot version %d, expected 2-5",
190 if (!init->referenceId)
191 yaz_log(LOG_WARN, "%smissing referenceID from init response",
193 else if (init->referenceId->len != REFID_LEN1
194 || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
195 yaz_log(LOG_WARN, "%sreference ID does not match");
199 if (m_subtst_no == 0)
201 if (!init->referenceId)
202 yaz_log(LOG_WARN, "%smissing referenceID from first init response",
204 else if (init->referenceId->len != REFID_LEN1
205 || memcmp(init->referenceId->buf, REFID_BUF1, REFID_LEN1))
206 yaz_log(LOG_WARN, "%sreference ID does not match");
211 if (!init->referenceId)
212 yaz_log(LOG_WARN, "%smissing referenceID from second init response",
214 else if (init->referenceId->len != REFID_LEN2
215 || memcmp(init->referenceId->buf, REFID_BUF2, REFID_LEN2))
216 yaz_log(LOG_WARN, "%sreference ID does not match");
226 for (i = 0; i <= 24; i++)
227 if (ODR_MASK_GET(init->options, i))
232 yaz_log(LOG_WARN, "%ssuspicuously few option bits set",
235 yaz_log(LOG_WARN, "%ssuspicuously many option bits set",
241 if (ODR_MASK_GET(init->options, Z_Options_negotiationModel))
243 Z_CharSetandLanguageNegotiation *p =
244 yaz_get_charneg_record(init->otherInfo);
248 char *charset=NULL, *lang=NULL;
250 NMEM m = nmem_create();
252 yaz_get_response_charneg(m, p, &charset, &lang,
254 yaz_log(LOG_LOG, "%sAccepted character set : %s",
255 m_session_str, charset);
256 yaz_log(LOG_LOG, "%sAccepted code language : %s",
257 m_session_str, lang ? lang : "none");
258 yaz_log(LOG_LOG, "%sAccepted records in ...: %d",
259 m_session_str, selected );
266 if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->maximumRecordSize)
267 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
269 if (m_subtst_no * m_subtst_no * 100000 + 2000 < *init->preferredMessageSize)
270 yaz_log(LOG_WARN, "%smaximumRecordSize bigger than proposed size");
281 if (result && ODR_MASK_GET(init->options, Z_Options_scan))
287 if (result && ODR_MASK_GET(init->options, Z_Options_sort))
299 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_searchResponse)
301 Z_SearchResponse *sr = gdu->u.z3950->u.searchResponse;
305 if (sr->records && (sr->records->which == Z_Records_NSD
307 sr->records->which == Z_Records_multipleNSD))
309 yaz_log(LOG_WARN, "%sSearch Error", m_session_str);
313 else if (!sr->resultCount || *sr->resultCount == 0)
320 yaz_log(LOG_LOG, "%sgot %d result count with %s",
321 m_session_str, *sr->resultCount,
322 try_query[m_query_no]);
323 m_got_result_set = 1;
331 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_presentResponse)
333 Z_PresentResponse *sr = gdu->u.z3950->u.presentResponse;
337 if (sr->records && (sr->records->which == Z_Records_NSD
339 sr->records->which == Z_Records_multipleNSD))
341 yaz_log(LOG_LOG, "%spresent returned NSD for %s",
342 m_session_str, try_syntax[m_record_syntax_no]);
344 else if (sr->records && sr->records->which == Z_Records_DBOSD
345 && sr->records->u.databaseOrSurDiagnostics->num_records>0
346 && sr->records->u.databaseOrSurDiagnostics->records[0])
348 if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_databaseRecord)
350 Z_External *ext = sr->records->u.databaseOrSurDiagnostics->records[0]->u.databaseRecord;
351 Odr_oid *expectRecordSyntax =
352 yaz_str_to_z3950oid(odr_decode(), CLASS_RECSYN,
353 try_syntax[m_record_syntax_no]);
354 if (oid_oidcmp(expectRecordSyntax,
355 ext->direct_reference))
356 yaz_log(LOG_WARN, "%spresent bad record type for %s",
358 try_syntax[m_record_syntax_no]);
360 yaz_log(LOG_LOG, "%spresent OK for %s", m_session_str,
361 try_syntax[m_record_syntax_no]);
363 else if (sr->records->u.databaseOrSurDiagnostics->records[0]->which == Z_NamePlusRecord_surrogateDiagnostic)
364 yaz_log(LOG_LOG, "%spresent returned SD %s", m_session_str,
365 try_syntax[m_record_syntax_no]);
367 yaz_log(LOG_WARN, "%spresent returned fragment %s",
369 try_syntax[m_record_syntax_no]);
373 yaz_log(LOG_WARN, "%spresent returned no records or diagnostics", m_session_str);
376 m_record_syntax_no++;
380 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_scanResponse)
382 Z_ScanResponse *sr = gdu->u.z3950->u.scanResponse;
386 if (sr->entries->nonsurrogateDiagnostics)
388 yaz_log(LOG_LOG, "%sscan NSD for %s", m_session_str,
389 try_scan[m_scan_no]);
393 else if (sr->entries->entries && sr->entries->num_entries > 0)
395 yaz_log(LOG_LOG, "%sscan OK for %s", m_session_str,
396 try_scan[m_scan_no]);
401 yaz_log(LOG_WARN, "%sscan no entries/diagnostics for %s",
403 try_scan[m_scan_no]);
412 else if (gdu->u.z3950 && gdu->u.z3950->which == Z_APDU_sortResponse)
414 Z_SortResponse *sr = gdu->u.z3950->u.sortResponse;
420 yaz_log(LOG_LOG, "%ssort NSD for %s", m_session_str,
421 try_sort[m_sort_no]);
427 yaz_log(LOG_LOG, "%ssort OK for %s", m_session_str,
428 try_sort[m_sort_no]);
440 Zlint::Zlint(IYaz_PDU_Observable *the_PDU_Observable) :
441 Yaz_Z_Assoc(the_PDU_Observable)
443 m_PDU_Observable = the_PDU_Observable;
446 m_timeout_connect = 30;
450 m_protocol_version = 0;
451 sprintf(m_session_str, "%d ", m_tst_no);
454 void Zlint::connectNotify()
456 Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
457 Z_InitRequest *init = apdu->u.initRequest;
459 Z_OtherInformation **oi;
461 timeout(m_timeout_init);
466 /* check if target properly negotiates to v3 .. */
467 ODR_MASK_ZERO(init->protocolVersion);
468 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
469 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
470 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
473 /* check if target properly negotiates to v2 .. */
474 ODR_MASK_ZERO(init->protocolVersion);
475 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_1);
476 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_2);
479 /* check latest version of target - up to v9 */
480 ODR_MASK_ZERO(init->protocolVersion);
482 for (i = 0; i< 9; i++)
483 ODR_MASK_SET(init->protocolVersion, i);
486 /* send refID in init request */
487 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
488 init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
491 /* send double init with differnet refID's */
492 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
493 ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
494 init->referenceId = mk_refid(REFID_BUF1, REFID_LEN1);
495 send_Z_PDU(apdu, &len);
497 apdu = create_Z_PDU(Z_APDU_initRequest);
498 init = apdu->u.initRequest;
500 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
501 ODR_MASK_SET(init->options, Z_Options_concurrentOperations);
503 init->referenceId = mk_refid(REFID_BUF2, REFID_LEN2);
506 /* set all options.. see what target really supports .. */
507 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
508 ODR_MASK_ZERO(init->options);
509 for (i = 0; i <= 24; i++)
510 ODR_MASK_SET(init->options, i);
513 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
514 yaz_oi_APDU(apdu, &oi);
517 Z_OtherInformationUnit *p0;
518 const char *negotiationCharset[] = {
527 if ((p0=yaz_oi_update(oi, odr_encode(), NULL, 0, 0))) {
528 ODR_MASK_SET(init->options, Z_Options_negotiationModel);
530 p0->which = Z_OtherInfo_externallyDefinedInfo;
531 p0->information.externallyDefinedInfo =
533 yaz_set_proposal_charneg(
535 negotiationCharset, 5,
536 (const char**)&yazLang, yazLang ? 1 : 0, 1);
541 *init->maximumRecordSize = m_subtst_no * m_subtst_no* 100000 + 2000;
542 *init->preferredMessageSize = m_subtst_no * m_subtst_no *100000 + 2000;
546 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
547 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
551 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
552 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
553 ODR_MASK_SET(init->options, Z_Options_scan);
557 ODR_MASK_SET(init->protocolVersion, Z_ProtocolVersion_3);
558 ODR_MASK_SET(init->options, Z_Options_namedResultSets);
559 ODR_MASK_SET(init->options, Z_Options_sort);
562 int r = send_Z_PDU(apdu, &len);
565 int Zlint::nextTest()
571 sprintf(m_session_str, "%d ", m_tst_no);
592 m_record_syntax_no = 0;
593 m_got_result_set = 0;
608 // current test failed badly - goto next or stop..
609 void Zlint::closeNextTest()
619 void Zlint::failNotify()
621 yaz_log(LOG_WARN, "%sconnection closed by foreign host", m_session_str);
625 void Zlint::timeoutNotify()
627 yaz_log(LOG_WARN, "%sconnection timed out", m_session_str);
631 void Zlint::testContinue()
637 if (m_got_result_set)
639 // must search again to establish.. keep query
640 m_got_result_set = 0;
641 m_record_syntax_no++;
658 void Zlint::sendTest()
664 if (m_got_result_set == 0)
666 apdu = zget_APDU(odr_encode(), Z_APDU_searchRequest);
668 sr = apdu->u.searchRequest;
669 sr->query = (Z_Query *) odr_malloc(odr_encode(), sizeof(*sr->query));
670 if (try_query[m_query_no] && sr)
672 sr->query->which = Z_Query_type_1;
674 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
676 sr->databaseNames = &m_database;
677 sr->num_databaseNames = 1;
679 rpn = yaz_pqf_parse(pqf_parser, odr_encode(), try_query[m_query_no]);
681 yaz_pqf_destroy (pqf_parser);
686 yaz_log(LOG_LOG, "%spqf: %s",
687 m_session_str, try_query[m_query_no]);
689 sr->query->u.type_1 = rpn;
690 send_Z_PDU(apdu, &len);
697 yaz_log(LOG_WARN, "%sunable to get any hit count",
702 else if (m_got_result_set && try_syntax[m_record_syntax_no])
705 apdu = zget_APDU(odr_encode(), Z_APDU_presentRequest);
706 Z_PresentRequest *pr = apdu->u.presentRequest;
707 *pr->numberOfRecordsRequested = 1;
708 *pr->resultSetStartPoint = 1;
710 pr->preferredRecordSyntax =
711 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
712 try_syntax[m_record_syntax_no]);
713 send_Z_PDU(apdu, &len);
719 apdu = zget_APDU(odr_encode(), Z_APDU_scanRequest);
720 if (apdu && try_scan[m_scan_no])
723 YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
724 Z_ScanRequest *sr = apdu->u.scanRequest;
725 sr->termListAndStartPoint = yaz_pqf_scan(pqf_parser,
728 try_scan[m_scan_no]);
730 sr->databaseNames = &m_database;
731 sr->num_databaseNames = 1;
733 yaz_pqf_destroy (pqf_parser);
734 send_Z_PDU(apdu, &len);
740 apdu = zget_APDU(odr_encode(), Z_APDU_sortRequest);
741 if (apdu && try_sort[m_sort_no])
743 char *setstring = "default";
745 Z_SortRequest *sr = apdu->u.sortRequest;
747 sr->num_inputResultSetNames = 1;
748 sr->num_inputResultSetNames = 1;
749 sr->inputResultSetNames = (Z_InternationalString **)
750 odr_malloc (odr_encode(), sizeof(*sr->inputResultSetNames));
751 sr->inputResultSetNames[0] = odr_strdup (odr_encode(), setstring);
752 sr->sortedResultSetName = odr_strdup(odr_encode(), setstring);
753 sr->sortSequence = yaz_sort_spec(odr_encode(), try_sort[m_sort_no]);
754 send_Z_PDU(apdu, &len);
764 IYaz_PDU_Observer *Zlint::sessionNotify(
765 IYaz_PDU_Observable *the_PDU_Observable, int fd)
770 void Zlint::connect()
772 if (m_host && m_tst_no != -1)
774 yaz_log(LOG_LOG, "%sconnecting to %s", m_session_str, m_host);
775 timeout(m_timeout_connect);
780 void Zlint::args(int argc, char **argv)
784 while ((ret = options("v", argv, argc, &arg)) != -2)
794 m_host = xstrdup(arg);
795 cs_get_host_args(m_host, &basep);
796 if (!basep || !*basep)
798 m_database = xstrdup(basep);
805 int main(int argc, char **argv)
807 Yaz_SocketManager mySocketManager;
808 Zlint z(new Yaz_PDU_Assoc(&mySocketManager));
813 while (mySocketManager.processEvent() > 0)