libipsec: Fix memory leak in event relay
[strongswan.git] / src / libipsec / ipsec_event_relay.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "ipsec_event_relay.h"
19
20 #include <library.h>
21 #include <utils/debug.h>
22 #include <threading/rwlock.h>
23 #include <collections/linked_list.h>
24 #include <collections/blocking_queue.h>
25 #include <processing/jobs/callback_job.h>
26
27 typedef struct private_ipsec_event_relay_t private_ipsec_event_relay_t;
28
29 /**
30 * Private additions to ipsec_event_relay_t.
31 */
32 struct private_ipsec_event_relay_t {
33
34 /**
35 * Public members
36 */
37 ipsec_event_relay_t public;
38
39 /**
40 * Registered listeners
41 */
42 linked_list_t *listeners;
43
44 /**
45 * Lock to safely access the list of listeners
46 */
47 rwlock_t *lock;
48
49 /**
50 * Blocking queue for events
51 */
52 blocking_queue_t *queue;
53 };
54
55 /**
56 * Helper struct used to manage events in a queue
57 */
58 typedef struct {
59
60 /**
61 * Type of the event
62 */
63 enum {
64 IPSEC_EVENT_EXPIRE,
65 } type;
66
67 /**
68 * Reqid of the SA, if any
69 */
70 u_int32_t reqid;
71
72 /**
73 * SPI of the SA, if any
74 */
75 u_int32_t spi;
76
77 /**
78 * Additional data for specific event types
79 */
80 union {
81
82 struct {
83 /** Protocol of the SA */
84 u_int8_t protocol;
85 /** TRUE in case of a hard expire */
86 bool hard;
87 } expire;
88
89 } data;
90
91 } ipsec_event_t;
92
93 /**
94 * Dequeue events and relay them to listeners
95 */
96 static job_requeue_t handle_events(private_ipsec_event_relay_t *this)
97 {
98 enumerator_t *enumerator;
99 ipsec_event_listener_t *current;
100 ipsec_event_t *event;
101
102 event = this->queue->dequeue(this->queue);
103
104 this->lock->read_lock(this->lock);
105 enumerator = this->listeners->create_enumerator(this->listeners);
106 while (enumerator->enumerate(enumerator, (void**)&current))
107 {
108 switch (event->type)
109 {
110 case IPSEC_EVENT_EXPIRE:
111 if (current->expire)
112 {
113 current->expire(event->reqid, event->data.expire.protocol,
114 event->spi, event->data.expire.hard);
115 }
116 break;
117 }
118 }
119 enumerator->destroy(enumerator);
120 this->lock->unlock(this->lock);
121 free(event);
122 return JOB_REQUEUE_DIRECT;
123 }
124
125 METHOD(ipsec_event_relay_t, expire, void,
126 private_ipsec_event_relay_t *this, u_int32_t reqid, u_int8_t protocol,
127 u_int32_t spi, bool hard)
128 {
129 ipsec_event_t *event;
130
131 INIT(event,
132 .type = IPSEC_EVENT_EXPIRE,
133 .reqid = reqid,
134 .spi = spi,
135 .data = {
136 .expire = {
137 .protocol = protocol,
138 .hard = hard,
139 },
140 },
141 );
142 this->queue->enqueue(this->queue, event);
143 }
144
145 METHOD(ipsec_event_relay_t, register_listener, void,
146 private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
147 {
148 this->lock->write_lock(this->lock);
149 this->listeners->insert_last(this->listeners, listener);
150 this->lock->unlock(this->lock);
151 }
152
153 METHOD(ipsec_event_relay_t, unregister_listener, void,
154 private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
155 {
156 this->lock->write_lock(this->lock);
157 this->listeners->remove(this->listeners, listener, NULL);
158 this->lock->unlock(this->lock);
159 }
160
161 METHOD(ipsec_event_relay_t, destroy, void,
162 private_ipsec_event_relay_t *this)
163 {
164 this->queue->destroy_function(this->queue, free);
165 this->listeners->destroy(this->listeners);
166 this->lock->destroy(this->lock);
167 free(this);
168 }
169
170 /**
171 * Described in header.
172 */
173 ipsec_event_relay_t *ipsec_event_relay_create()
174 {
175 private_ipsec_event_relay_t *this;
176
177 INIT(this,
178 .public = {
179 .expire = _expire,
180 .register_listener = _register_listener,
181 .unregister_listener = _unregister_listener,
182 .destroy = _destroy,
183 },
184 .listeners = linked_list_create(),
185 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
186 .queue = blocking_queue_create(),
187 );
188
189 lib->processor->queue_job(lib->processor,
190 (job_t*)callback_job_create((callback_job_cb_t)handle_events, this,
191 NULL, (callback_job_cancel_t)return_false));
192
193 return &this->public;
194 }