blob: f035614488e6b1c252ece14a8401717a3702b765 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2015, WIDE Project and NICT *
6* All rights reserved. *
7* *
8* Redistribution and use of this software in source and binary forms, with or without modification, are *
9* permitted provided that the following conditions are met: *
10* *
11* * Redistributions of source code must retain the above *
12* copyright notice, this list of conditions and the *
13* following disclaimer. *
14* *
15* * Redistributions in binary form must reproduce the above *
16* copyright notice, this list of conditions and the *
17* following disclaimer in the documentation and/or other *
18* materials provided with the distribution. *
19* *
20* * Neither the name of the WIDE Project or NICT nor the *
21* names of its contributors may be used to endorse or *
22* promote products derived from this software without *
23* specific prior written permission of WIDE Project and *
24* NICT. *
25* *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34*********************************************************************************************************/
35
36#include "fdcore-internal.h"
37
38/* Structures for the fd_hook_data_hdl management */
39static struct fd_hook_data_hdl {
40 size_t pmd_size;
41 void (*pmd_init_cb)(struct fd_hook_permsgdata *);
42 void (*pmd_fini_cb)(struct fd_hook_permsgdata *);
43} HDH_array[FD_HOOK_HANDLE_LIMIT];
44static int max_index = 0;
45static pthread_mutex_t HDH_lock = PTHREAD_MUTEX_INITIALIZER;
46
47/* The structure linked from the msg structure list */
48struct pmd_list_item {
49 struct fd_list chain; /* this list is ordered by hdl */
50 struct fd_hook_data_hdl * hdl;
51 struct fd_hook_permsgdata { } pmd; /* this data belongs to the extension; we only know the size of it */
52};
53
54#define sizeof_pmd(hdl) (((size_t)&((struct pmd_list_item *)0)->pmd) + hdl->pmd_size)
55
56/* Now a hook registered by an extension */
57struct fd_hook_hdl {
58 struct fd_list chain[HOOK_LAST+1];
59 void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata);
60 void *regdata;
61 struct fd_hook_data_hdl *data_hdl;
62};
63
64/* Array of those hooks */
65struct {
66 struct fd_list sentinel;
67 pthread_rwlock_t rwlock;
68} HS_array[HOOK_LAST+1];
69
70/* Initialize the array of sentinels for the hooks */
71int fd_hooks_init(void)
72{
73 int i;
74 for (i=0; i <= HOOK_LAST; i++) {
75 fd_list_init(&HS_array[i].sentinel, NULL);
76 CHECK_POSIX( pthread_rwlock_init(&HS_array[i].rwlock, NULL) );
77 }
78 return 0;
79}
80
81/* Get a slot in the array */
82int fd_hook_data_register(
83 size_t permsgdata_size,
84 void (*permsgdata_init_cb) (struct fd_hook_permsgdata *),
85 void (*permsgdata_fini_cb) (struct fd_hook_permsgdata *),
86 struct fd_hook_data_hdl **new_handle)
87{
88 int ret = ENOSPC, idx;
89 TRACE_ENTRY("%zd %p %p %p", permsgdata_size, permsgdata_init_cb, permsgdata_fini_cb, new_handle);
90
91 CHECK_PARAMS( permsgdata_size && new_handle );
92
93 CHECK_POSIX( pthread_mutex_lock(&HDH_lock) );
94 if (max_index < FD_HOOK_HANDLE_LIMIT) {
95 idx = max_index++;
96 ret = 0;
97 }
98 CHECK_POSIX( pthread_mutex_unlock(&HDH_lock) );
99
100 if (ret == 0) {
101 HDH_array[idx].pmd_size = permsgdata_size;
102 HDH_array[idx].pmd_init_cb = permsgdata_init_cb;
103 HDH_array[idx].pmd_fini_cb = permsgdata_fini_cb;
104 *new_handle = &HDH_array[idx];
105 }
106
107 return ret;
108}
109
110/* Register a new hook callback */
111int fd_hook_register ( uint32_t type_mask,
112 void (*fd_hook_cb)(enum fd_hook_type type, struct msg * msg, struct peer_hdr * peer, void * other, struct fd_hook_permsgdata *pmd, void * regdata),
113 void *regdata,
114 struct fd_hook_data_hdl *data_hdl,
115 struct fd_hook_hdl ** handler )
116{
117 struct fd_hook_hdl * newhdl = NULL;
118 int i;
119
120 TRACE_ENTRY("%x %p %p %p %p", type_mask, fd_hook_cb, regdata, data_hdl, handler);
121
122 CHECK_PARAMS( fd_hook_cb && handler );
123
124 CHECK_MALLOC( newhdl = malloc(sizeof(struct fd_hook_hdl)) );
125 memset(newhdl, 0, sizeof(struct fd_hook_hdl));
126
127 newhdl->fd_hook_cb = fd_hook_cb;
128 newhdl->regdata = regdata;
129 newhdl->data_hdl = data_hdl;
130
131 for (i=0; i <= HOOK_LAST; i++) {
132 fd_list_init(&newhdl->chain[i], newhdl);
133 if (type_mask & (1<<i)) {
134 CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
135 fd_list_insert_before( &HS_array[i].sentinel, &newhdl->chain[i]);
136 CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
137 }
138 }
139
140 *handler = newhdl;
141 return 0;
142}
143
144/* free this hook callback */
145int fd_hook_unregister( struct fd_hook_hdl * handler )
146{
147 int i;
148 TRACE_ENTRY("%p", handler);
149 CHECK_PARAMS( handler );
150
151 for (i=0; i <= HOOK_LAST; i++) {
152 if ( ! FD_IS_LIST_EMPTY(&handler->chain[i])) {
153 CHECK_POSIX( pthread_rwlock_wrlock(&HS_array[i].rwlock) );
154 fd_list_unlink(&handler->chain[i]);
155 CHECK_POSIX( pthread_rwlock_unlock(&HS_array[i].rwlock) );
156 }
157 }
158
159 free(handler);
160
161 return 0;
162}
163
164/* callback for the libfdproto to free the data associated with a message */
165static void pmdl_free(struct fd_msg_pmdl *pmdl)
166{
167 /* destroy all the items in the list */
168 while (!FD_IS_LIST_EMPTY(&pmdl->sentinel)) {
169 struct pmd_list_item * li = (struct pmd_list_item *)(pmdl->sentinel.next);
170 if (li->hdl->pmd_fini_cb) {
171 (*li->hdl->pmd_fini_cb)(&li->pmd);
172 }
173 fd_list_unlink(&li->chain);
174 free(li);
175 }
176 CHECK_POSIX_DO( pthread_mutex_destroy(&pmdl->lock), );
177 pmdl->sentinel.o = NULL;
178}
179
180/* Save the list of pmd into the message structure, as well as the callback to free this list */
181void fd_hook_associate(struct msg * msg, struct fd_msg_pmdl * pmdl)
182{
183 struct fd_msg_pmdl * in_msg;
184
185 CHECK_PARAMS_DO( msg && pmdl, return );
186 in_msg = fd_msg_pmdl_get(msg);
187 ASSERT(in_msg && (in_msg->sentinel.o == NULL)); /* error / already initialized ??? */
188 in_msg->sentinel.o = pmdl_free;
189 /* Now move all items from the pmdl pointer into the initialized list */
190 CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
191 fd_list_move_end(&in_msg->sentinel, &pmdl->sentinel);
192 CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
193 pmdl_free(pmdl);
194 /* We're done */
195}
196
197/* Return the location of the permsgdata area corresponding to this handle, after eventually having created it. Return NULL in case of failure */
198static struct fd_hook_permsgdata * get_or_create_pmd(struct fd_msg_pmdl *pmdl, struct fd_hook_hdl * h)
199{
200 struct fd_hook_permsgdata * ret = NULL;
201 struct fd_list * li;
202
203 CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
204
205 if (pmdl->sentinel.o == NULL) {
206 pmdl->sentinel.o = pmdl_free;
207 }
208
209 /* Search in the list for an item with the same handle. The list is ordered by this handle */
210 for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
211 struct pmd_list_item * pli = (struct pmd_list_item *) li;
212 if (pli->hdl == h->data_hdl)
213 ret = &pli->pmd;
214 if (pli->hdl >= h->data_hdl)
215 break;
216 }
217 if (!ret) {
218 /* we need to create a new one and insert before li */
219 struct pmd_list_item * pli;
220 CHECK_MALLOC_DO( pli = malloc(sizeof_pmd(h->data_hdl)), );
221 if (pli) {
222 memset(pli, 0, sizeof_pmd(h->data_hdl));
223 fd_list_init(&pli->chain, pli);
224 pli->hdl = h->data_hdl;
225 ret = &pli->pmd;
226 if (h->data_hdl->pmd_init_cb) {
227 (*h->data_hdl->pmd_init_cb)(ret);
228 }
229 fd_list_insert_before(li, &pli->chain);
230 }
231 }
232
233 CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
234 return ret;
235}
236
237struct fd_hook_permsgdata * fd_hook_get_request_pmd(struct fd_hook_data_hdl *data_hdl, struct msg * answer)
238{
239 struct msg * qry;
240 struct fd_msg_pmdl *pmdl;
241 struct fd_hook_permsgdata * ret = NULL;
242 struct fd_list * li;
243
244 CHECK_FCT_DO( fd_msg_answ_getq(answer, &qry), return NULL );
245 if (!qry)
246 return NULL;
247
248 pmdl = fd_msg_pmdl_get(qry);
249 if (!pmdl)
250 return NULL;
251
252 CHECK_POSIX_DO( pthread_mutex_lock(&pmdl->lock), );
253 /* Search in the list for an item with the same handle. The list is ordered by this handle */
254 for (li=pmdl->sentinel.next; li != &pmdl->sentinel; li = li->next) {
255 struct pmd_list_item * pli = (struct pmd_list_item *) li;
256 if (pli->hdl == data_hdl)
257 ret = &pli->pmd;
258 if (pli->hdl >= data_hdl)
259 break;
260 }
261 CHECK_POSIX_DO( pthread_mutex_unlock(&pmdl->lock), );
262 return ret;
263}
264
265/* Create a mask */
266uint32_t fd_hook_mask_helper(int dummy, ...)
267{
268 va_list ap;
269 uint32_t ret = 0;
270 int next;
271
272 va_start(ap, dummy);
273 while ((next = va_arg(ap, int)) >= 0) {
274 if (next > HOOK_LAST)
275 break; /* invalid parameter */
276 ret |= (1<<next);
277 }
278 va_end(ap);
279
280 return ret;
281}
282
283static pthread_mutex_t hook_default_mtx = PTHREAD_MUTEX_INITIALIZER;
284static char * hook_default_buf = NULL;
285static size_t hook_default_len = 0;
286
287/* The function that does the work of calling the extension's callbacks and also managing the permessagedata structures */
288void fd_hook_call(enum fd_hook_type type, struct msg * msg, struct fd_peer * peer, void * other, struct fd_msg_pmdl * pmdl)
289{
290 struct fd_list * li;
291 ASSERT(type <= HOOK_LAST);
292 int call_default = 0;
293
294 /* lock the list of hooks for this type */
295 CHECK_POSIX_DO( pthread_rwlock_rdlock(&HS_array[type].rwlock), );
296
297 pthread_cleanup_push( fd_cleanup_rwlock, &HS_array[type].rwlock );
298
299 if (FD_IS_LIST_EMPTY(&HS_array[type].sentinel)) {
300 call_default = 1;
301 } else {
302 /* for each registered hook */
303 for (li = HS_array[type].sentinel.next; li != &HS_array[type].sentinel; li = li->next) {
304 struct fd_hook_hdl * h = (struct fd_hook_hdl *)li->o;
305 struct fd_hook_permsgdata * pmd = NULL;
306
307 /* do we need to handle pmd ? */
308 if (h->data_hdl && pmdl) {
309 pmd = get_or_create_pmd(pmdl, h);
310 }
311
312 /* Now, call this callback */
313 (*h->fd_hook_cb)(type, msg, &peer->p_hdr, other, pmd, h->regdata);
314 }
315 }
316
317 pthread_cleanup_pop(0);
318
319 /* done */
320 CHECK_POSIX_DO( pthread_rwlock_unlock(&HS_array[type].rwlock), );
321
322 if (call_default) {
323 CHECK_POSIX_DO( pthread_mutex_lock(&hook_default_mtx), );
324
325 pthread_cleanup_push( fd_cleanup_mutex, &hook_default_mtx );
326
327 /* There was no registered handler, default behavior for this hook */
328 switch (type) {
329 case HOOK_DATA_RECEIVED: {
330 struct fd_cnx_rcvdata *rcv_data = other;
331 LOG_A("RCV: %zd bytes", rcv_data->length);
332 break;
333 }
334
335 case HOOK_MESSAGE_RECEIVED: {
336 CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
337 LOG_D("RCV from '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
338 break;
339 }
340
341 case HOOK_MESSAGE_LOCAL: {
342 CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
343 LOG_A("Handled to framework for sending: %s", hook_default_buf);
344 break;
345 }
346
347 case HOOK_MESSAGE_SENDING: {
348 LOG_A("SENDING message to '%s'", peer ? peer->p_hdr.info.pi_diamid : "<unknown>");
349 break;
350 }
351
352 case HOOK_MESSAGE_SENT: {
353 CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
354 LOG_D("SENT to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
355 break;
356 }
357
358 case HOOK_MESSAGE_FAILOVER: {
359 CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
360 LOG_D("Failing over message sent to '%s': %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
361 break;
362 }
363
364 case HOOK_MESSAGE_PARSING_ERROR: {
365 if (msg) {
366 DiamId_t id = NULL;
367 if (fd_msg_source_get( msg, &id, NULL ))
368 id = (DiamId_t)"<error getting source>";
369
370 if (!id)
371 id = (DiamId_t)"<local>";
372
373 CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
374
375 LOG_E("Parsing error: '%s' for the following message received from '%s':", (char *)other, (char *)id);
376 LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
377 } else {
378 struct fd_cnx_rcvdata *rcv_data = other;
379 CHECK_MALLOC_DO(fd_dump_extend_hexdump(&hook_default_buf, &hook_default_len, NULL, rcv_data->buffer, rcv_data->length, 0, 0), break);
380 LOG_E("Parsing error: cannot parse %zdB buffer from '%s': %s", rcv_data->length, peer ? peer->p_hdr.info.pi_diamid : "<unknown>", hook_default_buf);
381 }
382 break;
383 }
384
385 case HOOK_MESSAGE_PARSING_ERROR2: {
386 CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
387
388 LOG_E("Returning following message after parsing error:");
389 LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
390 break;
391 }
392
393 case HOOK_MESSAGE_ROUTING_ERROR: {
394 CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
395 LOG_E("Routing error: '%s' for the following message:", (char *)other);
396 LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
397 break;
398 }
399
400 case HOOK_MESSAGE_ROUTING_FORWARD: {
401 CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
402 LOG_D("FORWARDING: %s", hook_default_buf);
403 break;
404 }
405
406 case HOOK_MESSAGE_ROUTING_LOCAL: {
407 CHECK_MALLOC_DO(fd_msg_dump_summary(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
408 LOG_D("DISPATCHING: %s", hook_default_buf);
409 break;
410 }
411
412 case HOOK_MESSAGE_DROPPED: {
413 CHECK_MALLOC_DO(fd_msg_dump_treeview(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
414 LOG_E("Message discarded ('%s'):", (char *)other);
415 LOG_SPLIT(FD_LOG_ERROR, " ", hook_default_buf, NULL);
416 break;
417 }
418
419 case HOOK_PEER_CONNECT_FAILED: {
420 if (msg) {
421 CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
422 LOG_N("Connection to '%s' failed: '%s'; CER/CEA dump:", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
423 LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
424 } else {
425 LOG_D("Connection to '%s' failed: %s", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", (char *)other);
426 }
427 break;
428 }
429
430 case HOOK_PEER_CONNECT_SUCCESS: {
431 DiamId_t id = NULL;
432 if ((!fd_msg_source_get( msg, &id, NULL )) && (id == NULL)) { /* The CEA is locally issued */
433 fd_msg_answ_getq(msg, &msg); /* We dump the CER in that case */
434 }
435 CHECK_MALLOC_DO(fd_msg_dump_full(&hook_default_buf, &hook_default_len, NULL, msg, NULL, 0, 1), break);
436 char protobuf[40];
437 if (peer) {
438 CHECK_FCT_DO(fd_peer_cnx_proto_info(&peer->p_hdr, protobuf, sizeof(protobuf)), break );
439 } else {
440 protobuf[0] = '-';
441 protobuf[1] = '\0';
442 }
443 LOG_N("Connected to '%s' (%s), remote capabilities: ", peer ? peer->p_hdr.info.pi_diamid : "<unknown>", protobuf);
444 LOG_SPLIT(FD_LOG_NOTICE, " ", hook_default_buf, NULL);
445 break;
446 }
447
448 }
449
450 pthread_cleanup_pop(0);
451
452 CHECK_POSIX_DO( pthread_mutex_unlock(&hook_default_mtx), );
453 }
454}