1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include <private-lib-core.h>
26 #include <private-lib-abstract.h>
27
28 extern const lws_abs_transport_t lws_abs_transport_cli_raw_skt,
29 lws_abs_transport_cli_unit_test;
30 #if defined(LWS_WITH_SMTP)
31 extern const lws_abs_protocol_t lws_abs_protocol_smtp;
32 #endif
33 #if defined(LWS_WITH_MQTT)
34 extern const lws_abs_protocol_t lws_abs_protocol_mqttc;
35 #endif
36
37 static const lws_abs_transport_t * const available_abs_transports[] = {
38 &lws_abs_transport_cli_raw_skt,
39 &lws_abs_transport_cli_unit_test,
40 };
41
42 #if defined(LWS_WITH_ABSTRACT)
43 static const lws_abs_protocol_t * const available_abs_protocols[] = {
44 #if defined(LWS_WITH_SMTP)
45 &lws_abs_protocol_smtp,
46 #endif
47 #if defined(LWS_WITH_MQTT)
48 &lws_abs_protocol_mqttc,
49 #endif
50 };
51 #endif
52
53 const lws_abs_transport_t *
lws_abs_transport_get_by_name(const char * name)54 lws_abs_transport_get_by_name(const char *name)
55 {
56 int n;
57
58 for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abs_transports); n++)
59 if (!strcmp(name, available_abs_transports[n]->name))
60 return available_abs_transports[n];
61
62 lwsl_err("%s: cannot find '%s'\n", __func__, name);
63
64 return NULL;
65 }
66
67 const lws_abs_protocol_t *
lws_abs_protocol_get_by_name(const char * name)68 lws_abs_protocol_get_by_name(const char *name)
69 {
70 #if defined(LWS_WITH_ABSTRACT)
71 int n;
72
73 for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abs_protocols); n++)
74 if (!strcmp(name, available_abs_protocols[n]->name))
75 return available_abs_protocols[n];
76 #endif
77 lwsl_err("%s: cannot find '%s'\n", __func__, name);
78
79 return NULL;
80 }
81
82 const lws_token_map_t *
lws_abs_get_token(const lws_token_map_t * token_map,short name_index)83 lws_abs_get_token(const lws_token_map_t *token_map, short name_index)
84 {
85 if (!token_map)
86 return NULL;
87
88 do {
89 if (token_map->name_index == name_index)
90 return token_map;
91 token_map++;
92 } while (token_map->name_index);
93
94 return NULL;
95 }
96
97 static int
lws_abstract_compare_connection(lws_abs_t * abs1,lws_abs_t * abs2)98 lws_abstract_compare_connection(lws_abs_t *abs1, lws_abs_t *abs2)
99 {
100 /* it has to be using the same protocol */
101 if (abs1->ap != abs2->ap)
102 return 1;
103
104 /* protocol has to allow some kind of binding */
105 if (!abs1->ap->flags)
106 return 1;
107
108 /* it has to be using the same transport */
109 if (abs1->at != abs2->at)
110 return 1;
111
112 /*
113 * The transport must feel the endpoint and conditions in use match the
114 * requested endpoint and conditions... and the transport type must be
115 * willing to allow it
116 */
117 if (abs1->at->compare(abs1, abs2))
118 return 1;
119
120 /*
121 * The protocol must feel they are in compatible modes if any
122 * (and the protocol type must be willing to allow it)
123 */
124 if (abs1->ap->compare(abs1, abs2))
125 return 1;
126
127 /*
128 * If no objection by now, we can say there's already a comparable
129 * connection and both the protocol and transport feel we can make
130 * use of it.
131 */
132
133 return 0;
134 }
135
136 static int
find_compatible(struct lws_dll2 * d,void * user)137 find_compatible(struct lws_dll2 *d, void *user)
138 {
139 lws_abs_t *ai1 = (lws_abs_t *)user,
140 *ai2 = lws_container_of(d, lws_abs_t, abstract_instances);
141
142 if (!lws_abstract_compare_connection(ai1, ai2)) {
143 /* we can bind to it */
144 lws_dll2_add_tail(&ai1->bound, &ai2->children_owner);
145
146 return 1;
147 }
148
149 return 0;
150 }
151
152 lws_abs_t *
lws_abs_bind_and_create_instance(const lws_abs_t * abs)153 lws_abs_bind_and_create_instance(const lws_abs_t *abs)
154 {
155 size_t size = sizeof(lws_abs_t) + abs->ap->alloc + abs->at->alloc;
156 lws_abs_t *ai;
157 int n;
158
159 /*
160 * since we know we will allocate the lws_abs_t, the protocol's
161 * instance allocation, and the transport's instance allocation,
162 * we merge it into a single heap allocation
163 */
164 ai = lws_malloc(size, "abs inst");
165 if (!ai)
166 return NULL;
167
168 *ai = *abs;
169 ai->ati = NULL;
170
171 ai->api = (char *)ai + sizeof(lws_abs_t);
172
173 if (!ai->ap->flags) /* protocol only understands single connections */
174 goto fresh;
175
176 lws_vhost_lock(ai->vh); /* ----------------------------------- vh { */
177
178 /*
179 * Let's have a look for any already-connected transport we can use
180 */
181
182 n = lws_dll2_foreach_safe(&ai->vh->abstract_instances_owner, ai,
183 find_compatible);
184
185 lws_vhost_unlock(ai->vh); /* } vh --------------------------------- */
186
187 if (n)
188 goto vh_list_add;
189
190 /* there's no existing connection doing what we want */
191
192 fresh:
193
194 ai->ati = (char *)ai->api + abs->ap->alloc;
195 if (ai->at->create(ai)) {
196 ai->ati = NULL;
197 goto bail;
198 }
199
200 vh_list_add:
201 /* add us to the vhost's dll2 of instances */
202
203 lws_dll2_clear(&ai->abstract_instances);
204 lws_dll2_add_head(&ai->abstract_instances,
205 &ai->vh->abstract_instances_owner);
206
207 if (ai->ap->create(ai)) {
208 ai->api = NULL;
209 goto bail;
210 }
211
212 if (ai->bound.owner) { /* we are a piggybacker */
213 lws_abs_t *ai2 = lws_container_of(ai->bound.owner, lws_abs_t,
214 children_owner);
215 /*
216 * Provide an 'event' in the parent context to start handling
217 * the bind if it's otherwise idle. We give the parent abs
218 * because we don't know if we're "next" or whatever. Just that
219 * a child joined him and he should look into his child
220 * situation in case he was waiting for one to appear.
221 */
222 if (ai2->ap->child_bind(ai2)) {
223 lwsl_info("%s: anticpated child bind fail\n", __func__);
224 lws_dll2_remove(&ai->bound);
225
226 goto bail;
227 }
228 }
229
230 return ai;
231
232 bail:
233 lws_abs_destroy_instance(&ai);
234
235 return NULL;
236 }
237
238 /*
239 * We get called to clean up each child that was still bound to a parent
240 * at the time the parent is getting destroyed.
241 */
242
243 static void
__lws_abs_destroy_instance2(lws_abs_t ** ai)244 __lws_abs_destroy_instance2(lws_abs_t **ai)
245 {
246 lws_abs_t *a = *ai;
247
248 if (a->api)
249 a->ap->destroy(&a->api);
250 if (a->ati)
251 a->at->destroy(&a->ati);
252
253 lws_dll2_remove(&a->abstract_instances);
254
255 *ai = NULL;
256 free(a);
257 }
258
259 static int
__reap_children(struct lws_dll2 * d,void * user)260 __reap_children(struct lws_dll2 *d, void *user)
261 {
262 lws_abs_t *ac = lws_container_of(d, lws_abs_t, bound);
263
264 lws_dll2_foreach_safe(&ac->children_owner, NULL, __reap_children);
265
266 /* then destroy ourselves */
267
268 __lws_abs_destroy_instance2(&ac);
269
270 return 0;
271 }
272
273 void
lws_abs_destroy_instance(lws_abs_t ** ai)274 lws_abs_destroy_instance(lws_abs_t **ai)
275 {
276 lws_abs_t *a = *ai;
277
278 /* destroy child instances that are bound to us first... */
279
280 lws_vhost_lock(a->vh); /* ----------------------------------- vh { */
281
282 lws_dll2_foreach_safe(&a->children_owner, NULL, __reap_children);
283
284 /* ...then destroy ourselves */
285
286 __lws_abs_destroy_instance2(ai);
287
288 lws_vhost_unlock(a->vh); /* } vh --------------------------------- */
289 }
290
291 lws_abs_t *
lws_abstract_alloc(struct lws_vhost * vhost,void * user,const char * abstract_path,const lws_token_map_t * ap_tokens,const lws_token_map_t * at_tokens,struct lws_sequencer * seq,void * opaque_user_data)292 lws_abstract_alloc(struct lws_vhost *vhost, void *user,
293 const char *abstract_path, const lws_token_map_t *ap_tokens,
294 const lws_token_map_t *at_tokens, struct lws_sequencer *seq,
295 void *opaque_user_data)
296 {
297 lws_abs_t *abs = lws_zalloc(sizeof(*abs), __func__);
298 struct lws_tokenize ts;
299 lws_tokenize_elem e;
300 char tmp[30];
301
302 if (!abs)
303 return NULL;
304
305 lws_tokenize_init(&ts, abstract_path, LWS_TOKENIZE_F_MINUS_NONTERM);
306
307 e = lws_tokenize(&ts);
308 if (e != LWS_TOKZE_TOKEN)
309 goto abs_path_problem;
310
311 if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp)))
312 goto abs_path_problem;
313
314 abs->ap = lws_abs_protocol_get_by_name(tmp);
315 if (!abs->ap)
316 goto abs_path_problem;
317
318 e = lws_tokenize(&ts);
319 if (e != LWS_TOKZE_DELIMITER)
320 goto abs_path_problem;
321
322 e = lws_tokenize(&ts);
323 if (e != LWS_TOKZE_TOKEN)
324 goto abs_path_problem;
325
326 if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp)))
327 goto abs_path_problem;
328
329 abs->at = lws_abs_transport_get_by_name(tmp);
330 if (!abs->at)
331 goto abs_path_problem;
332
333 abs->vh = vhost;
334 abs->ap_tokens = ap_tokens;
335 abs->at_tokens = at_tokens;
336 abs->seq = seq;
337 abs->opaque_user_data = opaque_user_data;
338
339 lwsl_info("%s: allocated %s\n", __func__, abstract_path);
340
341 return abs;
342
343 abs_path_problem:
344 lwsl_err("%s: bad abs path '%s'\n", __func__, abstract_path);
345 lws_free_set_NULL(abs);
346
347 return NULL;
348 }
349
350 void
lws_abstract_free(lws_abs_t ** pabs)351 lws_abstract_free(lws_abs_t **pabs)
352 {
353 if (*pabs)
354 lws_free_set_NULL(*pabs);
355 }
356