2 * Copyright (C) 2015 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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>.
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
19 #include "imv_hcd_agent.h"
20 #include "imv_hcd_state.h"
23 #include <imv/imv_agent.h>
24 #include <imv/imv_msg.h>
25 #include <generic/generic_attr_bool.h>
26 #include <generic/generic_attr_chunk.h>
27 #include <generic/generic_attr_string.h>
28 #include <ietf/ietf_attr.h>
29 #include <ietf/ietf_attr_attr_request.h>
30 #include <ietf/ietf_attr_fwd_enabled.h>
31 #include <pwg/pwg_attr.h>
32 #include <pwg/pwg_attr_vendor_smi_code.h>
33 #include "tcg/seg/tcg_seg_attr_max_size.h"
34 #include "tcg/seg/tcg_seg_attr_seg_env.h"
36 #include <tncif_names.h>
37 #include <tncif_pa_subtypes.h>
40 #include <utils/debug.h>
42 #define HCD_MAX_ATTR_SIZE 10000000
44 typedef struct private_imv_hcd_agent_t private_imv_hcd_agent_t
;
46 /* Subscribed PA-TNC message subtypes */
47 static pen_type_t msg_types
[] = {
48 { PEN_IETF
, PA_SUBTYPE_IETF_OPERATING_SYSTEM
},
49 { PEN_PWG
, PA_SUBTYPE_PWG_HCD_SYSTEM
},
50 { PEN_PWG
, PA_SUBTYPE_PWG_HCD_CONSOLE
},
51 { PEN_PWG
, PA_SUBTYPE_PWG_HCD_MARKER
},
52 { PEN_PWG
, PA_SUBTYPE_PWG_HCD_FINISHER
},
53 { PEN_PWG
, PA_SUBTYPE_PWG_HCD_INTERFACE
},
54 { PEN_PWG
, PA_SUBTYPE_PWG_HCD_SCANNER
}
57 static imv_hcd_attr_t
attr_type_to_flag(pwg_attr_t attr_type
)
61 case PWG_HCD_DEFAULT_PWD_ENABLED
:
62 return IMV_HCD_ATTR_DEFAULT_PWD_ENABLED
;
63 case PWG_HCD_FIREWALL_SETTING
:
64 return IMV_HCD_ATTR_FIREWALL_SETTING
;
65 case PWG_HCD_FORWARDING_ENABLED
:
66 return IMV_HCD_ATTR_FORWARDING_ENABLED
;
67 case PWG_HCD_MACHINE_TYPE_MODEL
:
68 return IMV_HCD_ATTR_MACHINE_TYPE_MODEL
;
69 case PWG_HCD_PSTN_FAX_ENABLED
:
70 return IMV_HCD_ATTR_PSTN_FAX_ENABLED
;
71 case PWG_HCD_TIME_SOURCE
:
72 return IMV_HCD_ATTR_TIME_SOURCE
;
73 case PWG_HCD_USER_APP_ENABLED
:
74 return IMV_HCD_ATTR_USER_APP_ENABLED
;
75 case PWG_HCD_USER_APP_PERSIST_ENABLED
:
76 return IMV_HCD_ATTR_USER_APP_PERSIST_ENABLED
;
77 case PWG_HCD_VENDOR_NAME
:
78 return IMV_HCD_ATTR_VENDOR_NAME
;
79 case PWG_HCD_VENDOR_SMI_CODE
:
80 return IMV_HCD_ATTR_VENDOR_SMI_CODE
;
81 case PWG_HCD_CERTIFICATION_STATE
:
82 return IMV_HCD_ATTR_CERTIFICATION_STATE
;
83 case PWG_HCD_CONFIGURATION_STATE
:
84 return IMV_HCD_ATTR_CONFIGURATION_STATE
;
85 case PWG_HCD_ATTRS_NATURAL_LANG
:
86 return IMV_HCD_ATTR_NATURAL_LANG
;
87 case PWG_HCD_FIRMWARE_NAME
:
88 return IMV_HCD_ATTR_FIRMWARE_NAME
;
89 case PWG_HCD_RESIDENT_APP_NAME
:
90 return IMV_HCD_ATTR_RESIDENT_APP_NAME
;
91 case PWG_HCD_USER_APP_NAME
:
92 return IMV_HCD_ATTR_USER_APP_NAME
;
94 return IMV_HCD_ATTR_NONE
;
99 * Private data of an imv_hcd_agent_t object.
101 struct private_imv_hcd_agent_t
{
104 * Public members of imv_hcd_agent_t
106 imv_agent_if_t
public;
109 * IMV agent responsible for generic functions
115 METHOD(imv_agent_if_t
, bind_functions
, TNC_Result
,
116 private_imv_hcd_agent_t
*this, TNC_TNCS_BindFunctionPointer bind_function
)
118 return this->agent
->bind_functions(this->agent
, bind_function
);
121 METHOD(imv_agent_if_t
, notify_connection_change
, TNC_Result
,
122 private_imv_hcd_agent_t
*this, TNC_ConnectionID id
,
123 TNC_ConnectionState new_state
)
125 TNC_IMV_Action_Recommendation rec
;
127 imv_session_t
*session
;
131 case TNC_CONNECTION_STATE_CREATE
:
132 state
= imv_hcd_state_create(id
);
133 return this->agent
->create_state(this->agent
, state
);
134 case TNC_CONNECTION_STATE_DELETE
:
135 return this->agent
->delete_state(this->agent
, id
);
136 case TNC_CONNECTION_STATE_ACCESS_ALLOWED
:
137 case TNC_CONNECTION_STATE_ACCESS_ISOLATED
:
138 case TNC_CONNECTION_STATE_ACCESS_NONE
:
139 if (this->agent
->get_state(this->agent
, id
, &state
) && imcv_db
)
141 session
= state
->get_session(state
);
143 if (session
->get_policy_started(session
))
147 case TNC_CONNECTION_STATE_ACCESS_ALLOWED
:
148 rec
= TNC_IMV_ACTION_RECOMMENDATION_ALLOW
;
150 case TNC_CONNECTION_STATE_ACCESS_ISOLATED
:
151 rec
= TNC_IMV_ACTION_RECOMMENDATION_ISOLATE
;
153 case TNC_CONNECTION_STATE_ACCESS_NONE
:
155 rec
= TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS
;
157 imcv_db
->add_recommendation(imcv_db
, session
, rec
);
158 if (!imcv_db
->policy_script(imcv_db
, session
, FALSE
))
160 DBG1(DBG_IMV
, "error in policy script stop");
164 /* fall through to default state */
166 return this->agent
->change_state(this->agent
, id
, new_state
, NULL
);
171 * Process a received message
173 static TNC_Result
receive_msg(private_imv_hcd_agent_t
*this, imv_state_t
*state
,
177 imv_hcd_state_t
*hcd_state
;
179 enum_name_t
*pa_subtype_names
;
180 pen_type_t type
, msg_type
;
182 bool fatal_error
= FALSE
, assessment
= FALSE
;
183 enumerator_t
*enumerator
;
185 hcd_state
= (imv_hcd_state_t
*)state
;
187 /* generate an outgoing PA-TNC message - we might need it */
188 out_msg
= imv_msg_create_as_reply(in_msg
);
190 /* parse received PA-TNC message and handle local and remote errors */
191 result
= in_msg
->receive(in_msg
,out_msg
, &fatal_error
);
192 if (result
!= TNC_RESULT_SUCCESS
)
194 out_msg
->destroy(out_msg
);
197 msg_type
= in_msg
->get_msg_type(in_msg
);
198 pa_subtype_names
= get_pa_subtype_names(msg_type
.vendor_id
);
199 DBG2(DBG_IMV
, "received attributes for PA subtype %N/%N",
200 pen_names
, msg_type
.vendor_id
, pa_subtype_names
, msg_type
.type
);
202 /* set current subtype */
203 if (msg_type
.vendor_id
== PEN_IETF
)
205 hcd_state
->set_subtype(hcd_state
, PA_SUBTYPE_PWG_HCD_SYSTEM
);
209 hcd_state
->set_subtype(hcd_state
, msg_type
.type
);
212 /* analyze PA-TNC attributes */
213 enumerator
= in_msg
->create_attribute_enumerator(in_msg
);
214 while (enumerator
->enumerate(enumerator
, &attr
))
216 type
= attr
->get_type(attr
);
218 if (type
.vendor_id
== PEN_IETF
)
222 case IETF_ATTR_FORWARDING_ENABLED
:
224 ietf_attr_fwd_enabled_t
*attr_cast
;
225 os_fwd_status_t fwd_status
;
227 attr_cast
= (ietf_attr_fwd_enabled_t
*)attr
;
228 fwd_status
= attr_cast
->get_status(attr_cast
);
229 DBG2(DBG_IMV
, " %N: %N", ietf_attr_names
, type
.type
,
230 os_fwd_status_names
, fwd_status
);
231 state
->set_action_flags(state
,
232 IMV_HCD_ATTR_FORWARDING_ENABLED
);
235 case IETF_ATTR_FACTORY_DEFAULT_PWD_ENABLED
:
237 generic_attr_bool_t
*attr_cast
;
240 attr_cast
= (generic_attr_bool_t
*)attr
;
241 status
= attr_cast
->get_status(attr_cast
);
242 DBG2(DBG_IMV
, " %N: %s", ietf_attr_names
, type
.type
,
243 status ?
"yes" : "no");
244 state
->set_action_flags(state
,
245 IMV_HCD_ATTR_DEFAULT_PWD_ENABLED
);
252 else if (type
.vendor_id
== PEN_PWG
)
254 state
->set_action_flags(state
, attr_type_to_flag(type
.type
));
258 case PWG_HCD_ATTRS_NATURAL_LANG
:
259 case PWG_HCD_MACHINE_TYPE_MODEL
:
260 case PWG_HCD_VENDOR_NAME
:
261 case PWG_HCD_TIME_SOURCE
:
262 case PWG_HCD_FIRMWARE_NAME
:
263 case PWG_HCD_FIRMWARE_STRING_VERSION
:
264 case PWG_HCD_RESIDENT_APP_NAME
:
265 case PWG_HCD_RESIDENT_APP_STRING_VERSION
:
266 case PWG_HCD_USER_APP_NAME
:
267 case PWG_HCD_USER_APP_STRING_VERSION
:
271 value
= attr
->get_value(attr
);
272 DBG2(DBG_IMV
, " %N: %.*s", pwg_attr_names
, type
.type
,
273 value
.len
, value
.ptr
);
276 case PWG_HCD_FIRMWARE_PATCHES
:
277 case PWG_HCD_RESIDENT_APP_PATCHES
:
278 case PWG_HCD_USER_APP_PATCHES
:
283 value
= attr
->get_value(attr
);
286 /* remove any trailing LF from patches string */
287 if (len
&& (value
.ptr
[len
- 1] == '\n'))
291 DBG2(DBG_IMV
, " %N:%s%.*s", pwg_attr_names
, type
.type
,
292 len ?
"\n" : " ", len
, value
.ptr
);
295 case PWG_HCD_FIRMWARE_VERSION
:
296 case PWG_HCD_RESIDENT_APP_VERSION
:
297 case PWG_HCD_USER_APP_VERSION
:
301 value
= attr
->get_value(attr
);
302 DBG2(DBG_IMV
, " %N: %#B", pwg_attr_names
, type
.type
, &value
);
305 case PWG_HCD_CERTIFICATION_STATE
:
306 case PWG_HCD_CONFIGURATION_STATE
:
310 value
= attr
->get_value(attr
);
311 DBG2(DBG_IMV
, " %N: %B", pwg_attr_names
, type
.type
, &value
);
314 case PWG_HCD_DEFAULT_PWD_ENABLED
:
315 case PWG_HCD_PSTN_FAX_ENABLED
:
316 case PWG_HCD_USER_APP_ENABLED
:
317 case PWG_HCD_USER_APP_PERSIST_ENABLED
:
319 generic_attr_bool_t
*attr_cast
;
322 attr_cast
= (generic_attr_bool_t
*)attr
;
323 status
= attr_cast
->get_status(attr_cast
);
324 DBG2(DBG_IMV
, " %N: %s", pwg_attr_names
, type
.type
,
325 status ?
"yes" : "no");
327 if (type
.type
== PWG_HCD_USER_APP_ENABLED
&& !status
)
329 /* do not request user applications */
330 hcd_state
->set_user_app_disabled(hcd_state
);
334 case PWG_HCD_FORWARDING_ENABLED
:
336 ietf_attr_fwd_enabled_t
*attr_cast
;
337 os_fwd_status_t fwd_status
;
339 attr_cast
= (ietf_attr_fwd_enabled_t
*)attr
;
340 fwd_status
= attr_cast
->get_status(attr_cast
);
341 DBG2(DBG_IMV
, " %N: %N", pwg_attr_names
, type
.type
,
342 os_fwd_status_names
, fwd_status
);
346 case PWG_HCD_VENDOR_SMI_CODE
:
348 pwg_attr_vendor_smi_code_t
*attr_cast
;
351 attr_cast
= (pwg_attr_vendor_smi_code_t
*)attr
;
352 smi_code
= attr_cast
->get_vendor_smi_code(attr_cast
);
353 DBG2(DBG_IMV
, " %N: 0x%06x (%u)", pwg_attr_names
, type
.type
,
362 enumerator
->destroy(enumerator
);
366 state
->set_recommendation(state
,
367 TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
,
368 TNC_IMV_EVALUATION_RESULT_ERROR
);
374 hcd_state
->set_handshake_state(hcd_state
, IMV_HCD_STATE_END
);
375 result
= out_msg
->send_assessment(out_msg
);
376 if (result
== TNC_RESULT_SUCCESS
)
378 result
= this->agent
->provide_recommendation(this->agent
, state
);
383 /* send PA-TNC message with the EXCL flag set */
384 result
= out_msg
->send(out_msg
, TRUE
);
386 out_msg
->destroy(out_msg
);
391 METHOD(imv_agent_if_t
, receive_message
, TNC_Result
,
392 private_imv_hcd_agent_t
*this, TNC_ConnectionID id
,
393 TNC_MessageType msg_type
, chunk_t msg
)
399 if (!this->agent
->get_state(this->agent
, id
, &state
))
401 return TNC_RESULT_FATAL
;
403 in_msg
= imv_msg_create_from_data(this->agent
, state
, id
, msg_type
, msg
);
404 result
= receive_msg(this, state
, in_msg
);
405 in_msg
->destroy(in_msg
);
410 METHOD(imv_agent_if_t
, receive_message_long
, TNC_Result
,
411 private_imv_hcd_agent_t
*this, TNC_ConnectionID id
,
412 TNC_UInt32 src_imc_id
, TNC_UInt32 dst_imv_id
,
413 TNC_VendorID msg_vid
, TNC_MessageSubtype msg_subtype
, chunk_t msg
)
419 if (!this->agent
->get_state(this->agent
, id
, &state
))
421 return TNC_RESULT_FATAL
;
423 in_msg
= imv_msg_create_from_long_data(this->agent
, state
, id
,
424 src_imc_id
, dst_imv_id
, msg_vid
, msg_subtype
, msg
);
425 result
= receive_msg(this, state
, in_msg
);
426 in_msg
->destroy(in_msg
);
433 * Build an IETF Attribute Request attribute for missing attributes
435 static pa_tnc_attr_t
* build_attr_request(uint32_t received
)
438 ietf_attr_attr_request_t
*attr_cast
;
440 attr
= ietf_attr_attr_request_create(PEN_RESERVED
, 0);
441 attr_cast
= (ietf_attr_attr_request_t
*)attr
;
443 if (!(received
& IMV_HCD_ATTR_NATURAL_LANG
))
445 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_ATTRS_NATURAL_LANG
);
447 if (!(received
& IMV_HCD_ATTR_DEFAULT_PWD_ENABLED
))
449 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_DEFAULT_PWD_ENABLED
);
451 if (!(received
& IMV_HCD_ATTR_FIREWALL_SETTING
))
453 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_FIREWALL_SETTING
);
455 if (!(received
& IMV_HCD_ATTR_FIRMWARE_NAME
))
457 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_FIRMWARE_NAME
);
459 if (!(received
& IMV_HCD_ATTR_FORWARDING_ENABLED
))
461 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_FORWARDING_ENABLED
);
463 if (!(received
& IMV_HCD_ATTR_MACHINE_TYPE_MODEL
))
465 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_MACHINE_TYPE_MODEL
);
467 if (!(received
& IMV_HCD_ATTR_PSTN_FAX_ENABLED
))
469 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_PSTN_FAX_ENABLED
);
471 if (!(received
& IMV_HCD_ATTR_RESIDENT_APP_NAME
))
473 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_RESIDENT_APP_NAME
);
475 if (!(received
& IMV_HCD_ATTR_TIME_SOURCE
))
477 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_TIME_SOURCE
);
479 if (!(received
& IMV_HCD_ATTR_USER_APP_ENABLED
))
481 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_USER_APP_ENABLED
);
483 if (!(received
& IMV_HCD_ATTR_USER_APP_PERSIST_ENABLED
))
485 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_USER_APP_PERSIST_ENABLED
);
487 if (!(received
& IMV_HCD_ATTR_USER_APP_NAME
))
489 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_USER_APP_NAME
);
491 if (!(received
& IMV_HCD_ATTR_VENDOR_NAME
))
493 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_VENDOR_NAME
);
495 if (!(received
& IMV_HCD_ATTR_VENDOR_SMI_CODE
))
497 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_VENDOR_SMI_CODE
);
499 if (!(received
& IMV_HCD_ATTR_CERTIFICATION_STATE
))
501 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_CERTIFICATION_STATE
);
503 if (!(received
& IMV_HCD_ATTR_CONFIGURATION_STATE
))
505 attr_cast
->add(attr_cast
, PEN_PWG
, PWG_HCD_CONFIGURATION_STATE
);
510 METHOD(imv_agent_if_t
, batch_ending
, TNC_Result
,
511 private_imv_hcd_agent_t
*this, TNC_ConnectionID id
)
515 imv_hcd_state_t
*hcd_state
;
516 imv_hcd_handshake_state_t handshake_state
;
519 TNC_Result result
= TNC_RESULT_SUCCESS
;
521 if (!this->agent
->get_state(this->agent
, id
, &state
))
523 return TNC_RESULT_FATAL
;
525 hcd_state
= (imv_hcd_state_t
*)state
;
526 handshake_state
= hcd_state
->get_handshake_state(hcd_state
);
527 imv_id
= this->agent
->get_id(this->agent
);
529 if (handshake_state
== IMV_HCD_STATE_END
)
531 return TNC_RESULT_SUCCESS
;
534 if (handshake_state
== IMV_HCD_STATE_INIT
)
536 size_t max_attr_size
= HCD_MAX_ATTR_SIZE
;
538 seg_contract_t
*contract
;
539 seg_contract_manager_t
*contracts
;
544 /* Determine maximum PA-TNC attribute segment size */
545 max_seg_size
= state
->get_max_msg_len(state
)
547 - PA_TNC_ATTR_HEADER_SIZE
548 - TCG_SEG_ATTR_SEG_ENV_HEADER
549 - PA_TNC_ATTR_HEADER_SIZE
550 - TCG_SEG_ATTR_MAX_SIZE_SIZE
;
551 contracts
= state
->get_contracts(state
);
553 for (i
= 1; i
< countof(msg_types
); i
++)
555 out_msg
= imv_msg_create(this->agent
, state
, id
, imv_id
,
556 TNC_IMCID_ANY
, msg_types
[i
]);
558 /* Announce support of PA-TNC segmentation to IMC */
559 contract
= seg_contract_create(msg_types
[i
], max_attr_size
,
560 max_seg_size
, TRUE
, imv_id
, FALSE
);
561 contract
->get_info_string(contract
, buf
, BUF_LEN
, TRUE
);
562 DBG2(DBG_IMV
, "%s", buf
);
563 contracts
->add_contract(contracts
, contract
);
564 attr
= tcg_seg_attr_max_size_create(max_attr_size
, max_seg_size
,
566 out_msg
->add_attribute(out_msg
, attr
);
568 hcd_state
->set_subtype(hcd_state
, msg_types
[i
].type
);
569 received
= state
->get_action_flags(state
);
571 if ((received
& IMV_HCD_ATTR_MUST
) != IMV_HCD_ATTR_MUST
)
573 /* create attribute request for missing mandatory attributes */
574 out_msg
->add_attribute(out_msg
, build_attr_request(received
));
576 result
= out_msg
->send(out_msg
, FALSE
);
577 out_msg
->destroy(out_msg
);
579 if (result
!= TNC_RESULT_SUCCESS
)
584 hcd_state
->set_handshake_state(hcd_state
, IMV_HCD_STATE_ATTR_REQ
);
590 METHOD(imv_agent_if_t
, solicit_recommendation
, TNC_Result
,
591 private_imv_hcd_agent_t
*this, TNC_ConnectionID id
)
594 imv_hcd_state_t
* hcd_state
;
595 imv_hcd_handshake_state_t handshake_state
;
596 enum_name_t
*pa_subtype_names
;
597 bool missing
= FALSE
;
601 if (!this->agent
->get_state(this->agent
, id
, &state
))
603 return TNC_RESULT_FATAL
;
605 hcd_state
= (imv_hcd_state_t
*)state
;
606 handshake_state
= hcd_state
->get_handshake_state(hcd_state
);
608 if (handshake_state
== IMV_HCD_STATE_ATTR_REQ
)
610 pa_subtype_names
= get_pa_subtype_names(PEN_PWG
);
612 for (i
= 1; i
< countof(msg_types
); i
++)
614 hcd_state
->set_subtype(hcd_state
, msg_types
[i
].type
);
615 received
= state
->get_action_flags(state
);
616 if ((received
& IMV_HCD_ATTR_MUST
) != IMV_HCD_ATTR_MUST
)
618 DBG1(DBG_IMV
, "missing attributes for PA subtype %N/%N",
619 pen_names
, PEN_PWG
, pa_subtype_names
, msg_types
[i
].type
);
626 state
->set_recommendation(state
,
627 TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS
,
628 TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR
);
632 state
->set_recommendation(state
,
633 TNC_IMV_ACTION_RECOMMENDATION_ALLOW
,
634 TNC_IMV_EVALUATION_RESULT_COMPLIANT
);
637 hcd_state
->set_handshake_state(hcd_state
, IMV_HCD_STATE_END
);
639 return this->agent
->provide_recommendation(this->agent
, state
);
642 METHOD(imv_agent_if_t
, destroy
, void,
643 private_imv_hcd_agent_t
*this)
645 DESTROY_IF(this->agent
);
650 * Described in header.
652 imv_agent_if_t
*imv_hcd_agent_create(const char *name
, TNC_IMVID id
,
653 TNC_Version
*actual_version
)
655 private_imv_hcd_agent_t
*this;
658 agent
= imv_agent_create(name
, msg_types
, countof(msg_types
), id
,
667 .bind_functions
= _bind_functions
,
668 .notify_connection_change
= _notify_connection_change
,
669 .receive_message
= _receive_message
,
670 .receive_message_long
= _receive_message_long
,
671 .batch_ending
= _batch_ending
,
672 .solicit_recommendation
= _solicit_recommendation
,
678 return &this->public;