2 * Copyright (C) 2011 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #include "stroke_socket.h"
20 #include <sys/types.h>
22 #include <sys/socket.h>
29 #include <threading/mutex.h>
30 #include <threading/thread.h>
31 #include <threading/condvar.h>
32 #include <utils/linked_list.h>
33 #include <processing/jobs/callback_job.h>
35 #include "stroke_config.h"
36 #include "stroke_control.h"
37 #include "stroke_cred.h"
38 #include "stroke_ca.h"
39 #include "stroke_attribute.h"
40 #include "stroke_list.h"
43 * To avoid clogging the thread pool with (blocking) jobs, we limit the number
44 * of concurrently handled stroke commands.
46 #define MAX_CONCURRENT_DEFAULT 4
48 typedef struct stroke_job_context_t stroke_job_context_t
;
49 typedef struct private_stroke_socket_t private_stroke_socket_t
;
52 * private data of stroke_socket
54 struct private_stroke_socket_t
{
59 stroke_socket_t
public;
62 * Unix socket to listen for strokes
67 * job accepting stroke messages
69 callback_job_t
*receiver
;
72 * job handling stroke messages
74 callback_job_t
*handler
;
77 * queued stroke commands
79 linked_list_t
*commands
;
82 * lock for command list
87 * condvar to signal the arrival or completion of commands
92 * the number of currently handled commands
97 * the maximum number of concurrently handled commands
102 * configuration backend
104 stroke_config_t
*config
;
109 stroke_attribute_t
*attribute
;
112 * controller to control daemon
114 stroke_control_t
*control
;
127 * status information logging
133 * job context to pass to processing thread
135 struct stroke_job_context_t
{
138 * file descriptor to read from
143 * global stroke interface
145 private_stroke_socket_t
*this;
149 * Helper function which corrects the string pointers
150 * in a stroke_msg_t. Strings in a stroke_msg sent over "wire"
151 * contains RELATIVE addresses (relative to the beginning of the
152 * stroke_msg). They must be corrected if they reach our address
155 static void pop_string(stroke_msg_t
*msg
, char **string
)
162 /* check for sanity of string pointer and string */
163 if (string
< (char**)msg
||
164 string
> (char**)((char*)msg
+ sizeof(stroke_msg_t
)) ||
165 (unsigned long)*string
< (unsigned long)((char*)msg
->buffer
- (char*)msg
) ||
166 (unsigned long)*string
> msg
->length
)
168 *string
= "(invalid pointer in stroke msg)";
172 *string
= (char*)msg
+ (unsigned long)*string
;
177 * Pop the strings of a stroke_end_t struct and log them for debugging purposes
179 static void pop_end(stroke_msg_t
*msg
, const char* label
, stroke_end_t
*end
)
181 pop_string(msg
, &end
->address
);
182 pop_string(msg
, &end
->subnets
);
183 pop_string(msg
, &end
->sourceip
);
184 pop_string(msg
, &end
->auth
);
185 pop_string(msg
, &end
->auth2
);
186 pop_string(msg
, &end
->id
);
187 pop_string(msg
, &end
->id2
);
188 pop_string(msg
, &end
->cert
);
189 pop_string(msg
, &end
->cert2
);
190 pop_string(msg
, &end
->ca
);
191 pop_string(msg
, &end
->ca2
);
192 pop_string(msg
, &end
->groups
);
193 pop_string(msg
, &end
->cert_policy
);
194 pop_string(msg
, &end
->updown
);
196 DBG2(DBG_CFG
, " %s=%s", label
, end
->address
);
197 DBG2(DBG_CFG
, " %ssubnet=%s", label
, end
->subnets
);
198 DBG2(DBG_CFG
, " %ssourceip=%s", label
, end
->sourceip
);
199 DBG2(DBG_CFG
, " %sauth=%s", label
, end
->auth
);
200 DBG2(DBG_CFG
, " %sauth2=%s", label
, end
->auth2
);
201 DBG2(DBG_CFG
, " %sid=%s", label
, end
->id
);
202 DBG2(DBG_CFG
, " %sid2=%s", label
, end
->id2
);
203 DBG2(DBG_CFG
, " %scert=%s", label
, end
->cert
);
204 DBG2(DBG_CFG
, " %scert2=%s", label
, end
->cert2
);
205 DBG2(DBG_CFG
, " %sca=%s", label
, end
->ca
);
206 DBG2(DBG_CFG
, " %sca2=%s", label
, end
->ca2
);
207 DBG2(DBG_CFG
, " %sgroups=%s", label
, end
->groups
);
208 DBG2(DBG_CFG
, " %supdown=%s", label
, end
->updown
);
212 * Add a connection to the configuration list
214 static void stroke_add_conn(private_stroke_socket_t
*this, stroke_msg_t
*msg
)
216 pop_string(msg
, &msg
->add_conn
.name
);
217 DBG1(DBG_CFG
, "received stroke: add connection '%s'", msg
->add_conn
.name
);
219 DBG2(DBG_CFG
, "conn %s", msg
->add_conn
.name
);
220 pop_end(msg
, "left", &msg
->add_conn
.me
);
221 pop_end(msg
, "right", &msg
->add_conn
.other
);
222 pop_string(msg
, &msg
->add_conn
.eap_identity
);
223 pop_string(msg
, &msg
->add_conn
.aaa_identity
);
224 pop_string(msg
, &msg
->add_conn
.algorithms
.ike
);
225 pop_string(msg
, &msg
->add_conn
.algorithms
.esp
);
226 pop_string(msg
, &msg
->add_conn
.ikeme
.mediated_by
);
227 pop_string(msg
, &msg
->add_conn
.ikeme
.peerid
);
228 DBG2(DBG_CFG
, " eap_identity=%s", msg
->add_conn
.eap_identity
);
229 DBG2(DBG_CFG
, " aaa_identity=%s", msg
->add_conn
.aaa_identity
);
230 DBG2(DBG_CFG
, " ike=%s", msg
->add_conn
.algorithms
.ike
);
231 DBG2(DBG_CFG
, " esp=%s", msg
->add_conn
.algorithms
.esp
);
232 DBG2(DBG_CFG
, " dpddelay=%d", msg
->add_conn
.dpd
.delay
);
233 DBG2(DBG_CFG
, " dpdaction=%d", msg
->add_conn
.dpd
.action
);
234 DBG2(DBG_CFG
, " closeaction=%d", msg
->add_conn
.close_action
);
235 DBG2(DBG_CFG
, " mediation=%s", msg
->add_conn
.ikeme
.mediation ?
"yes" : "no");
236 DBG2(DBG_CFG
, " mediated_by=%s", msg
->add_conn
.ikeme
.mediated_by
);
237 DBG2(DBG_CFG
, " me_peerid=%s", msg
->add_conn
.ikeme
.peerid
);
239 this->config
->add(this->config
, msg
);
240 this->attribute
->add_pool(this->attribute
, msg
);
244 * Delete a connection from the list
246 static void stroke_del_conn(private_stroke_socket_t
*this, stroke_msg_t
*msg
)
248 pop_string(msg
, &msg
->del_conn
.name
);
249 DBG1(DBG_CFG
, "received stroke: delete connection '%s'", msg
->del_conn
.name
);
251 this->config
->del(this->config
, msg
);
252 this->attribute
->del_pool(this->attribute
, msg
);
256 * initiate a connection by name
258 static void stroke_initiate(private_stroke_socket_t
*this, stroke_msg_t
*msg
, FILE *out
)
260 pop_string(msg
, &msg
->initiate
.name
);
261 DBG1(DBG_CFG
, "received stroke: initiate '%s'", msg
->initiate
.name
);
263 this->control
->initiate(this->control
, msg
, out
);
267 * terminate a connection by name
269 static void stroke_terminate(private_stroke_socket_t
*this, stroke_msg_t
*msg
, FILE *out
)
271 pop_string(msg
, &msg
->terminate
.name
);
272 DBG1(DBG_CFG
, "received stroke: terminate '%s'", msg
->terminate
.name
);
274 this->control
->terminate(this->control
, msg
, out
);
278 * terminate a connection by peers virtual IP
280 static void stroke_terminate_srcip(private_stroke_socket_t
*this,
281 stroke_msg_t
*msg
, FILE *out
)
283 pop_string(msg
, &msg
->terminate_srcip
.start
);
284 pop_string(msg
, &msg
->terminate_srcip
.end
);
285 DBG1(DBG_CFG
, "received stroke: terminate-srcip %s-%s",
286 msg
->terminate_srcip
.start
, msg
->terminate_srcip
.end
);
288 this->control
->terminate_srcip(this->control
, msg
, out
);
292 * rekey a connection by name/id
294 static void stroke_rekey(private_stroke_socket_t
*this, stroke_msg_t
*msg
, FILE *out
)
296 pop_string(msg
, &msg
->terminate
.name
);
297 DBG1(DBG_CFG
, "received stroke: rekey '%s'", msg
->rekey
.name
);
299 this->control
->rekey(this->control
, msg
, out
);
303 * route a policy (install SPD entries)
305 static void stroke_route(private_stroke_socket_t
*this, stroke_msg_t
*msg
, FILE *out
)
307 pop_string(msg
, &msg
->route
.name
);
308 DBG1(DBG_CFG
, "received stroke: route '%s'", msg
->route
.name
);
310 this->control
->route(this->control
, msg
, out
);
316 static void stroke_unroute(private_stroke_socket_t
*this, stroke_msg_t
*msg
, FILE *out
)
318 pop_string(msg
, &msg
->terminate
.name
);
319 DBG1(DBG_CFG
, "received stroke: unroute '%s'", msg
->route
.name
);
321 this->control
->unroute(this->control
, msg
, out
);
325 * Add a ca information record to the cainfo list
327 static void stroke_add_ca(private_stroke_socket_t
*this,
328 stroke_msg_t
*msg
, FILE *out
)
330 pop_string(msg
, &msg
->add_ca
.name
);
331 DBG1(DBG_CFG
, "received stroke: add ca '%s'", msg
->add_ca
.name
);
333 pop_string(msg
, &msg
->add_ca
.cacert
);
334 pop_string(msg
, &msg
->add_ca
.crluri
);
335 pop_string(msg
, &msg
->add_ca
.crluri2
);
336 pop_string(msg
, &msg
->add_ca
.ocspuri
);
337 pop_string(msg
, &msg
->add_ca
.ocspuri2
);
338 pop_string(msg
, &msg
->add_ca
.certuribase
);
339 DBG2(DBG_CFG
, "ca %s", msg
->add_ca
.name
);
340 DBG2(DBG_CFG
, " cacert=%s", msg
->add_ca
.cacert
);
341 DBG2(DBG_CFG
, " crluri=%s", msg
->add_ca
.crluri
);
342 DBG2(DBG_CFG
, " crluri2=%s", msg
->add_ca
.crluri2
);
343 DBG2(DBG_CFG
, " ocspuri=%s", msg
->add_ca
.ocspuri
);
344 DBG2(DBG_CFG
, " ocspuri2=%s", msg
->add_ca
.ocspuri2
);
345 DBG2(DBG_CFG
, " certuribase=%s", msg
->add_ca
.certuribase
);
347 this->ca
->add(this->ca
, msg
);
351 * Delete a ca information record from the cainfo list
353 static void stroke_del_ca(private_stroke_socket_t
*this,
354 stroke_msg_t
*msg
, FILE *out
)
356 pop_string(msg
, &msg
->del_ca
.name
);
357 DBG1(DBG_CFG
, "received stroke: delete ca '%s'", msg
->del_ca
.name
);
359 this->ca
->del(this->ca
, msg
);
364 * show status of daemon
366 static void stroke_status(private_stroke_socket_t
*this,
367 stroke_msg_t
*msg
, FILE *out
, bool all
, bool wait
)
369 pop_string(msg
, &(msg
->status
.name
));
371 this->list
->status(this->list
, msg
, out
, all
, wait
);
375 * list various information
377 static void stroke_list(private_stroke_socket_t
*this, stroke_msg_t
*msg
, FILE *out
)
379 if (msg
->list
.flags
& LIST_CAINFOS
)
381 this->ca
->list(this->ca
, msg
, out
);
383 this->list
->list(this->list
, msg
, out
);
387 * reread various information
389 static void stroke_reread(private_stroke_socket_t
*this,
390 stroke_msg_t
*msg
, FILE *out
)
392 this->cred
->reread(this->cred
, msg
, out
);
396 * purge various information
398 static void stroke_purge(private_stroke_socket_t
*this,
399 stroke_msg_t
*msg
, FILE *out
)
401 if (msg
->purge
.flags
& PURGE_OCSP
)
403 lib
->credmgr
->flush_cache(lib
->credmgr
, CERT_X509_OCSP_RESPONSE
);
405 if (msg
->purge
.flags
& PURGE_CRLS
)
407 lib
->credmgr
->flush_cache(lib
->credmgr
, CERT_X509_CRL
);
409 if (msg
->purge
.flags
& PURGE_CERTS
)
411 lib
->credmgr
->flush_cache(lib
->credmgr
, CERT_X509
);
413 if (msg
->purge
.flags
& PURGE_IKE
)
415 this->control
->purge_ike(this->control
, msg
, out
);
420 * Export in-memory credentials
422 static void stroke_export(private_stroke_socket_t
*this,
423 stroke_msg_t
*msg
, FILE *out
)
425 pop_string(msg
, &msg
->export
.selector
);
427 if (msg
->purge
.flags
& EXPORT_X509
)
429 enumerator_t
*enumerator
;
430 identification_t
*id
;
434 id
= identification_create_from_string(msg
->export
.selector
);
435 enumerator
= lib
->credmgr
->create_cert_enumerator(lib
->credmgr
,
436 CERT_X509
, KEY_ANY
, id
, FALSE
);
437 while (enumerator
->enumerate(enumerator
, &cert
))
439 if (cert
->get_encoding(cert
, CERT_PEM
, &encoded
))
441 fprintf(out
, "%.*s", (int)encoded
.len
, encoded
.ptr
);
445 enumerator
->destroy(enumerator
);
453 static void stroke_leases(private_stroke_socket_t
*this,
454 stroke_msg_t
*msg
, FILE *out
)
456 pop_string(msg
, &msg
->leases
.pool
);
457 pop_string(msg
, &msg
->leases
.address
);
459 this->list
->leases(this->list
, msg
, out
);
465 static void stroke_memusage(private_stroke_socket_t
*this,
466 stroke_msg_t
*msg
, FILE *out
)
468 if (lib
->leak_detective
)
470 lib
->leak_detective
->usage(lib
->leak_detective
, out
);
475 * set the verbosity debug output
477 static void stroke_loglevel(private_stroke_socket_t
*this,
478 stroke_msg_t
*msg
, FILE *out
)
480 enumerator_t
*enumerator
;
481 sys_logger_t
*sys_logger
;
482 file_logger_t
*file_logger
;
485 pop_string(msg
, &(msg
->loglevel
.type
));
486 DBG1(DBG_CFG
, "received stroke: loglevel %d for %s",
487 msg
->loglevel
.level
, msg
->loglevel
.type
);
489 group
= enum_from_name(debug_names
, msg
->loglevel
.type
);
492 fprintf(out
, "invalid type (%s)!\n", msg
->loglevel
.type
);
495 /* we set the loglevel on ALL sys- and file-loggers */
496 enumerator
= charon
->sys_loggers
->create_enumerator(charon
->sys_loggers
);
497 while (enumerator
->enumerate(enumerator
, &sys_logger
))
499 sys_logger
->set_level(sys_logger
, group
, msg
->loglevel
.level
);
501 enumerator
->destroy(enumerator
);
502 enumerator
= charon
->file_loggers
->create_enumerator(charon
->file_loggers
);
503 while (enumerator
->enumerate(enumerator
, &file_logger
))
505 file_logger
->set_level(file_logger
, group
, msg
->loglevel
.level
);
507 enumerator
->destroy(enumerator
);
511 * set various config options
513 static void stroke_config(private_stroke_socket_t
*this,
514 stroke_msg_t
*msg
, FILE *out
)
516 this->cred
->cachecrl(this->cred
, msg
->config
.cachecrl
);
520 * destroy a job context
522 static void stroke_job_context_destroy(stroke_job_context_t
*this)
532 * called to signal the completion of a command
534 static inline job_requeue_t
job_processed(private_stroke_socket_t
*this)
536 this->mutex
->lock(this->mutex
);
538 this->condvar
->signal(this->condvar
);
539 this->mutex
->unlock(this->mutex
);
540 return JOB_REQUEUE_NONE
;
544 * process a stroke request from the socket pointed by "fd"
546 static job_requeue_t
process(stroke_job_context_t
*ctx
)
549 u_int16_t msg_length
;
552 private_stroke_socket_t
*this = ctx
->this;
553 int strokefd
= ctx
->fd
;
555 /* peek the length */
556 bytes_read
= recv(strokefd
, &msg_length
, sizeof(msg_length
), MSG_PEEK
);
557 if (bytes_read
!= sizeof(msg_length
))
559 DBG1(DBG_CFG
, "reading length of stroke message failed: %s",
561 return job_processed(this);
565 msg
= alloca(msg_length
);
566 bytes_read
= recv(strokefd
, msg
, msg_length
, 0);
567 if (bytes_read
!= msg_length
)
569 DBG1(DBG_CFG
, "reading stroke message failed: %s", strerror(errno
));
570 return job_processed(this);
573 out
= fdopen(strokefd
, "w+");
576 DBG1(DBG_CFG
, "opening stroke output channel failed: %s", strerror(errno
));
577 return job_processed(this);
580 DBG3(DBG_CFG
, "stroke message %b", (void*)msg
, msg_length
);
585 stroke_initiate(this, msg
, out
);
588 stroke_route(this, msg
, out
);
591 stroke_unroute(this, msg
, out
);
594 stroke_terminate(this, msg
, out
);
596 case STR_TERMINATE_SRCIP
:
597 stroke_terminate_srcip(this, msg
, out
);
600 stroke_rekey(this, msg
, out
);
603 stroke_status(this, msg
, out
, FALSE
, TRUE
);
606 stroke_status(this, msg
, out
, TRUE
, TRUE
);
608 case STR_STATUS_ALL_NOBLK
:
609 stroke_status(this, msg
, out
, TRUE
, FALSE
);
612 stroke_add_conn(this, msg
);
615 stroke_del_conn(this, msg
);
618 stroke_add_ca(this, msg
, out
);
621 stroke_del_ca(this, msg
, out
);
624 stroke_loglevel(this, msg
, out
);
627 stroke_config(this, msg
, out
);
630 stroke_list(this, msg
, out
);
633 stroke_reread(this, msg
, out
);
636 stroke_purge(this, msg
, out
);
639 stroke_export(this, msg
, out
);
642 stroke_leases(this, msg
, out
);
645 stroke_memusage(this, msg
, out
);
648 DBG1(DBG_CFG
, "received unknown stroke");
652 /* fclose() closes underlying FD */
654 return job_processed(this);
658 * Handle queued stroke commands
660 static job_requeue_t
handle(private_stroke_socket_t
*this)
662 stroke_job_context_t
*ctx
;
666 this->mutex
->lock(this->mutex
);
667 thread_cleanup_push((thread_cleanup_t
)this->mutex
->unlock
, this->mutex
);
668 oldstate
= thread_cancelability(TRUE
);
669 while (this->commands
->get_count(this->commands
) == 0 ||
670 this->handling
>= this->max_concurrent
)
672 this->condvar
->wait(this->condvar
, this->mutex
);
674 thread_cancelability(oldstate
);
675 this->commands
->remove_first(this->commands
, (void**)&ctx
);
677 thread_cleanup_pop(TRUE
);
678 job
= callback_job_create_with_prio((callback_job_cb_t
)process
, ctx
,
679 (void*)stroke_job_context_destroy
, this->handler
, JOB_PRIO_HIGH
);
680 lib
->processor
->queue_job(lib
->processor
, (job_t
*)job
);
681 return JOB_REQUEUE_DIRECT
;
685 * Accept stroke commands and queue them to be handled
687 static job_requeue_t
receive(private_stroke_socket_t
*this)
689 struct sockaddr_un strokeaddr
;
690 int strokeaddrlen
= sizeof(strokeaddr
);
693 stroke_job_context_t
*ctx
;
695 oldstate
= thread_cancelability(TRUE
);
696 strokefd
= accept(this->socket
, (struct sockaddr
*)&strokeaddr
, &strokeaddrlen
);
697 thread_cancelability(oldstate
);
701 DBG1(DBG_CFG
, "accepting stroke connection failed: %s", strerror(errno
));
702 return JOB_REQUEUE_FAIR
;
709 this->mutex
->lock(this->mutex
);
710 this->commands
->insert_last(this->commands
, ctx
);
711 this->condvar
->signal(this->condvar
);
712 this->mutex
->unlock(this->mutex
);
714 return JOB_REQUEUE_FAIR
;
718 * initialize and open stroke socket
720 static bool open_socket(private_stroke_socket_t
*this)
722 struct sockaddr_un socket_addr
;
725 socket_addr
.sun_family
= AF_UNIX
;
726 strcpy(socket_addr
.sun_path
, STROKE_SOCKET
);
728 /* set up unix socket */
729 this->socket
= socket(AF_UNIX
, SOCK_STREAM
, 0);
730 if (this->socket
== -1)
732 DBG1(DBG_CFG
, "could not create stroke socket");
736 unlink(socket_addr
.sun_path
);
737 old
= umask(~(S_IRWXU
| S_IRWXG
));
738 if (bind(this->socket
, (struct sockaddr
*)&socket_addr
, sizeof(socket_addr
)) < 0)
740 DBG1(DBG_CFG
, "could not bind stroke socket: %s", strerror(errno
));
745 if (chown(socket_addr
.sun_path
, charon
->uid
, charon
->gid
) != 0)
747 DBG1(DBG_CFG
, "changing stroke socket permissions failed: %s",
751 if (listen(this->socket
, 10) < 0)
753 DBG1(DBG_CFG
, "could not listen on stroke socket: %s", strerror(errno
));
755 unlink(socket_addr
.sun_path
);
761 METHOD(stroke_socket_t
, destroy
, void,
762 private_stroke_socket_t
*this)
764 this->handler
->cancel(this->handler
);
765 this->receiver
->cancel(this->receiver
);
766 this->commands
->destroy_function(this->commands
, (void*)stroke_job_context_destroy
);
767 this->condvar
->destroy(this->condvar
);
768 this->mutex
->destroy(this->mutex
);
769 lib
->credmgr
->remove_set(lib
->credmgr
, &this->ca
->set
);
770 lib
->credmgr
->remove_set(lib
->credmgr
, &this->cred
->set
);
771 charon
->backends
->remove_backend(charon
->backends
, &this->config
->backend
);
772 hydra
->attributes
->remove_provider(hydra
->attributes
, &this->attribute
->provider
);
773 this->cred
->destroy(this->cred
);
774 this->ca
->destroy(this->ca
);
775 this->config
->destroy(this->config
);
776 this->attribute
->destroy(this->attribute
);
777 this->control
->destroy(this->control
);
778 this->list
->destroy(this->list
);
785 stroke_socket_t
*stroke_socket_create()
787 private_stroke_socket_t
*this;
795 if (!open_socket(this))
801 this->cred
= stroke_cred_create();
802 this->attribute
= stroke_attribute_create();
803 this->ca
= stroke_ca_create(this->cred
);
804 this->config
= stroke_config_create(this->ca
, this->cred
);
805 this->control
= stroke_control_create();
806 this->list
= stroke_list_create(this->attribute
);
808 this->mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
809 this->condvar
= condvar_create(CONDVAR_TYPE_DEFAULT
);
810 this->commands
= linked_list_create();
811 this->max_concurrent
= lib
->settings
->get_int(lib
->settings
,
812 "charon.plugins.stroke.max_concurrent", MAX_CONCURRENT_DEFAULT
);
814 lib
->credmgr
->add_set(lib
->credmgr
, &this->ca
->set
);
815 lib
->credmgr
->add_set(lib
->credmgr
, &this->cred
->set
);
816 charon
->backends
->add_backend(charon
->backends
, &this->config
->backend
);
817 hydra
->attributes
->add_provider(hydra
->attributes
, &this->attribute
->provider
);
819 this->receiver
= callback_job_create_with_prio((callback_job_cb_t
)receive
,
820 this, NULL
, NULL
, JOB_PRIO_CRITICAL
);
821 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->receiver
);
823 this->handler
= callback_job_create_with_prio((callback_job_cb_t
)handle
,
824 this, NULL
, NULL
, JOB_PRIO_CRITICAL
);
825 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->handler
);
827 return &this->public;