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 #if !defined(NO_GNU_SOURCE_THIS_TIME)
26 #define NO_GNU_SOURCE_THIS_TIME
27 #endif
28 #if !defined(_DARWIN_C_SOURCE)
29 #define _DARWIN_C_SOURCE
30 #endif
31 
32 #include "private-lib-core.h"
33 #include <string.h>
34 #include <stdio.h>
35 
36 #include <sys/stat.h>
37 #if defined(WIN32)
38 #include <direct.h>
39 #define read _read
40 #define open _open
41 #define close _close
42 #define write _write
43 #define mkdir(x,y) _mkdir(x)
44 #define rmdir _rmdir
45 #define unlink _unlink
46 #define HAVE_STRUCT_TIMESPEC
47 #if defined(pid_t)
48 #undef pid_t
49 #endif
50 #endif /* win32 */
51 
52 #define COMBO_SIZEOF 512
53 
54 #if !defined(LWS_PLAT_FREERTOS)
55 
56 #if defined(WIN32)
57 #include "../../win32port/dirent/dirent-win32.h"
58 #else
59 #include <dirent.h>
60 #endif
61 
filter(const struct dirent * ent)62 static int filter(const struct dirent *ent)
63 {
64 	if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
65 		return 0;
66 
67 	return 1;
68 }
69 
70 
71 #if !defined(WIN32)
72 static char csep = '/';
73 #else
74 static char csep = '\\';
75 #endif
76 
77 static void
lws_dir_via_stat(char * combo,size_t l,const char * path,struct lws_dir_entry * lde)78 lws_dir_via_stat(char *combo, size_t l, const char *path, struct lws_dir_entry *lde)
79 {
80         struct stat s;
81 
82         lws_strncpy(combo + l, path, COMBO_SIZEOF - l);
83 
84         lde->type = LDOT_UNKNOWN;
85 
86         if (!stat(combo, &s)) {
87 		switch (s.st_mode & S_IFMT) {
88 		case S_IFBLK:
89 			lde->type = LDOT_BLOCK;
90 			break;
91 		case S_IFCHR:
92 			lde->type = LDOT_CHAR;
93 			break;
94 		case S_IFDIR:
95 			lde->type = LDOT_DIR;
96 			break;
97 		case S_IFIFO:
98 			lde->type = LDOT_FIFO;
99 			break;
100 #if !defined(WIN32)
101 		case S_IFLNK:
102 			lde->type = LDOT_LINK;
103 			break;
104 #endif
105 		case S_IFREG:
106 			lde->type = LDOT_FILE;
107 			break;
108 		default:
109 			break;
110 		}
111         }
112 }
113 
114 int
lws_dir(const char * dirpath,void * user,lws_dir_callback_function cb)115 lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb)
116 {
117 	struct lws_dir_entry lde;
118 	struct dirent **namelist;
119 	int n, i, ret = 1;
120 	char combo[COMBO_SIZEOF];
121 	size_t l;
122 
123 	l = (size_t)(ssize_t)lws_snprintf(combo, COMBO_SIZEOF - 2, "%s", dirpath);
124 	combo[l++] = csep;
125 	combo[l] = '\0';
126 
127 	n = scandir((char *)dirpath, &namelist, filter, alphasort);
128 	if (n < 0) {
129 		lwsl_err("Scandir on '%s' failed, errno %d\n", dirpath, LWS_ERRNO);
130 		return 1;
131 	}
132 
133 	for (i = 0; i < n; i++) {
134 #if !defined(__sun)
135 		unsigned int type = namelist[i]->d_type;
136 #endif
137 		if (strchr(namelist[i]->d_name, '~'))
138 			goto skip;
139 		lde.name = namelist[i]->d_name;
140 
141 		/*
142 		 * some filesystems don't report this (ZFS) and tell that
143 		 * files are LDOT_UNKNOWN
144 		 */
145 
146 #if defined(__sun)
147 		lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde);
148 #else
149 		/*
150 		 * XFS on Linux doesn't fill in d_type at all, always zero.
151 		 */
152 
153 		if (DT_BLK != DT_UNKNOWN && type == DT_BLK)
154 			lde.type = LDOT_BLOCK;
155 		else if (DT_CHR != DT_UNKNOWN && type == DT_CHR)
156 			lde.type = LDOT_CHAR;
157 		else if (DT_DIR != DT_UNKNOWN && type == DT_DIR)
158 			lde.type = LDOT_DIR;
159 		else if (DT_FIFO != DT_UNKNOWN && type == DT_FIFO)
160 			lde.type = LDOT_FIFO;
161 		else if (DT_LNK != DT_UNKNOWN && type == DT_LNK)
162 			lde.type = LDOT_LINK;
163 		else if (DT_REG != DT_UNKNOWN && type == DT_REG)
164 			lde.type = LDOT_FILE;
165 		else if (DT_SOCK != DT_UNKNOWN && type == DT_SOCK)
166 			lde.type = LDOTT_SOCKET;
167 		else {
168 			lde.type = LDOT_UNKNOWN;
169 			lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde);
170 		}
171 #endif
172 		if (cb(dirpath, user, &lde)) {
173 			while (i < n)
174 				free(namelist[i++]);
175 			ret = 0; /* told to stop by cb */
176 			goto bail;
177 		}
178 skip:
179 		free(namelist[i]);
180 	}
181 
182 bail:
183 	free(namelist);
184 
185 	return ret;
186 }
187 
188 /*
189  * Check filename against one globby filter
190  *
191  * We can support things like "*.rpm"
192  */
193 
194 static int
lws_dir_glob_check(const char * nm,const char * filt)195 lws_dir_glob_check(const char *nm, const char *filt)
196 {
197 	while (*nm) {
198 		if (*filt == '*') {
199 			if (!strcmp(nm, filt + 1))
200 				return 1;
201 		} else {
202 			if (*nm != *filt)
203 				return 0;
204 			filt++;
205 		}
206 		nm++;
207 	}
208 
209 	return 0;
210 }
211 
212 /*
213  * We get passed a single filter string, like "*.txt" or "mydir/\*.rpm" or so.
214  */
215 
216 int
lws_dir_glob_cb(const char * dirpath,void * user,struct lws_dir_entry * lde)217 lws_dir_glob_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
218 {
219 	lws_dir_glob_t *filter = (lws_dir_glob_t*)user;
220 	char path[384];
221 
222 	if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
223 		return 0;
224 
225 	if (lde->type == LDOT_DIR)
226 		return 0;
227 
228 	if (lws_dir_glob_check(lde->name, filter->filter)) {
229 		lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep,
230 							   lde->name);
231 		filter->cb(filter->user, path);
232 	}
233 
234 	return 0;
235 }
236 
237 int
lws_dir_rm_rf_cb(const char * dirpath,void * user,struct lws_dir_entry * lde)238 lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
239 {
240 	char path[384];
241 
242 	if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
243 		return 0;
244 
245 	lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep, lde->name);
246 
247 	if (lde->type == LDOT_DIR) {
248 #if !defined(WIN32) && !defined(_WIN32) && !defined(__COVERITY__)
249 		char dummy[8];
250 		/*
251 		 * hm... eg, recursive dir symlinks can show up a LDOT_DIR
252 		 * here.  If it's a symlink, don't recurse into it.
253 		 *
254 		 * Notice we immediately discard dummy without looking in it.
255 		 * There is no way to get into trouble from its lack of NUL
256 		 * termination in dummy[].  We just wanted to know if it was
257 		 * a symlink at all.
258 		 *
259 		 * Hide this from Coverity since it flags any use of readlink()
260 		 * even if safe.
261 		 */
262 		if (readlink(path, dummy, sizeof(dummy)) < 0)
263 #endif
264 			lws_dir(path, NULL, lws_dir_rm_rf_cb);
265 
266 		if (rmdir(path))
267 			lwsl_warn("%s: rmdir %s failed %d\n", __func__, path, errno);
268 	} else {
269 		if (unlink(path)) {
270 #if defined(WIN32)
271 			SetFileAttributesA(path, FILE_ATTRIBUTE_NORMAL);
272 			if (unlink(path))
273 #else
274 			if (rmdir(path))
275 #endif
276 			lwsl_warn("%s: unlink %s failed %d (type %d)\n",
277 					__func__, path, errno, lde->type);
278 		}
279 	}
280 
281 	return 0;
282 }
283 
284 
285 #endif
286 
287 #if defined(LWS_WITH_PLUGINS_API)
288 
289 struct lws_plugins_args {
290 	struct lws_plugin	**pplugin;
291 	const char		*_class;
292 	const char		*filter;
293 	each_plugin_cb_t	each;
294 	void			*each_user;
295 };
296 
297 static int
lws_plugins_dir_cb(const char * dirpath,void * user,struct lws_dir_entry * lde)298 lws_plugins_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
299 {
300 	struct lws_plugins_args *pa = (struct lws_plugins_args *)user;
301 	char path[256], base[64], *q = base;
302 	const lws_plugin_header_t *pl;
303 	const char *p;
304 
305 	if (strlen(lde->name) < 7)
306 		return 0; /* keep going */
307 
308 	/*
309 	 * The actual plugin names for protocol plugins look like
310 	 * "libprotocol_lws_ssh_base.so" and for event libs
311 	 * "libwebsockets-evlib_ev.so"... to recover the base name of
312 	 * "lws_ssh_base" and "evlib_ev" we strip from the left to after the
313 	 * first _ or -, and then truncate at the first .
314 	 */
315 
316 	p = lde->name;
317 	while (*p && *p != '_' && *p != '-')
318 		p++;
319 	if (!*p)
320 		return 0;
321 	p++;
322 	while (*p && *p != '.' && lws_ptr_diff(q, base) < (int)sizeof(base) - 1)
323 		*q++ = *p++;
324 	*q = '\0';
325 
326 	/* if he's given a filter, only match if base matches it */
327 	if (pa->filter && strcmp(base, pa->filter))
328 		return 0; /* keep going */
329 
330 	lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name);
331 
332 	pl = lws_plat_dlopen(pa->pplugin, path, base, pa->_class,
333 			     pa->each, pa->each_user);
334 
335 	/*
336 	 * If we were looking for a specific plugin, finding it should make
337 	 * us stop looking (eg, to account for directory precedence of the
338 	 * same plugin).  If scanning for plugins in a dir, we always keep
339 	 * going.
340 	 */
341 
342 	return pa->filter && pl;
343 }
344 
345 int
lws_plugins_init(struct lws_plugin ** pplugin,const char * const * d,const char * _class,const char * filter,each_plugin_cb_t each,void * each_user)346 lws_plugins_init(struct lws_plugin **pplugin, const char * const *d,
347 		 const char *_class, const char *filter,
348 		 each_plugin_cb_t each, void *each_user)
349 {
350 	struct lws_plugins_args pa;
351 	char *ld_env;
352 	int ret = 1;
353 
354 	pa.pplugin = pplugin;
355 	pa._class = _class;
356 	pa.each = each;
357 	pa.each_user = each_user;
358 	pa.filter = filter;
359 
360 	/*
361 	 * Check LD_LIBRARY_PATH override path first if present
362 	 */
363 
364 	ld_env = getenv("LD_LIBRARY_PATH");
365 	if (ld_env) {
366 		char temp[128];
367 		struct lws_tokenize ts;
368 
369 		memset(&ts, 0, sizeof(ts));
370 		ts.start = ld_env;
371 		ts.len = strlen(ld_env);
372 		ts.flags = LWS_TOKENIZE_F_SLASH_NONTERM |
373 			   LWS_TOKENIZE_F_DOT_NONTERM |
374 			   LWS_TOKENIZE_F_MINUS_NONTERM |
375 			   LWS_TOKENIZE_F_NO_INTEGERS |
376 			   LWS_TOKENIZE_F_NO_FLOATS;
377 
378 		do {
379 			ts.e = (int8_t)lws_tokenize(&ts);
380 			if (ts.e != LWS_TOKZE_TOKEN)
381 				continue;
382 
383 			lws_strnncpy(temp, ts.token,
384 				     ts.token_len,
385 				     sizeof(temp));
386 
387 			lwsl_info("%s: trying %s\n", __func__, temp);
388 			if (!lws_dir(temp, &pa, lws_plugins_dir_cb))
389 				ret = 0;
390 
391 		} while (ts.e > 0);
392 	}
393 
394 	while (d && *d) {
395 		lwsl_info("%s: trying %s\n", __func__, *d);
396 		if (!lws_dir(*d, &pa, lws_plugins_dir_cb))
397 			ret = 0;
398 
399 		d++;
400 	}
401 
402 	return ret;
403 }
404 
405 int
lws_plugins_destroy(struct lws_plugin ** pplugin,each_plugin_cb_t each,void * each_user)406 lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each,
407 		    void *each_user)
408 {
409 	struct lws_plugin *p = *pplugin, *p1;
410 
411 	while (p) {
412 		if (each)
413 			each(p, each_user);
414 		lws_plat_destroy_dl(p);
415 		p1 = p->list;
416 		p->list = NULL;
417 		lws_free(p);
418 		p = p1;
419 	}
420 
421 	*pplugin = NULL;
422 
423 	return 0;
424 }
425 #endif
426