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