1 /*! \file    utils.c
2  * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3  * \copyright GNU General Public License v3
4  * \brief    Utilities and helpers
5  * \details  Implementations of a few methods that may be of use here
6  * and there in the code.
7  *
8  * \ingroup core
9  * \ref core
10  */
11 
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <sys/file.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <arpa/inet.h>
21 #include <inttypes.h>
22 
23 #include <zlib.h>
24 #include <openssl/rand.h>
25 
26 #include "utils.h"
27 #include "debug.h"
28 #include "mutex.h"
29 
30 #if __MACH__
31 #include "mach_gettime.h"
32 #endif
33 
janus_get_monotonic_time(void)34 gint64 janus_get_monotonic_time(void) {
35 	struct timespec ts;
36 	clock_gettime (CLOCK_MONOTONIC, &ts);
37 	return (ts.tv_sec*G_GINT64_CONSTANT(1000000)) + (ts.tv_nsec/G_GINT64_CONSTANT(1000));
38 }
39 
janus_get_real_time(void)40 gint64 janus_get_real_time(void) {
41 	struct timespec ts;
42 	clock_gettime (CLOCK_REALTIME, &ts);
43 	return (ts.tv_sec*G_GINT64_CONSTANT(1000000)) + (ts.tv_nsec/G_GINT64_CONSTANT(1000));
44 }
45 
janus_is_true(const char * value)46 gboolean janus_is_true(const char *value) {
47 	return value && (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"));
48 }
49 
janus_strcmp_const_time(const void * str1,const void * str2)50 gboolean janus_strcmp_const_time(const void *str1, const void *str2) {
51 	if(str1 == NULL || str2 == NULL)
52 		return FALSE;
53 	const unsigned char *string1 = (const unsigned char *)str1;
54 	const unsigned char *string2 = (const unsigned char *)str2;
55 	size_t maxlen = strlen((char *)string1);
56 	if(strlen((char *)string2) > maxlen)
57 		maxlen = strlen((char *)string2);
58 	unsigned char *buf1 = g_malloc0(maxlen+1);
59 	memcpy(buf1, string1, strlen(str1));
60 	unsigned char *buf2 = g_malloc0(maxlen+1);
61 	memcpy(buf2, string2, strlen(str2));
62 	unsigned char result = 0;
63 	size_t i = 0;
64 	for (i = 0; i < maxlen; i++) {
65 		result |= buf1[i] ^ buf2[i];
66 	}
67 	g_free(buf1);
68 	buf1 = NULL;
69 	g_free(buf2);
70 	buf2 = NULL;
71 	return result == 0;
72 }
73 
janus_random_uint32(void)74 guint32 janus_random_uint32(void) {
75 	guint32 ret = 0;
76 	if(RAND_bytes((void *)&ret, sizeof(ret)) != 1) {
77 		JANUS_LOG(LOG_WARN, "Safe RAND_bytes() failed, falling back to unsafe PRNG\n");
78 		return g_random_int();
79 	}
80 	return ret;
81 }
82 
janus_random_uint64_full(void)83 guint64 janus_random_uint64_full(void) {
84 	guint64 ret = 0;
85 	if(RAND_bytes((void *)&ret, sizeof(ret)) != 1) {
86 		JANUS_LOG(LOG_WARN, "Safe RAND_bytes() failed, falling back to unsafe PRNG\n");
87 		return ((guint64)g_random_int() << 32) | g_random_int();
88 	}
89 	return ret;
90 }
91 
janus_random_uint64(void)92 guint64 janus_random_uint64(void) {
93 	return janus_random_uint64_full() & 0x1FFFFFFFFFFFFF;
94 }
95 
janus_random_uuid(void)96 char *janus_random_uuid(void) {
97 #if GLIB_CHECK_VERSION(2, 52, 0)
98 	return g_uuid_string_random();
99 #else
100 	/* g_uuid_string_random is only available from glib 2.52, so if it's
101 	 * not available we have to do it manually: the following code is
102 	 * heavily based on https://github.com/rxi/uuid4 (MIT license) */
103 	const char *template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
104 	const char *samples = "0123456789abcdef";
105 	union { unsigned char b[16]; uint64_t word[2]; } rnd;
106 	rnd.word[0] = janus_random_uint64_full();
107 	rnd.word[1] = janus_random_uint64_full();
108 	/* Generate the string */
109 	char uuid[37], *dst = uuid;
110 	const char *p = template;
111 	int i = 0, n = 0;
112 	while(*p) {
113 		n = rnd.b[i >> 1];
114 		n = (i & 1) ? (n >> 4) : (n & 0xf);
115 		switch (*p) {
116 			case 'x':
117 				*dst = samples[n];
118 				i++;
119 				break;
120 			case 'y':
121 				*dst = samples[(n & 0x3) + 8];
122 				i++;
123 				break;
124 			default:
125 				*dst = *p;
126 		}
127 		p++;
128 		dst++;
129 	}
130 	uuid[36] = '\0';
131 	return g_strdup(uuid);
132 #endif
133 }
134 
janus_uint64_dup(guint64 num)135 guint64 *janus_uint64_dup(guint64 num) {
136 	guint64 *numdup = g_malloc(sizeof(guint64));
137 	*numdup = num;
138 	return numdup;
139 }
140 
janus_uint64_hash(guint64 num)141 guint64 janus_uint64_hash(guint64 num) {
142 	num = (num ^ (num >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
143 	num = (num ^ (num >> 27)) * UINT64_C(0x94d049bb133111eb);
144 	num = num ^ (num >> 31);
145 	return num;
146 }
147 
janus_string_to_uint8(const char * str,uint8_t * num)148 int janus_string_to_uint8(const char *str, uint8_t *num) {
149 	if(str == NULL || num == NULL)
150 		return -EINVAL;
151 	long int val = strtol(str, 0, 10);
152 	if(val < 0 || val > UINT8_MAX)
153 		return -ERANGE;
154 	*num = val;
155 	return 0;
156 }
157 
janus_string_to_uint16(const char * str,uint16_t * num)158 int janus_string_to_uint16(const char *str, uint16_t *num) {
159 	if(str == NULL || num == NULL)
160 		return -EINVAL;
161 	long int val = strtol(str, 0, 10);
162 	if(val < 0 || val > UINT16_MAX)
163 		return -ERANGE;
164 	*num = val;
165 	return 0;
166 }
167 
janus_string_to_uint32(const char * str,uint32_t * num)168 int janus_string_to_uint32(const char *str, uint32_t *num) {
169 	if(str == NULL || num == NULL)
170 		return -EINVAL;
171 	long long int val = strtoll(str, 0, 10);
172 	if(val < 0 || val > UINT32_MAX)
173 		return -ERANGE;
174 	*num = val;
175 	return 0;
176 }
177 
janus_flags_reset(janus_flags * flags)178 void janus_flags_reset(janus_flags *flags) {
179 	if(flags != NULL)
180 		g_atomic_pointer_set(flags, 0);
181 }
182 
janus_flags_set(janus_flags * flags,gsize flag)183 void janus_flags_set(janus_flags *flags, gsize flag) {
184 	if(flags != NULL) {
185 		g_atomic_pointer_or(flags, flag);
186 	}
187 }
188 
janus_flags_clear(janus_flags * flags,gsize flag)189 void janus_flags_clear(janus_flags *flags, gsize flag) {
190 	if(flags != NULL) {
191 		g_atomic_pointer_and(flags, ~(flag));
192 	}
193 }
194 
janus_flags_is_set(janus_flags * flags,gsize flag)195 gboolean janus_flags_is_set(janus_flags *flags, gsize flag) {
196 	if(flags != NULL) {
197 		gsize bit = ((gsize) g_atomic_pointer_get(flags)) & flag;
198 		return (bit != 0);
199 	}
200 	return FALSE;
201 }
202 
203 /* Easy way to replace multiple occurrences of a string with another */
janus_string_replace(char * message,const char * old_string,const char * new_string)204 char *janus_string_replace(char *message, const char *old_string, const char *new_string)
205 {
206 	if(!message || !old_string || !new_string)
207 		return NULL;
208 
209 	if(!strstr(message, old_string)) {	/* Nothing to be done (old_string is not there) */
210 		return message;
211 	}
212 	if(!strcmp(old_string, new_string)) {	/* Nothing to be done (old_string=new_string) */
213 		return message;
214 	}
215 	if(strlen(old_string) == strlen(new_string)) {	/* Just overwrite */
216 		char *outgoing = message;
217 		char *pos = strstr(outgoing, old_string), *tmp = NULL;
218 		int i = 0;
219 		while(pos) {
220 			i++;
221 			memcpy(pos, new_string, strlen(new_string));
222 			pos += strlen(old_string);
223 			tmp = strstr(pos, old_string);
224 			pos = tmp;
225 		}
226 		return outgoing;
227 	} else {	/* We need to resize */
228 		char *outgoing = g_strdup(message);
229 		g_free(message);
230 		if(outgoing == NULL) {
231 			return NULL;
232 		}
233 		int diff = strlen(new_string) - strlen(old_string);
234 		/* Count occurrences */
235 		int counter = 0;
236 		char *pos = strstr(outgoing, old_string), *tmp = NULL;
237 		while(pos) {
238 			counter++;
239 			pos += strlen(old_string);
240 			tmp = strstr(pos, old_string);
241 			pos = tmp;
242 		}
243 		uint16_t old_stringlen = strlen(outgoing)+1, new_stringlen = old_stringlen + diff*counter;
244 		if(diff > 0) {	/* Resize now */
245 			tmp = g_realloc(outgoing, new_stringlen);
246 			outgoing = tmp;
247 		}
248 		/* Replace string */
249 		pos = strstr(outgoing, old_string);
250 		while(pos) {
251 			if(diff > 0) {	/* Move to the right (new_string is larger than old_string) */
252 				uint16_t len = strlen(pos)+1;
253 				memmove(pos + diff, pos, len);
254 				memcpy(pos, new_string, strlen(new_string));
255 				pos += strlen(new_string);
256 				tmp = strstr(pos, old_string);
257 			} else {	/* Move to the left (new_string is smaller than old_string) */
258 				uint16_t len = strlen(pos - diff)+1;
259 				memmove(pos, pos - diff, len);
260 				memcpy(pos, new_string, strlen(new_string));
261 				pos += strlen(old_string);
262 				tmp = strstr(pos, old_string);
263 			}
264 			pos = tmp;
265 		}
266 		if(diff < 0) {	/* We skipped the resize previously (shrinking memory) */
267 			tmp = g_realloc(outgoing, new_stringlen);
268 			outgoing = tmp;
269 		}
270 		outgoing[strlen(outgoing)] = '\0';
271 		return outgoing;
272 	}
273 }
274 
janus_strlcat(char * dest,const char * src,size_t dest_size)275 size_t janus_strlcat(char *dest, const char *src, size_t dest_size) {
276 	size_t ret = g_strlcat(dest, src, dest_size);
277 	if(ret >= dest_size)
278 		JANUS_LOG(LOG_ERR, "janus_strlcat: truncation occurred, %lu >= %lu\n", ret, dest_size);
279 	return ret;
280 }
281 
janus_mkdir(const char * dir,mode_t mode)282 int janus_mkdir(const char *dir, mode_t mode) {
283 	char tmp[256];
284 	char *p = NULL;
285 	size_t len;
286 
287 	int res = 0;
288 	g_snprintf(tmp, sizeof(tmp), "%s", dir);
289 	len = strlen(tmp);
290 	if(tmp[len - 1] == '/')
291 		tmp[len - 1] = 0;
292 	for(p = tmp + 1; *p; p++) {
293 		if(*p == '/') {
294 			*p = 0;
295 			res = mkdir(tmp, mode);
296 			if(res != 0 && errno != EEXIST) {
297 				JANUS_LOG(LOG_ERR, "Error creating folder %s\n", tmp);
298 				return res;
299 			}
300 			*p = '/';
301 		}
302 	}
303 	res = mkdir(tmp, mode);
304 	if(res != 0 && errno != EEXIST)
305 		return res;
306 	return 0;
307 }
308 
janus_make_absolute_path(const gchar * base_dir,const gchar * path)309 gchar *janus_make_absolute_path(const gchar *base_dir, const gchar *path) {
310 	if(!path)
311 		return NULL;
312 	if(g_path_is_absolute(path))
313 		return g_strdup(path);
314 	if(!base_dir)
315 		return NULL;
316 	return g_build_filename(base_dir, path, NULL);
317 }
318 
janus_get_codec_pt(const char * sdp,const char * codec)319 int janus_get_codec_pt(const char *sdp, const char *codec) {
320 	if(!sdp || !codec)
321 		return -1;
322 	int video = 0;
323 	const char *format = NULL, *format2 = NULL;
324 	if(!strcasecmp(codec, "opus")) {
325 		video = 0;
326 		format = "opus/48000/2";
327 		format2 = "OPUS/48000/2";
328 	} else if(!strcasecmp(codec, "pcmu")) {
329 		/* We know the payload type is 0: we just need to make sure it's there */
330 		video = 0;
331 		format = "pcmu/8000";
332 		format2 = "PCMU/8000";
333 	} else if(!strcasecmp(codec, "pcma")) {
334 		/* We know the payload type is 8: we just need to make sure it's there */
335 		video = 0;
336 		format = "pcma/8000";
337 		format2 = "PCMA/8000";
338 	} else if(!strcasecmp(codec, "g722")) {
339 		/* We know the payload type is 9: we just need to make sure it's there */
340 		video = 0;
341 		format = "g722/8000";
342 		format2 = "G722/8000";
343 	} else if(!strcasecmp(codec, "isac16")) {
344 		video = 0;
345 		format = "isac/16000";
346 		format2 = "ISAC/16000";
347 	} else if(!strcasecmp(codec, "isac32")) {
348 		video = 0;
349 		format = "isac/32000";
350 		format2 = "ISAC/32000";
351 	} else if(!strcasecmp(codec, "vp8")) {
352 		video = 1;
353 		format = "vp8/90000";
354 		format2 = "VP8/90000";
355 	} else if(!strcasecmp(codec, "vp9")) {
356 		video = 1;
357 		format = "vp9/90000";
358 		format2 = "VP9/90000";
359 	} else if(!strcasecmp(codec, "h264")) {
360 		video = 1;
361 		format = "h264/90000";
362 		format2 = "H264/90000";
363 	} else if(!strcasecmp(codec, "av1")) {
364 		video = 1;
365 		format = "av1x/90000";
366 		format2 = "AV1X/90000";
367 	} else if(!strcasecmp(codec, "h265")) {
368 		video = 1;
369 		format = "h265/90000";
370 		format2 = "H265/90000";
371 	} else {
372 		JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
373 		return -1;
374 	}
375 	/* First of all, let's check if the codec is there */
376 	if(!video) {
377 		if(!strstr(sdp, "m=audio") || (!strstr(sdp, format) && !strstr(sdp, format2)))
378 			return -2;
379 	} else {
380 		if(!strstr(sdp, "m=video") || (!strstr(sdp, format) && !strstr(sdp, format2)))
381 			return -2;
382 	}
383 	char rtpmap[50], rtpmap2[50];
384 	g_snprintf(rtpmap, 50, "a=rtpmap:%%d %s", format);
385 	g_snprintf(rtpmap2, 50, "a=rtpmap:%%d %s", format2);
386 	/* Look for the mapping */
387 	const char *line = strstr(sdp, video ? "m=video" : "m=audio");
388 	while(line) {
389 		char *next = strchr(line, '\n');
390 		if(next) {
391 			*next = '\0';
392 			if(strstr(line, "a=rtpmap") && strstr(line, format)) {
393 				/* Gotcha! */
394 				int pt = 0;
395 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
396 				if(sscanf(line, rtpmap, &pt) == 1) {
397 					*next = '\n';
398 					return pt;
399 				}
400 			} else if(strstr(line, "a=rtpmap") && strstr(line, format2)) {
401 				/* Gotcha! */
402 				int pt = 0;
403 				if(sscanf(line, rtpmap2, &pt) == 1) {
404 #pragma GCC diagnostic warning "-Wformat-nonliteral"
405 					*next = '\n';
406 					return pt;
407 				}
408 			}
409 			*next = '\n';
410 		}
411 		line = next ? (next+1) : NULL;
412 	}
413 	return -3;
414 }
415 
janus_get_codec_from_pt(const char * sdp,int pt)416 const char *janus_get_codec_from_pt(const char *sdp, int pt) {
417 	if(!sdp || pt < 0)
418 		return NULL;
419 	if(pt == 0)
420 		return "pcmu";
421 	if(pt == 8)
422 		return "pcma";
423 	if(pt == 9)
424 		return "g722";
425 	/* Look for the mapping */
426 	char rtpmap[50];
427 	g_snprintf(rtpmap, 50, "a=rtpmap:%d ", pt);
428 	const char *line = strstr(sdp, "m=");
429 	while(line) {
430 		char *next = strchr(line, '\n');
431 		if(next) {
432 			*next = '\0';
433 			if(strstr(line, rtpmap)) {
434 				/* Gotcha! */
435 				char name[100];
436 				if(sscanf(line, "a=rtpmap:%d %99s", &pt, name) == 2) {
437 					*next = '\n';
438 					if(strstr(name, "vp8") || strstr(name, "VP8"))
439 						return "vp8";
440 					if(strstr(name, "vp9") || strstr(name, "VP9"))
441 						return "vp9";
442 					if(strstr(name, "h264") || strstr(name, "H264"))
443 						return "h264";
444 					if(strstr(name, "av1") || strstr(name, "AV1"))
445 						return "av1";
446 					if(strstr(name, "h265") || strstr(name, "H265"))
447 						return "h265";
448 					if(strstr(name, "opus") || strstr(name, "OPUS"))
449 						return "opus";
450 					if(strstr(name, "pcmu") || strstr(name, "PCMU"))
451 						return "pcmu";
452 					if(strstr(name, "pcma") || strstr(name, "PCMA"))
453 						return "pcma";
454 					if(strstr(name, "g722") || strstr(name, "G722"))
455 						return "g722";
456 					if(strstr(name, "isac/16") || strstr(name, "ISAC/16"))
457 						return "isac16";
458 					if(strstr(name, "isac/32") || strstr(name, "ISAC/32"))
459 						return "isac32";
460 					JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", name);
461 					return NULL;
462 				}
463 			}
464 			*next = '\n';
465 		}
466 		line = next ? (next+1) : NULL;
467 	}
468 	return NULL;
469 }
470 
471 /* PID file management */
472 static char *pidfile = NULL;
473 static int pidfd = -1;
474 static FILE *pidf = NULL;
janus_pidfile_create(const char * file)475 int janus_pidfile_create(const char *file) {
476 	if(file == NULL)
477 		return 0;
478 	pidfile = g_strdup(file);
479 	/* Try creating a PID file (or opening an existing one) */
480 	pidfd = open(pidfile, O_RDWR|O_CREAT|O_TRUNC, 0644);
481 	if(pidfd < 0) {
482 		JANUS_LOG(LOG_FATAL, "Error opening/creating PID file %s, does Janus have enough permissions?\n", pidfile);
483 		return -1;
484 	}
485 	pidf = fdopen(pidfd, "r+");
486 	if(pidf == NULL) {
487 		JANUS_LOG(LOG_FATAL, "Error opening/creating PID file %s, does Janus have enough permissions?\n", pidfile);
488 		close(pidfd);
489 		return -1;
490 	}
491 	/* Try locking the PID file */
492 	int pid = 0;
493 	if(flock(pidfd, LOCK_EX|LOCK_NB) < 0) {
494 		if(fscanf(pidf, "%d", &pid) == 1) {
495 			JANUS_LOG(LOG_FATAL, "Error locking PID file (lock held by PID %d?)\n", pid);
496 		} else {
497 			JANUS_LOG(LOG_FATAL, "Error locking PID file (lock held by unknown PID?)\n");
498 		}
499 		fclose(pidf);
500 		return -1;
501 	}
502 	/* Write the PID */
503 	pid = getpid();
504 	if(fprintf(pidf, "%d\n", pid) < 0) {
505 		JANUS_LOG(LOG_FATAL, "Error writing PID in file, error %d (%s)\n", errno, g_strerror(errno));
506 		fclose(pidf);
507 		return -1;
508 	}
509 	fflush(pidf);
510 	/* We're done */
511 	return 0;
512 }
513 
janus_pidfile_remove(void)514 int janus_pidfile_remove(void) {
515 	if(pidfile == NULL || pidfd < 0 || pidf == NULL)
516 		return 0;
517 	/* Unlock the PID file and remove it */
518 	if(flock(pidfd, LOCK_UN) < 0) {
519 		JANUS_LOG(LOG_FATAL, "Error unlocking PID file\n");
520 		fclose(pidf);
521 		close(pidfd);
522 		return -1;
523 	}
524 	fclose(pidf);
525 	unlink(pidfile);
526 	g_free(pidfile);
527 	return 0;
528 }
529 
530 /* Protected folders management */
531 static GList *protected_folders = NULL;
532 static janus_mutex pf_mutex = JANUS_MUTEX_INITIALIZER;
533 
janus_protected_folder_add(const char * folder)534 void janus_protected_folder_add(const char *folder) {
535 	if(folder == NULL)
536 		return;
537 	janus_mutex_lock(&pf_mutex);
538 	protected_folders = g_list_append(protected_folders, g_strdup(folder));
539 	janus_mutex_unlock(&pf_mutex);
540 }
541 
janus_is_folder_protected(const char * path)542 gboolean janus_is_folder_protected(const char *path) {
543 	/* We need a valid pathname (can't start with a space, we don't trim) */
544 	if(path == NULL || *path == ' ')
545 		return TRUE;
546 	/* Resolve the pathname to its real path first */
547 	char resolved[PATH_MAX+1];
548 	resolved[0] = '\0';
549 	if(realpath(path, resolved) == NULL && errno != ENOENT) {
550 		JANUS_LOG(LOG_ERR, "Error resolving path '%s'... %d (%s)\n",
551 			path, errno, g_strerror(errno));
552 		return TRUE;
553 	}
554 	/* Traverse the list of protected folders to see if any match */
555 	janus_mutex_lock(&pf_mutex);
556 	if(protected_folders == NULL) {
557 		/* No protected folder in the list */
558 		janus_mutex_unlock(&pf_mutex);
559 		return FALSE;
560 	}
561 	gboolean protected = FALSE;
562 	GList *temp = protected_folders;
563 	while(temp) {
564 		char *folder = (char *)temp->data;
565 		if(folder && (strstr(resolved, folder) == resolved)) {
566 			protected = TRUE;
567 			break;
568 		}
569 		temp = temp->next;
570 	}
571 	janus_mutex_unlock(&pf_mutex);
572 	return protected;
573 }
574 
janus_protected_folders_clear(void)575 void janus_protected_folders_clear(void) {
576 	janus_mutex_lock(&pf_mutex);
577 	g_list_free_full(protected_folders, (GDestroyNotify)g_free);
578 	janus_mutex_unlock(&pf_mutex);
579 }
580 
581 
janus_get_json_type_name(int jtype,unsigned int flags,char * type_name)582 void janus_get_json_type_name(int jtype, unsigned int flags, char *type_name) {
583 	/* Longest possible combination is "a non-empty boolean" plus one for null char */
584 	gsize req_size = 20;
585 	/* Don't allow for both "positive" and "non-empty" because that needlessly increases the size. */
586 	if((flags & JANUS_JSON_PARAM_POSITIVE) != 0) {
587 		g_strlcpy(type_name, "a positive ", req_size);
588 	}
589 	else if((flags & JANUS_JSON_PARAM_NONEMPTY) != 0) {
590 		g_strlcpy(type_name, "a non-empty ", req_size);
591 	}
592 	else if(jtype == JSON_INTEGER || jtype == JSON_ARRAY || jtype == JSON_OBJECT) {
593 		g_strlcpy(type_name, "an ", req_size);
594 	}
595 	else {
596 		g_strlcpy(type_name, "a ", req_size);
597 	}
598 	switch(jtype) {
599 		case JSON_TRUE:
600 			janus_strlcat(type_name, "boolean", req_size);
601 			break;
602 		case JSON_INTEGER:
603 			janus_strlcat(type_name, "integer", req_size);
604 			break;
605 		case JSON_REAL:
606 			janus_strlcat(type_name, "real", req_size);
607 			break;
608 		case JSON_STRING:
609 			janus_strlcat(type_name, "string", req_size);
610 			break;
611 		case JSON_ARRAY:
612 			janus_strlcat(type_name, "array", req_size);
613 			break;
614 		case JSON_OBJECT:
615 			janus_strlcat(type_name, "object", req_size);
616 			break;
617 		default:
618 			break;
619 	}
620 }
621 
janus_json_is_valid(json_t * val,json_type jtype,unsigned int flags)622 gboolean janus_json_is_valid(json_t *val, json_type jtype, unsigned int flags) {
623 	gboolean is_valid = (json_typeof(val) == jtype || (jtype == JSON_TRUE && json_typeof(val) == JSON_FALSE));
624 	if(!is_valid)
625 		return FALSE;
626 	if((flags & JANUS_JSON_PARAM_POSITIVE) != 0) {
627 		switch(jtype) {
628 			case JSON_INTEGER:
629 				is_valid = (json_integer_value(val) >= 0);
630 				break;
631 			case JSON_REAL:
632 				is_valid = (json_real_value(val) >= 0);
633 				break;
634 			default:
635 				break;
636 		}
637 	}
638 	else if((flags & JANUS_JSON_PARAM_NONEMPTY) != 0) {
639 		switch(jtype) {
640 			case JSON_STRING:
641 				is_valid = (strlen(json_string_value(val)) > 0);
642 				break;
643 			case JSON_ARRAY:
644 				is_valid = (json_array_size(val) > 0);
645 				break;
646 			default:
647 				break;
648 		}
649 	}
650 	return is_valid;
651 }
652 
653 /* The following code is more related to codec specific helpers */
654 #if defined(__ppc__) || defined(__ppc64__)
655 	# define swap2(d)  \
656 	((d&0x000000ff)<<8) |  \
657 	((d&0x0000ff00)>>8)
658 #else
659 	# define swap2(d) d
660 #endif
661 
janus_vp8_is_keyframe(const char * buffer,int len)662 gboolean janus_vp8_is_keyframe(const char *buffer, int len) {
663 	if(!buffer || len < 16)
664 		return FALSE;
665 	/* Parse VP8 header now */
666 	uint8_t vp8pd = *buffer;
667 	uint8_t xbit = (vp8pd & 0x80);
668 	uint8_t sbit = (vp8pd & 0x10);
669 	if(xbit) {
670 		JANUS_LOG(LOG_HUGE, "  -- X bit is set!\n");
671 		/* Read the Extended control bits octet */
672 		buffer++;
673 		vp8pd = *buffer;
674 		uint8_t ibit = (vp8pd & 0x80);
675 		uint8_t lbit = (vp8pd & 0x40);
676 		uint8_t tbit = (vp8pd & 0x20);
677 		uint8_t kbit = (vp8pd & 0x10);
678 		if(ibit) {
679 			JANUS_LOG(LOG_HUGE, "  -- I bit is set!\n");
680 			/* Read the PictureID octet */
681 			buffer++;
682 			vp8pd = *buffer;
683 			uint16_t picid = vp8pd, wholepicid = picid;
684 			uint8_t mbit = (vp8pd & 0x80);
685 			if(mbit) {
686 				JANUS_LOG(LOG_HUGE, "  -- M bit is set!\n");
687 				memcpy(&picid, buffer, sizeof(uint16_t));
688 				wholepicid = ntohs(picid);
689 				picid = (wholepicid & 0x7FFF);
690 				buffer++;
691 			}
692 			JANUS_LOG(LOG_HUGE, "  -- -- PictureID: %"SCNu16"\n", picid);
693 		}
694 		if(lbit) {
695 			JANUS_LOG(LOG_HUGE, "  -- L bit is set!\n");
696 			/* Read the TL0PICIDX octet */
697 			buffer++;
698 			vp8pd = *buffer;
699 		}
700 		if(tbit || kbit) {
701 			JANUS_LOG(LOG_HUGE, "  -- T/K bit is set!\n");
702 			/* Read the TID/KEYIDX octet */
703 			buffer++;
704 			vp8pd = *buffer;
705 		}
706 	}
707 	buffer++;	/* Now we're in the payload */
708 	if(sbit) {
709 		JANUS_LOG(LOG_HUGE, "  -- S bit is set!\n");
710 		unsigned long int vp8ph = 0;
711 		memcpy(&vp8ph, buffer, 4);
712 		vp8ph = ntohl(vp8ph);
713 		uint8_t pbit = ((vp8ph & 0x01000000) >> 24);
714 		if(!pbit) {
715 			JANUS_LOG(LOG_HUGE, "  -- P bit is NOT set!\n");
716 			/* It is a key frame! Get resolution for debugging */
717 			unsigned char *c = (unsigned char *)buffer+3;
718 			/* vet via sync code */
719 			if(c[0]!=0x9d||c[1]!=0x01||c[2]!=0x2a) {
720 				JANUS_LOG(LOG_HUGE, "First 3-bytes after header not what they're supposed to be?\n");
721 			} else {
722 				unsigned short val3, val5;
723 				memcpy(&val3,c+3,sizeof(short));
724 				int vp8w = swap2(val3)&0x3fff;
725 				int vp8ws = swap2(val3)>>14;
726 				memcpy(&val5,c+5,sizeof(short));
727 				int vp8h = swap2(val5)&0x3fff;
728 				int vp8hs = swap2(val5)>>14;
729 				JANUS_LOG(LOG_HUGE, "Got a VP8 key frame: %dx%d (scale=%dx%d)\n", vp8w, vp8h, vp8ws, vp8hs);
730 				return TRUE;
731 			}
732 		}
733 	}
734 	/* If we got here it's not a key frame */
735 	return FALSE;
736 }
737 
janus_vp9_is_keyframe(const char * buffer,int len)738 gboolean janus_vp9_is_keyframe(const char *buffer, int len) {
739 	if(!buffer || len < 16)
740 		return FALSE;
741 	/* Parse VP9 header now */
742 	uint8_t vp9pd = *buffer;
743 	uint8_t ibit = (vp9pd & 0x80);
744 	uint8_t pbit = (vp9pd & 0x40);
745 	uint8_t lbit = (vp9pd & 0x20);
746 	uint8_t fbit = (vp9pd & 0x10);
747 	uint8_t vbit = (vp9pd & 0x02);
748 	buffer++;
749 	len--;
750 	if(ibit) {
751 		/* Read the PictureID octet */
752 		vp9pd = *buffer;
753 		uint16_t picid = vp9pd, wholepicid = picid;
754 		uint8_t mbit = (vp9pd & 0x80);
755 		if(!mbit) {
756 			buffer++;
757 			len--;
758 		} else {
759 			memcpy(&picid, buffer, sizeof(uint16_t));
760 			wholepicid = ntohs(picid);
761 			picid = (wholepicid & 0x7FFF);
762 			buffer += 2;
763 			len -= 2;
764 		}
765 	}
766 	if(lbit) {
767 		buffer++;
768 		len--;
769 		if(!fbit) {
770 			/* Non-flexible mode, skip TL0PICIDX */
771 			buffer++;
772 			len--;
773 		}
774 	}
775 	if(fbit && pbit) {
776 		/* Skip reference indices */
777 		uint8_t nbit = 1;
778 		while(nbit) {
779 			vp9pd = *buffer;
780 			nbit = (vp9pd & 0x01);
781 			buffer++;
782 			len--;
783 			if(len == 0)	/* Make sure we don't overflow */
784 				return FALSE;
785 		}
786 	}
787 	if(vbit) {
788 		/* Parse and skip SS */
789 		vp9pd = *buffer;
790 		uint n_s = (vp9pd & 0xE0) >> 5;
791 		n_s++;
792 		uint8_t ybit = (vp9pd & 0x10);
793 		if(ybit) {
794 			/* Iterate on all spatial layers and get resolution */
795 			buffer++;
796 			len--;
797 			if(len == 0)	/* Make sure we don't overflow */
798 				return FALSE;
799 			uint i=0;
800 			for(i=0; i<n_s && len>=4; i++,len-=4) {
801 				/* Width */
802 				uint16_t w;
803 				memcpy(&w, buffer, sizeof(uint16_t));
804 				int vp9w = ntohs(w);
805 				buffer += 2;
806 				/* Height */
807 				uint16_t h;
808 				memcpy(&h, buffer, sizeof(uint16_t));
809 				int vp9h = ntohs(h);
810 				buffer += 2;
811 				if(vp9w || vp9h) {
812 					JANUS_LOG(LOG_HUGE, "Got a VP9 key frame: %dx%d\n", vp9w, vp9h);
813 					return TRUE;
814 				}
815 			}
816 		}
817 	}
818 	/* If we got here it's not a key frame */
819 	return FALSE;
820 }
821 
janus_h264_is_keyframe(const char * buffer,int len)822 gboolean janus_h264_is_keyframe(const char *buffer, int len) {
823 	if(!buffer || len < 6)
824 		return FALSE;
825 	/* Parse H264 header now */
826 	uint8_t fragment = *buffer & 0x1F;
827 	uint8_t nal = *(buffer+1) & 0x1F;
828 	if(fragment == 7 || ((fragment == 28 || fragment == 29) && nal == 7)) {
829 		JANUS_LOG(LOG_HUGE, "Got an H264 key frame\n");
830 		return TRUE;
831 	} else if(fragment == 24) {
832 		/* May we find an SPS in this STAP-A? */
833 		buffer++;
834 		len--;
835 		uint16_t psize = 0;
836 		/* We're reading 3 bytes */
837 		while(len > 2) {
838 			memcpy(&psize, buffer, 2);
839 			psize = ntohs(psize);
840 			buffer += 2;
841 			len -= 2;
842 			int nal = *buffer & 0x1F;
843 			if(nal == 7) {
844 				JANUS_LOG(LOG_HUGE, "Got an SPS/PPS\n");
845 				return TRUE;
846 			}
847 			buffer += psize;
848 			len -= psize;
849 		}
850 	}
851 	/* If we got here it's not a key frame */
852 	return FALSE;
853 }
854 
janus_av1_is_keyframe(const char * buffer,int len)855 gboolean janus_av1_is_keyframe(const char *buffer, int len) {
856 	if(!buffer || len < 3)
857 		return FALSE;
858 	/* Read the aggregation header */
859 	uint8_t aggrh = *buffer;
860 	uint8_t zbit = (aggrh & 0x80) >> 7;
861 	uint8_t nbit = (aggrh & 0x08) >> 3;
862 	/* FIXME Ugly hack: we consider a packet with Z=0 and N=1 a keyframe */
863 	return (!zbit && nbit);
864 }
865 
janus_h265_is_keyframe(const char * buffer,int len)866 gboolean janus_h265_is_keyframe(const char *buffer, int len) {
867 	if(!buffer || len < 2)
868 		return FALSE;
869 	/* Parse the NAL unit */
870 	uint16_t unit = 0;
871 	memcpy(&unit, buffer, sizeof(uint16_t));
872 	unit = ntohs(unit);
873 	uint8_t type = (unit & 0x7E00) >> 9;
874 	if(type == 32 || type == 33) {
875 		/* FIXME We return TRUE for VPS and SPS */
876 		return TRUE;
877 	}
878 	return FALSE;
879 }
880 
janus_vp8_parse_descriptor(char * buffer,int len,uint16_t * picid,uint8_t * tl0picidx,uint8_t * tid,uint8_t * y,uint8_t * keyidx)881 int janus_vp8_parse_descriptor(char *buffer, int len,
882 		uint16_t *picid, uint8_t *tl0picidx, uint8_t *tid, uint8_t *y, uint8_t *keyidx) {
883 	if(!buffer || len < 6)
884 		return -1;
885 	if(picid)
886 		*picid = 0;
887 	if(tl0picidx)
888 		*tl0picidx = 0;
889 	if(tid)
890 		*tid = 0;
891 	if(y)
892 		*y = 0;
893 	if(keyidx)
894 		*keyidx = 0;
895 	uint8_t vp8pd = *buffer;
896 	uint8_t xbit = (vp8pd & 0x80);
897 	/* Read the Extended control bits octet */
898 	if(xbit) {
899 		buffer++;
900 		vp8pd = *buffer;
901 		uint8_t ibit = (vp8pd & 0x80);
902 		uint8_t lbit = (vp8pd & 0x40);
903 		uint8_t tbit = (vp8pd & 0x20);
904 		uint8_t kbit = (vp8pd & 0x10);
905 		if(ibit) {
906 			/* Read the PictureID octet */
907 			buffer++;
908 			vp8pd = *buffer;
909 			uint16_t partpicid = vp8pd, wholepicid = partpicid;
910 			uint8_t mbit = (vp8pd & 0x80);
911 			if(mbit) {
912 				memcpy(&partpicid, buffer, sizeof(uint16_t));
913 				wholepicid = ntohs(partpicid);
914 				partpicid = (wholepicid & 0x7FFF);
915 				if(picid)
916 					*picid = partpicid;
917 				buffer++;
918 			}
919 		}
920 		if(lbit) {
921 			/* Read the TL0PICIDX octet */
922 			buffer++;
923 			vp8pd = *buffer;
924 			if(tl0picidx)
925 				*tl0picidx = vp8pd;
926 		}
927 		if(tbit || kbit) {
928 			/* Read the TID/Y/KEYIDX octet */
929 			buffer++;
930 			vp8pd = *buffer;
931 			if(tid)
932 				*tid = (vp8pd & 0xC0) >> 6;
933 			if(y)
934 				*y = (vp8pd & 0x20) >> 5;
935 			if(keyidx)
936 				*keyidx = (vp8pd & 0x1F) >> 4;
937 		}
938 	}
939 	return 0;
940 }
941 
janus_vp8_replace_descriptor(char * buffer,int len,uint16_t picid,uint8_t tl0picidx)942 static int janus_vp8_replace_descriptor(char *buffer, int len, uint16_t picid, uint8_t tl0picidx) {
943 	if(!buffer || len < 6)
944 		return -1;
945 	uint8_t vp8pd = *buffer;
946 	uint8_t xbit = (vp8pd & 0x80);
947 	/* Read the Extended control bits octet */
948 	if(xbit) {
949 		buffer++;
950 		vp8pd = *buffer;
951 		uint8_t ibit = (vp8pd & 0x80);
952 		uint8_t lbit = (vp8pd & 0x40);
953 		uint8_t tbit = (vp8pd & 0x20);
954 		uint8_t kbit = (vp8pd & 0x10);
955 		if(ibit) {
956 			/* Overwrite the PictureID octet */
957 			buffer++;
958 			vp8pd = *buffer;
959 			uint8_t mbit = (vp8pd & 0x80);
960 			if(!mbit) {
961 				*buffer = picid;
962 			} else {
963 				uint16_t wholepicid = htons(picid);
964 				memcpy(buffer, &wholepicid, 2);
965 				*buffer |= 0x80;
966 				buffer++;
967 			}
968 		}
969 		if(lbit) {
970 			/* Overwrite the TL0PICIDX octet */
971 			buffer++;
972 			*buffer = tl0picidx;
973 		}
974 		if(tbit || kbit) {
975 			/* Should we overwrite the TID/Y/KEYIDX octet? */
976 			buffer++;
977 		}
978 	}
979 	return 0;
980 }
981 
janus_vp8_simulcast_context_reset(janus_vp8_simulcast_context * context)982 void janus_vp8_simulcast_context_reset(janus_vp8_simulcast_context *context) {
983 	if(context == NULL)
984 		return;
985 	/* Reset the context values */
986 	context->last_picid = 0;
987 	context->base_picid = 0;
988 	context->base_picid_prev = 0;
989 	context->last_tlzi = 0;
990 	context->base_tlzi = 0;
991 	context->base_tlzi_prev = 0;
992 }
993 
janus_vp8_simulcast_descriptor_update(char * buffer,int len,janus_vp8_simulcast_context * context,gboolean switched)994 void janus_vp8_simulcast_descriptor_update(char *buffer, int len, janus_vp8_simulcast_context *context, gboolean switched) {
995 	if(!buffer || len < 0)
996 		return;
997 	uint16_t picid = 0;
998 	uint8_t tlzi = 0;
999 	uint8_t tid = 0;
1000 	uint8_t ybit = 0;
1001 	uint8_t keyidx = 0;
1002 	/* Parse the identifiers in the VP8 payload descriptor */
1003 	if(janus_vp8_parse_descriptor(buffer, len, &picid, &tlzi, &tid, &ybit, &keyidx) < 0)
1004 		return;
1005 	if(switched) {
1006 		context->base_picid_prev = context->last_picid;
1007 		context->base_picid = picid;
1008 		context->base_tlzi_prev = context->last_tlzi;
1009 		context->base_tlzi = tlzi;
1010 	}
1011 	context->last_picid = (picid-context->base_picid)+context->base_picid_prev+1;
1012 	context->last_tlzi = (tlzi-context->base_tlzi)+context->base_tlzi_prev+1;
1013 	/* Overwrite the values in the VP8 payload descriptors with the ones we have */
1014 	janus_vp8_replace_descriptor(buffer, len, context->last_picid, context->last_tlzi);
1015 }
1016 
1017 /* Helper method to parse a VP9 RTP video frame and get some SVC-related info:
1018  * notice that this only works with VP9, right now, on an experimental basis */
janus_vp9_parse_svc(char * buffer,int len,gboolean * found,janus_vp9_svc_info * info)1019 int janus_vp9_parse_svc(char *buffer, int len, gboolean *found, janus_vp9_svc_info *info) {
1020 	if(!buffer || len < 8)
1021 		return -1;
1022 	/* VP9 depay: */
1023 		/* https://tools.ietf.org/html/draft-ietf-payload-vp9-04 */
1024 	/* Read the first octet (VP9 Payload Descriptor) */
1025 	uint8_t vp9pd = *buffer;
1026 	uint8_t ibit = (vp9pd & 0x80) >> 7;
1027 	uint8_t pbit = (vp9pd & 0x40) >> 6;
1028 	uint8_t lbit = (vp9pd & 0x20) >> 5;
1029 	uint8_t fbit = (vp9pd & 0x10) >> 4;
1030 	uint8_t bbit = (vp9pd & 0x08) >> 3;
1031 	uint8_t ebit = (vp9pd & 0x04) >> 2;
1032 	uint8_t vbit = (vp9pd & 0x02) >> 1;
1033 	if(!lbit) {
1034 		/* No Layer indices present, no need to go on */
1035 		if(found)
1036 			*found = FALSE;
1037 		return 0;
1038 	}
1039 	/* Move to the next octet and see what's there */
1040 	buffer++;
1041 	len--;
1042 	if(ibit) {
1043 		/* Read the PictureID octet */
1044 		vp9pd = *buffer;
1045 		uint16_t picid = vp9pd, wholepicid = picid;
1046 		uint8_t mbit = (vp9pd & 0x80);
1047 		if(!mbit) {
1048 			buffer++;
1049 			len--;
1050 		} else {
1051 			memcpy(&picid, buffer, sizeof(uint16_t));
1052 			wholepicid = ntohs(picid);
1053 			picid = (wholepicid & 0x7FFF);
1054 			buffer += 2;
1055 			len -= 2;
1056 		}
1057 	}
1058 	if(lbit) {
1059 		/* Read the octet and parse the layer indices now */
1060 		vp9pd = *buffer;
1061 		int tlid = (vp9pd & 0xE0) >> 5;
1062 		uint8_t ubit = (vp9pd & 0x10) >> 4;
1063 		int slid = (vp9pd & 0x0E) >> 1;
1064 		uint8_t dbit = (vp9pd & 0x01);
1065 		JANUS_LOG(LOG_HUGE, "%s Mode, Layer indices: Temporal: %d (u=%u), Spatial: %d (d=%u)\n",
1066 			fbit ? "Flexible" : "Non-flexible", tlid, ubit, slid, dbit);
1067 		if(info) {
1068 			info->temporal_layer = tlid;
1069 			info->spatial_layer = slid;
1070 			info->fbit = fbit;
1071 			info->pbit = pbit;
1072 			info->dbit = dbit;
1073 			info->ubit = ubit;
1074 			info->bbit = bbit;
1075 			info->ebit = ebit;
1076 		}
1077 		if(found)
1078 			*found = TRUE;
1079 		/* Go on, just to get to the SS, if available (which we currently ignore anyway) */
1080 		buffer++;
1081 		len--;
1082 		if(!fbit) {
1083 			/* Non-flexible mode, skip TL0PICIDX */
1084 			buffer++;
1085 			len--;
1086 		}
1087 	}
1088 	if(fbit && pbit) {
1089 		/* Skip reference indices */
1090 		uint8_t nbit = 1;
1091 		while(nbit) {
1092 			vp9pd = *buffer;
1093 			nbit = (vp9pd & 0x01);
1094 			buffer++;
1095 			len--;
1096 			if(len == 0)	/* Make sure we don't overflow */
1097 				return -1;
1098 		}
1099 	}
1100 	if(vbit) {
1101 		/* Parse and skip SS */
1102 		vp9pd = *buffer;
1103 		int n_s = (vp9pd & 0xE0) >> 5;
1104 		n_s++;
1105 		JANUS_LOG(LOG_HUGE, "There are %d spatial layers\n", n_s);
1106 		uint8_t ybit = (vp9pd & 0x10);
1107 		uint8_t gbit = (vp9pd & 0x08);
1108 		if(ybit) {
1109 			/* Iterate on all spatial layers and get resolution */
1110 			buffer++;
1111 			len--;
1112 			if(len == 0)	/* Make sure we don't overflow */
1113 				return -1;
1114 			int i=0;
1115 			for(i=0; i<n_s; i++) {
1116 				/* Been there, done that: skip skip skip */
1117 				buffer += 4;
1118 				len -= 4;
1119 				if(len <= 0)	/* Make sure we don't overflow */
1120 					return -1;
1121 			}
1122 		}
1123 		if(gbit) {
1124 			if(!ybit) {
1125 				buffer++;
1126 				len--;
1127 				if(len == 0)	/* Make sure we don't overflow */
1128 					return -1;
1129 			}
1130 			uint8_t n_g = *buffer;
1131 			JANUS_LOG(LOG_HUGE, "There are %u frames in a GOF\n", n_g);
1132 			buffer++;
1133 			len--;
1134 			if(len == 0)	/* Make sure we don't overflow */
1135 				return -1;
1136 			if(n_g > 0) {
1137 				int i=0;
1138 				for(i=0; i<n_g; i++) {
1139 					/* Read the R bits */
1140 					vp9pd = *buffer;
1141 					int r = (vp9pd & 0x0C) >> 2;
1142 					if(r > 0) {
1143 						/* Skip reference indices */
1144 						buffer += r;
1145 						len -= r;
1146 						if(len <= 0)	/* Make sure we don't overflow */
1147 							return -1;
1148 					}
1149 					buffer++;
1150 					len--;
1151 					if(len == 0)	/* Make sure we don't overflow */
1152 						return -1;
1153 				}
1154 			}
1155 		}
1156 	}
1157 	return 0;
1158 }
1159 
janus_push_bits(guint32 word,size_t num,guint32 val)1160 inline guint32 janus_push_bits(guint32 word, size_t num, guint32 val) {
1161 	if(num == 0)
1162 		return word;
1163 	return (word << num) | (val & (0xFFFFFFFF>>(32-num)));
1164 }
1165 
janus_set1(guint8 * data,size_t i,guint8 val)1166 inline void janus_set1(guint8 *data,size_t i,guint8 val) {
1167 	data[i] = val;
1168 }
1169 
janus_set2(guint8 * data,size_t i,guint32 val)1170 inline void janus_set2(guint8 *data,size_t i,guint32 val) {
1171 	data[i+1] = (guint8)(val);
1172 	data[i]   = (guint8)(val>>8);
1173 }
1174 
janus_set3(guint8 * data,size_t i,guint32 val)1175 inline void janus_set3(guint8 *data,size_t i,guint32 val) {
1176 	data[i+2] = (guint8)(val);
1177 	data[i+1] = (guint8)(val>>8);
1178 	data[i]   = (guint8)(val>>16);
1179 }
1180 
janus_set4(guint8 * data,size_t i,guint32 val)1181 inline void janus_set4(guint8 *data,size_t i,guint32 val) {
1182 	data[i+3] = (guint8)(val);
1183 	data[i+2] = (guint8)(val>>8);
1184 	data[i+1] = (guint8)(val>>16);
1185 	data[i]   = (guint8)(val>>24);
1186 }
1187 
1188 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
janus_gzip_compress(int compression,char * text,size_t tlen,char * compressed,size_t zlen)1189 size_t janus_gzip_compress(int compression, char *text, size_t tlen, char *compressed, size_t zlen) {
1190 	if(text == NULL || tlen < 1 || compressed == NULL || zlen < 1)
1191 		return -1;
1192 	if(compression < 0 || compression > 9) {
1193 		JANUS_LOG(LOG_WARN, "Invalid compression factor %d, falling back to default compression...\n", compression);
1194 		compression = Z_DEFAULT_COMPRESSION;
1195 	}
1196 
1197 	/* Initialize the deflater, and clarify we need gzip */
1198 	z_stream zs = { 0 };
1199 	zs.zalloc = Z_NULL;
1200 	zs.zfree = Z_NULL;
1201 	zs.opaque = Z_NULL;
1202 	zs.next_in = (Bytef *)text;
1203 	zs.avail_in = (uInt)tlen;
1204 	zs.next_out = (Bytef *)compressed;
1205 	zs.avail_out = (uInt)zlen;
1206 	int res = deflateInit2(&zs, compression, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
1207 	if(res != Z_OK) {
1208 		JANUS_LOG(LOG_ERR, "deflateInit error: %d\n", res);
1209 		return 0;
1210 	}
1211 	/* Deflate the string */
1212 	res = deflate(&zs, Z_FINISH);
1213 	if(res != Z_STREAM_END) {
1214 		JANUS_LOG(LOG_ERR, "deflate error: %d\n", res);
1215 		return 0;
1216 	}
1217 	res = deflateEnd(&zs);
1218 	if(res != Z_OK) {
1219 		JANUS_LOG(LOG_ERR, "deflateEnd error: %d\n", res);
1220 		return 0;
1221 	}
1222 
1223 	/* Done, return the size of the compressed data */
1224 	return zs.total_out;
1225 }
1226 #endif
1227