Added tnc/tnccs-20-fail-init and tnc/tnccs-20-fail-resp scenarios
[strongswan.git] / src / libtnccs / plugins / tnccs_20 / tnccs_20_client.c
1 /*
2 * Copyright (C) 2015 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "tnccs_20_client.h"
17 #include "messages/pb_tnc_msg.h"
18 #include "messages/ietf/pb_pa_msg.h"
19 #include "messages/ietf/pb_error_msg.h"
20 #include "messages/ietf/pb_assessment_result_msg.h"
21 #include "messages/ietf/pb_access_recommendation_msg.h"
22 #include "messages/ietf/pb_remediation_parameters_msg.h"
23 #include "messages/ietf/pb_reason_string_msg.h"
24 #include "messages/ietf/pb_language_preference_msg.h"
25 #include "messages/ita/pb_mutual_capability_msg.h"
26 #include "messages/ita/pb_noskip_test_msg.h"
27 #include "messages/tcg/pb_pdp_referral_msg.h"
28 #include "state_machine/pb_tnc_state_machine.h"
29
30 #include <tncif_names.h>
31 #include <tncif_pa_subtypes.h>
32
33 #include <tnc/tnc.h>
34 #include <tnc/tnccs/tnccs_manager.h>
35 #include <tnc/imc/imc_manager.h>
36
37 #include <threading/mutex.h>
38 #include <utils/debug.h>
39 #include <collections/linked_list.h>
40 #include <pen/pen.h>
41
42 typedef struct private_tnccs_20_client_t private_tnccs_20_client_t;
43
44 /**
45 * Private data of a tnccs_20_client_t object.
46 */
47 struct private_tnccs_20_client_t {
48
49 /**
50 * Public tnccs_20_client_t interface.
51 */
52 tnccs_20_client_t public;
53
54 /**
55 * PB-TNC State Machine
56 */
57 pb_tnc_state_machine_t *state_machine;
58
59 /**
60 * Connection ID assigned to this TNCCS connection
61 */
62 TNC_ConnectionID connection_id;
63
64 /**
65 * PB-TNC messages to be sent
66 */
67 linked_list_t *messages;
68
69 /**
70 * Type of PB-TNC batch being constructed
71 */
72 pb_tnc_batch_type_t batch_type;
73
74 /**
75 * Maximum PB-TNC batch size
76 */
77 size_t max_batch_len;
78
79 /**
80 * Mutex locking the batch in construction
81 */
82 mutex_t *mutex;
83
84 /**
85 * Flag set while processing
86 */
87 bool fatal_error;
88
89 /**
90 * Flag set by IMC RequestHandshakeRetry() function
91 */
92 bool request_handshake_retry;
93
94 /**
95 * SendMessage() by IMC only allowed if flag is set
96 */
97 bool send_msg;
98
99 /**
100 * PDP server FQDN
101 */
102 chunk_t pdp_server;
103
104 /**
105 * PDP server port
106 */
107 u_int16_t pdp_port;
108
109 /**
110 * Mutual PB-TNC protocol enabled
111 */
112 bool mutual;
113
114 /**
115 * Mutual Capability message sent
116 */
117 bool sent_mutual_capability;
118
119 };
120
121 /**
122 * The following two functions are shared with the tnccs_20_server class
123 */
124 void tnccs_20_handle_ietf_error_msg(pb_tnc_msg_t *msg, bool *fatal_error)
125 {
126 pb_error_msg_t *err_msg;
127 u_int32_t vendor_id;
128 u_int16_t error_code;
129 bool fatal;
130
131 err_msg = (pb_error_msg_t*)msg;
132 fatal = err_msg->get_fatal_flag(err_msg);
133 vendor_id = err_msg->get_vendor_id(err_msg);
134 error_code = err_msg->get_error_code(err_msg);
135
136 if (fatal)
137 {
138 *fatal_error = TRUE;
139 }
140
141 if (vendor_id == PEN_IETF)
142 {
143 switch (error_code)
144 {
145 case PB_ERROR_INVALID_PARAMETER:
146 case PB_ERROR_UNSUPPORTED_MANDATORY_MSG:
147 DBG1(DBG_TNC, "received %s PB-TNC error '%N' (offset %u bytes)",
148 fatal ? "fatal" : "non-fatal",
149 pb_tnc_error_code_names, error_code,
150 err_msg->get_offset(err_msg));
151 break;
152 case PB_ERROR_VERSION_NOT_SUPPORTED:
153 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
154 "caused by bad version 0x%02x",
155 fatal ? "fatal" : "non-fatal",
156 pb_tnc_error_code_names, error_code,
157 err_msg->get_bad_version(err_msg));
158 break;
159 case PB_ERROR_UNEXPECTED_BATCH_TYPE:
160 case PB_ERROR_LOCAL_ERROR:
161 default:
162 DBG1(DBG_TNC, "received %s PB-TNC error '%N'",
163 fatal ? "fatal" : "non-fatal",
164 pb_tnc_error_code_names, error_code);
165 break;
166 }
167 }
168 else
169 {
170 DBG1(DBG_TNC, "received %s PB-TNC error (%u) with Vendor ID 0x%06x",
171 fatal ? "fatal" : "non-fatal", error_code, vendor_id);
172 }
173 }
174
175 bool tnccs_20_handle_ita_mutual_capability_msg(pb_tnc_msg_t *msg)
176 {
177 pb_mutual_capability_msg_t *mutual_msg;
178 uint32_t protocols;
179
180 if (!lib->settings->get_bool(lib->settings,
181 "%s.plugins.tnccs-20.mutual", FALSE, lib->ns))
182 {
183 /* PB-TNC mutual capability disabled, ignore message */
184 return FALSE;
185 }
186
187 mutual_msg = (pb_mutual_capability_msg_t*)msg;
188 protocols = mutual_msg->get_protocols(mutual_msg);
189
190 if (protocols & PB_MUTUAL_HALF_DUPLEX)
191 {
192 DBG1(DBG_TNC, "activating mutual PB-TNC %N protocol",
193 pb_tnc_mutual_protocol_type_names, PB_MUTUAL_HALF_DUPLEX);
194 return TRUE;
195 }
196
197 return FALSE;
198 }
199
200 /**
201 * If the batch type changes then delete all accumulated PB-TNC messages
202 */
203 static void change_batch_type(private_tnccs_20_client_t *this,
204 pb_tnc_batch_type_t batch_type)
205 {
206 pb_tnc_msg_t *msg;
207
208 if (batch_type != this->batch_type)
209 {
210 if (this->batch_type != PB_BATCH_NONE)
211 {
212 DBG1(DBG_TNC, "cancelling PB-TNC %N batch",
213 pb_tnc_batch_type_names, this->batch_type);
214
215 while (this->messages->remove_last(this->messages,
216 (void**)&msg) == SUCCESS)
217 {
218 msg->destroy(msg);
219 }
220 }
221 this->batch_type = batch_type;
222 }
223 }
224
225 /**
226 * Handle a single PB-TNC IETF standard message according to its type
227 */
228 static void handle_ietf_message(private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
229 {
230 pen_type_t msg_type = msg->get_type(msg);
231
232 switch (msg_type.type)
233 {
234 case PB_MSG_EXPERIMENTAL:
235 /* nothing to do */
236 break;
237 case PB_MSG_PA:
238 {
239 pb_pa_msg_t *pa_msg;
240 pen_type_t msg_subtype;
241 u_int16_t imc_id, imv_id;
242 chunk_t msg_body;
243 bool excl;
244 enum_name_t *pa_subtype_names;
245
246 pa_msg = (pb_pa_msg_t*)msg;
247 msg_subtype = pa_msg->get_subtype(pa_msg);
248 msg_body = pa_msg->get_body(pa_msg);
249 imc_id = pa_msg->get_collector_id(pa_msg);
250 imv_id = pa_msg->get_validator_id(pa_msg);
251 excl = pa_msg->get_exclusive_flag(pa_msg);
252
253 pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
254 if (pa_subtype_names)
255 {
256 DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
257 pen_names, msg_subtype.vendor_id, pa_subtype_names,
258 msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
259 }
260 else
261 {
262 DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
263 pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
264 msg_subtype.type);
265 }
266 this->send_msg = TRUE;
267 tnc->imcs->receive_message(tnc->imcs, this->connection_id,
268 excl, msg_body.ptr, msg_body.len,
269 msg_subtype.vendor_id,
270 msg_subtype.type, imv_id, imc_id);
271 this->send_msg = FALSE;
272 break;
273 }
274 case PB_MSG_ASSESSMENT_RESULT:
275 {
276 pb_assessment_result_msg_t *assess_msg;
277 u_int32_t result;
278
279 assess_msg = (pb_assessment_result_msg_t*)msg;
280 result = assess_msg->get_assessment_result(assess_msg);
281 DBG1(DBG_TNC, "PB-TNC assessment result is '%N'",
282 TNC_IMV_Evaluation_Result_names, result);
283 break;
284 }
285 case PB_MSG_ACCESS_RECOMMENDATION:
286 {
287 pb_access_recommendation_msg_t *rec_msg;
288 pb_access_recommendation_code_t rec;
289 TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
290
291 rec_msg = (pb_access_recommendation_msg_t*)msg;
292 rec = rec_msg->get_access_recommendation(rec_msg);
293 DBG1(DBG_TNC, "PB-TNC access recommendation is '%N'",
294 pb_access_recommendation_code_names, rec);
295 switch (rec)
296 {
297 case PB_REC_ACCESS_ALLOWED:
298 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
299 break;
300 case PB_REC_ACCESS_DENIED:
301 state = TNC_CONNECTION_STATE_ACCESS_NONE;
302 break;
303 case PB_REC_QUARANTINED:
304 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
305 }
306 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
307 state);
308 break;
309 }
310 case PB_MSG_REMEDIATION_PARAMETERS:
311 {
312 pb_remediation_parameters_msg_t *rem_msg;
313 pen_type_t parameters_type;
314 chunk_t parameters, string, lang_code;
315
316 rem_msg = (pb_remediation_parameters_msg_t*)msg;
317 parameters_type = rem_msg->get_parameters_type(rem_msg);
318 parameters = rem_msg->get_parameters(rem_msg);
319
320 if (parameters_type.vendor_id == PEN_IETF)
321 {
322 switch (parameters_type.type)
323 {
324 case PB_REMEDIATION_URI:
325 DBG1(DBG_TNC, "remediation uri: %.*s",
326 parameters.len, parameters.ptr);
327 break;
328 case PB_REMEDIATION_STRING:
329 string = rem_msg->get_string(rem_msg, &lang_code);
330 DBG1(DBG_TNC, "remediation string: [%.*s]\n%.*s",
331 lang_code.len, lang_code.ptr,
332 string.len, string.ptr);
333 break;
334 default:
335 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
336 }
337 }
338 else
339 {
340 DBG1(DBG_TNC, "remediation parameters: %B", &parameters);
341 }
342 break;
343 }
344 case PB_MSG_ERROR:
345 tnccs_20_handle_ietf_error_msg(msg, &this->fatal_error);
346 break;
347 case PB_MSG_REASON_STRING:
348 {
349 pb_reason_string_msg_t *reason_msg;
350 chunk_t reason_string, language_code;
351
352 reason_msg = (pb_reason_string_msg_t*)msg;
353 reason_string = reason_msg->get_reason_string(reason_msg);
354 language_code = reason_msg->get_language_code(reason_msg);
355 DBG1(DBG_TNC, "reason string is '%.*s' [%.*s]",
356 (int)reason_string.len, reason_string.ptr,
357 (int)language_code.len, language_code.ptr);
358 break;
359 }
360 default:
361 break;
362 }
363 }
364
365 /**
366 * Handle a single PB-TNC TCG standard message according to its type
367 */
368 static void handle_tcg_message(private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
369 {
370 pen_type_t msg_type = msg->get_type(msg);
371
372 switch (msg_type.type)
373 {
374 case PB_TCG_MSG_PDP_REFERRAL:
375 {
376 pb_pdp_referral_msg_t *pdp_msg;
377 pen_type_t pdp_id_type;
378 u_int8_t pdp_protocol;
379
380 pdp_msg = (pb_pdp_referral_msg_t*)msg;
381 pdp_id_type = pdp_msg->get_identifier_type(pdp_msg);
382
383 if (pdp_id_type.vendor_id == PEN_TCG &&
384 pdp_id_type.type == PB_PDP_ID_FQDN)
385 {
386 this->pdp_server = chunk_clone(pdp_msg->get_fqdn(pdp_msg,
387 &pdp_protocol, &this->pdp_port));
388 if (pdp_protocol != 0)
389 {
390 DBG1(DBG_TNC, "unsupported PDP transport protocol");
391 break;
392 }
393 DBG1(DBG_TNC, "PDP server '%.*s' is listening on port %u",
394 this->pdp_server.len, this->pdp_server.ptr,
395 this->pdp_port);
396 }
397 break;
398 }
399 default:
400 break;
401 }
402 }
403
404 /**
405 * Handle a single PB-TNC ITA standard message according to its type
406 */
407 static void handle_ita_message(private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
408 {
409 pen_type_t msg_type = msg->get_type(msg);
410
411 switch (msg_type.type)
412 {
413 case PB_ITA_MSG_MUTUAL_CAPABILITY:
414 this->mutual = tnccs_20_handle_ita_mutual_capability_msg(msg);
415 break;
416 default:
417 break;
418 }
419 }
420
421 /**
422 * Handle a single PB-TNC message according to its type
423 */
424 static void handle_message(private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
425 {
426 pen_type_t msg_type = msg->get_type(msg);
427
428 switch (msg_type.vendor_id)
429 {
430 case PEN_IETF:
431 handle_ietf_message(this, msg);
432 break;
433 case PEN_TCG:
434 handle_tcg_message(this, msg);
435 break;
436 case PEN_ITA:
437 handle_ita_message(this, msg);
438 break;
439 default:
440 break;
441 }
442 }
443
444 /**
445 * Build a CRETRY batch
446 */
447 static void build_retry_batch(private_tnccs_20_client_t *this)
448 {
449 if (this->batch_type == PB_BATCH_CRETRY)
450 {
451 /* retry batch has already been selected */
452 return;
453 }
454 change_batch_type(this, PB_BATCH_CRETRY);
455 }
456
457 METHOD(tnccs_20_handler_t, process, status_t,
458 private_tnccs_20_client_t *this, pb_tnc_batch_t *batch)
459 {
460 pb_tnc_batch_type_t batch_type;
461 status_t status;
462
463 batch_type = batch->get_type(batch);
464
465 DBG1(DBG_TNC, "processing PB-TNC %N batch for Connection ID %d",
466 pb_tnc_batch_type_names, batch_type, this->connection_id);
467
468 status = batch->process(batch, this->state_machine);
469
470 if (status != FAILED)
471 {
472 enumerator_t *enumerator;
473 pb_tnc_msg_t *msg;
474 bool empty = TRUE;
475
476 if (batch_type == PB_BATCH_SRETRY)
477 {
478 /* Restart the measurements */
479 tnc->imcs->notify_connection_change(tnc->imcs,
480 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
481 this->send_msg = TRUE;
482 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
483 this->send_msg = FALSE;
484 }
485
486 enumerator = batch->create_msg_enumerator(batch);
487 while (enumerator->enumerate(enumerator, &msg))
488 {
489 handle_message(this, msg);
490 empty = FALSE;
491 }
492 enumerator->destroy(enumerator);
493
494 /* received a CLOSE batch from PB-TNC server */
495 if (batch_type == PB_BATCH_CLOSE)
496 {
497 return empty ? SUCCESS : FAILED;
498 }
499
500 this->send_msg = TRUE;
501 tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
502 this->send_msg = FALSE;
503 }
504
505 switch (status)
506 {
507 case FAILED:
508 this->fatal_error = TRUE;
509 status = VERIFY_ERROR;
510 break;
511 case VERIFY_ERROR:
512 break;
513 case SUCCESS:
514 default:
515 status = NEED_MORE;
516 break;
517 }
518
519 return status;
520 }
521
522 METHOD(tnccs_20_handler_t, build, status_t,
523 private_tnccs_20_client_t *this, void *buf, size_t *buflen, size_t *msglen)
524 {
525 status_t status;
526 pb_tnc_state_t state;
527
528 state = this->state_machine->get_state(this->state_machine);
529
530 if (this->fatal_error && state == PB_STATE_END)
531 {
532 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
533 return FAILED;
534 }
535
536 /* Do not allow any asynchronous IMCs to add additional messages */
537 this->mutex->lock(this->mutex);
538
539 if (this->request_handshake_retry)
540 {
541 if (state != PB_STATE_INIT)
542 {
543 build_retry_batch(this);
544 }
545
546 /* Reset the flag for the next handshake retry request */
547 this->request_handshake_retry = FALSE;
548 }
549
550 if (this->batch_type == PB_BATCH_NONE)
551 {
552 switch (state)
553 {
554 case PB_STATE_CLIENT_WORKING:
555 DBG2(DBG_TNC, "no client data to send, "
556 "sending empty PB-TNC CDATA batch");
557 this->batch_type = PB_BATCH_CDATA;
558 break;
559 case PB_STATE_DECIDED:
560 /**
561 * In the DECIDED state and if no CRETRY is under way,
562 * a PB-TNC client replies with an empty CLOSE batch.
563 */
564 this->batch_type = PB_BATCH_CLOSE;
565 break;
566 default:
567 break;
568 }
569 }
570
571 if (this->batch_type != PB_BATCH_NONE)
572 {
573 pb_tnc_batch_t *batch;
574 pb_tnc_msg_t *msg;
575 chunk_t data;
576 int msg_count;
577 enumerator_t *enumerator;
578
579 if (this->state_machine->send_batch(this->state_machine, this->batch_type))
580 {
581 batch = pb_tnc_batch_create(FALSE, this->batch_type,
582 min(this->max_batch_len, *buflen));
583
584 enumerator = this->messages->create_enumerator(this->messages);
585 while (enumerator->enumerate(enumerator, &msg))
586 {
587 if (batch->add_msg(batch, msg))
588 {
589 this->messages->remove_at(this->messages, enumerator);
590 }
591 else
592 {
593 break;
594 }
595 }
596 enumerator->destroy(enumerator);
597
598 batch->build(batch);
599 data = batch->get_encoding(batch);
600 DBG1(DBG_TNC, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
601 pb_tnc_batch_type_names, this->batch_type, data.len,
602 this->connection_id);
603 DBG3(DBG_TNC, "%B", &data);
604
605 *buflen = data.len;
606 *msglen = 0;
607 memcpy(buf, data.ptr, *buflen);
608 batch->destroy(batch);
609
610 msg_count = this->messages->get_count(this->messages);
611 if (msg_count)
612 {
613 DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
614 msg_count, (msg_count == 1) ? "" : "s",
615 pb_tnc_batch_type_names, this->batch_type);
616 }
617 else
618 {
619 this->batch_type = PB_BATCH_NONE;
620 }
621
622 status = ALREADY_DONE;
623 }
624 else
625 {
626 change_batch_type(this, PB_BATCH_NONE);
627 status = INVALID_STATE;
628 }
629 }
630 else
631 {
632 DBG1(DBG_TNC, "no PB-TNC batch to send");
633 status = INVALID_STATE;
634 }
635 this->mutex->unlock(this->mutex);
636
637 return status;
638 }
639
640 METHOD(tnccs_20_handler_t, begin_handshake, void,
641 private_tnccs_20_client_t *this, bool mutual)
642 {
643 pb_tnc_msg_t *msg;
644 char *pref_lang;
645
646 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
647 TNC_CONNECTION_STATE_HANDSHAKE);
648
649 /* Announce PB-TNC Mutual Capability if activated */
650 this->sent_mutual_capability = mutual;
651
652 if (!mutual && lib->settings->get_bool(lib->settings,
653 "%s.plugins.tnccs-20.mutual", FALSE, lib->ns))
654 {
655 pb_tnc_mutual_protocol_type_t protocols;
656
657 protocols = PB_MUTUAL_HALF_DUPLEX;
658 DBG2(DBG_TNC, "proposing PB-TNC mutual %N protocol",
659 pb_tnc_mutual_protocol_type_names, PB_MUTUAL_HALF_DUPLEX);
660 msg = pb_mutual_capability_msg_create(protocols);
661 this->mutex->lock(this->mutex);
662 this->messages->insert_last(this->messages, msg);
663 this->mutex->unlock(this->mutex);
664 this->sent_mutual_capability = TRUE;
665 }
666
667 /* Create PB-TNC Language Preference message */
668 pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
669 msg = pb_language_preference_msg_create(chunk_create(pref_lang,
670 strlen(pref_lang)));
671 this->mutex->lock(this->mutex);
672 this->messages->insert_last(this->messages, msg);
673 this->mutex->unlock(this->mutex);
674
675 this->send_msg = TRUE;
676 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
677 this->send_msg = FALSE;
678
679 /* Send a PB-Noskip-Test message for testing purposes */
680 if (lib->settings->get_bool(lib->settings,
681 "%s.plugins.tnccs-20.tests.pb_tnc_noskip", FALSE, lib->ns))
682 {
683 msg = pb_noskip_test_msg_create();
684 this->mutex->lock(this->mutex);
685 this->messages->insert_last(this->messages, msg);
686 this->mutex->unlock(this->mutex);
687 }
688 }
689
690 METHOD(tnccs_20_handler_t, get_send_flag, bool,
691 private_tnccs_20_client_t *this)
692 {
693 return this->send_msg;
694 }
695
696 METHOD(tnccs_20_handler_t, get_mutual, bool,
697 private_tnccs_20_client_t *this)
698 {
699 return this->mutual;
700 }
701
702 METHOD(tnccs_20_handler_t, get_state, pb_tnc_state_t,
703 private_tnccs_20_client_t *this)
704 {
705 return this->state_machine->get_state(this->state_machine);
706 }
707
708 METHOD(tnccs_20_handler_t, add_msg, void,
709 private_tnccs_20_client_t *this, pb_tnc_msg_t *msg)
710 {
711 /* adding PA message to CDATA batch only */
712 this->mutex->lock(this->mutex);
713 if (this->batch_type == PB_BATCH_NONE)
714 {
715 this->batch_type = PB_BATCH_CDATA;
716 }
717 if (this->batch_type == PB_BATCH_CDATA)
718 {
719 this->messages->insert_last(this->messages, msg);
720 }
721 else
722 {
723 msg->destroy(msg);
724 }
725 this->mutex->unlock(this->mutex);
726 }
727
728 METHOD(tnccs_20_handler_t, handle_errors, void,
729 private_tnccs_20_client_t *this, pb_tnc_batch_t *batch,
730 bool fatal_header_error)
731 {
732 pb_tnc_msg_t *msg;
733 enumerator_t *enumerator;
734
735 if (fatal_header_error || this->fatal_error)
736 {
737 this->mutex->lock(this->mutex);
738 change_batch_type(this, PB_BATCH_CLOSE);
739 this->mutex->unlock(this->mutex);
740 }
741
742 enumerator = batch->create_error_enumerator(batch);
743 while (enumerator->enumerate(enumerator, &msg))
744 {
745 this->mutex->lock(this->mutex);
746 this->messages->insert_last(this->messages, msg->get_ref(msg));
747 this->mutex->unlock(this->mutex);
748 }
749 enumerator->destroy(enumerator);
750 }
751
752 METHOD(tnccs_20_handler_t, destroy, void,
753 private_tnccs_20_client_t *this)
754 {
755 if (this->connection_id)
756 {
757 tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id, FALSE);
758 }
759 this->state_machine->destroy(this->state_machine);
760 this->mutex->destroy(this->mutex);
761 this->messages->destroy_offset(this->messages,
762 offsetof(pb_tnc_msg_t, destroy));
763 free(this->pdp_server.ptr);
764 free(this);
765 }
766
767 METHOD(tnccs_20_client_t, get_pdp_server, chunk_t,
768 private_tnccs_20_client_t *this, u_int16_t *port)
769 {
770 *port = this->pdp_port;
771
772 return this->pdp_server;
773 }
774
775 /**
776 * See header
777 */
778 tnccs_20_handler_t* tnccs_20_client_create(tnccs_t *tnccs,
779 tnccs_send_message_t send_msg,
780 size_t max_batch_len,
781 size_t max_msg_len)
782 {
783 private_tnccs_20_client_t *this;
784
785 INIT(this,
786 .public = {
787 .handler = {
788 .process = _process,
789 .build = _build,
790 .begin_handshake = _begin_handshake,
791 .get_send_flag = _get_send_flag,
792 .get_mutual = _get_mutual,
793 .get_state = _get_state,
794 .add_msg = _add_msg,
795 .handle_errors = _handle_errors,
796 .destroy = _destroy,
797 },
798 .get_pdp_server = _get_pdp_server,
799 },
800 .state_machine = pb_tnc_state_machine_create(FALSE),
801 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
802 .messages = linked_list_create(),
803 .batch_type = PB_BATCH_CDATA,
804 .max_batch_len = max_batch_len,
805 );
806
807 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
808 TNCCS_2_0, tnccs, send_msg,
809 &this->request_handshake_retry,
810 max_msg_len, NULL);
811 if (!this->connection_id)
812 {
813 destroy(this);
814 return NULL;
815 }
816 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
817 TNC_CONNECTION_STATE_CREATE);
818
819 return &this->public.handler;
820 }