Fix a variety of potential buffer overflows
[asterisk/asterisk.git] / main / format_pref.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Format Preference API
22  */
23
24 #include "asterisk.h"
25
26 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
27
28 #include "asterisk/_private.h"
29 #include "asterisk/frame.h"
30 #include "asterisk/channel.h"
31 #include "asterisk/utils.h"
32
33 void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right)
34 {
35         size_t f_len;
36         const const struct ast_format_list *f_list = ast_format_list_get(&f_len);
37         int x, differential = (int) 'A', mem;
38         char *from, *to;
39
40         /* TODO re-evaluate this function.  It is using the order of the formats specified
41          * in the global format list in a way that may not be safe. */
42         if (right) {
43                 from = pref->order;
44                 to = buf;
45                 mem = size;
46         } else {
47                 to = pref->order;
48                 from = buf;
49                 mem = AST_CODEC_PREF_SIZE;
50         }
51
52         memset(to, 0, mem);
53         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
54                 if (!from[x]) {
55                         break;
56                 }
57                 to[x] = right ? (from[x] + differential) : (from[x] - differential);
58                 if (!right && to[x] && (to[x] < f_len)) {
59                         ast_format_copy(&pref->formats[x], &f_list[to[x]-1].format);
60                 }
61         }
62         ast_format_list_destroy(f_list);
63 }
64
65 int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size)
66 {
67         int x;
68         struct ast_format format;
69         size_t total_len, slen;
70         const char *formatname;
71
72         memset(buf, 0, size);
73         total_len = size;
74         buf[0] = '(';
75         total_len--;
76         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
77                 if (total_len <= 0)
78                         break;
79                 if (!(ast_codec_pref_index(pref, x, &format)))
80                         break;
81                 if ((formatname = ast_getformatname(&format))) {
82                         slen = strlen(formatname);
83                         if (slen > total_len)
84                                 break;
85                         strncat(buf, formatname, total_len - 1); /* safe */
86                         total_len -= slen;
87                 }
88                 if (total_len && x < AST_CODEC_PREF_SIZE - 1 && ast_codec_pref_index(pref, x + 1, &format)) {
89                         strncat(buf, "|", total_len - 1); /* safe */
90                         total_len--;
91                 }
92         }
93         if (total_len) {
94                 strncat(buf, ")", total_len - 1); /* safe */
95                 total_len--;
96         }
97
98         return size - total_len;
99 }
100
101 struct ast_format *ast_codec_pref_index(struct ast_codec_pref *pref, int idx, struct ast_format *result)
102 {
103         if ((idx >= 0) && (idx < sizeof(pref->order)) && pref->formats[idx].id) {
104                 ast_format_copy(result, &pref->formats[idx]);
105         } else {
106                 ast_format_clear(result);
107                 return NULL;
108         }
109
110         return result;
111 }
112
113 /*! \brief Remove codec from pref list */
114 void ast_codec_pref_remove(struct ast_codec_pref *pref, struct ast_format *format)
115 {
116         struct ast_codec_pref oldorder;
117         int x, y = 0;
118         size_t f_len = 0;
119         const const struct ast_format_list *f_list;
120
121         if (!pref->order[0]) {
122                 return;
123         }
124
125         f_list = ast_format_list_get(&f_len);
126         memcpy(&oldorder, pref, sizeof(oldorder));
127         memset(pref, 0, sizeof(*pref));
128
129         for (x = 0; x < f_len; x++) {
130                 if (!oldorder.order[x]) {
131                         break;
132                 }
133                 if (ast_format_cmp(&f_list[oldorder.order[x]-1].format, format) == AST_FORMAT_CMP_NOT_EQUAL) {
134                         pref->order[y] = oldorder.order[x];
135                         ast_format_copy(&pref->formats[y], &oldorder.formats[x]);
136                         pref->framing[y++] = oldorder.framing[x];
137                 }
138         }
139         ast_format_list_destroy(f_list);
140 }
141
142 /*! \brief Append codec to list */
143 int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format)
144 {
145         int x, newindex = 0;
146         size_t f_len = 0;
147         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
148
149         ast_codec_pref_remove(pref, format);
150
151         for (x = 0; x < f_len; x++) {
152                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
153                         newindex = x + 1;
154                         break;
155                 }
156         }
157
158         if (newindex) {
159                 for (x = 0; x < f_len; x++) {
160                         if (!pref->order[x]) {
161                                 pref->order[x] = newindex;
162                                 ast_format_copy(&pref->formats[x], format);
163                                 break;
164                         }
165                 }
166         }
167
168         ast_format_list_destroy(f_list);
169         return x;
170 }
171
172 /*! \brief Prepend codec to list */
173 void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *format, int only_if_existing)
174 {
175         int x, newindex = 0;
176         size_t f_len = 0;
177         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
178
179         /* First step is to get the codecs "index number" */
180         for (x = 0; x < f_len; x++) {
181                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
182                         newindex = x + 1;
183                         break;
184                 }
185         }
186         /* Done if its unknown */
187         if (!newindex) {
188                 ast_format_list_destroy(f_list);
189                 return;
190         }
191
192         /* Now find any existing occurrence, or the end */
193         for (x = 0; x < AST_CODEC_PREF_SIZE; x++) {
194                 if (!pref->order[x] || pref->order[x] == newindex)
195                         break;
196         }
197
198         /* If we failed to find any occurrence, set to the end */
199         if (x == AST_CODEC_PREF_SIZE) {
200                 --x;
201         }
202
203         if (only_if_existing && !pref->order[x]) {
204                 ast_format_list_destroy(f_list);
205                 return;
206         }
207
208         /* Move down to make space to insert - either all the way to the end,
209            or as far as the existing location (which will be overwritten) */
210         for (; x > 0; x--) {
211                 pref->order[x] = pref->order[x - 1];
212                 pref->framing[x] = pref->framing[x - 1];
213                 ast_format_copy(&pref->formats[x], &pref->formats[x - 1]);
214         }
215
216         /* And insert the new entry */
217         pref->order[0] = newindex;
218         pref->framing[0] = 0; /* ? */
219         ast_format_copy(&pref->formats[0], format);
220         ast_format_list_destroy(f_list);
221 }
222
223 /*! \brief Set packet size for codec */
224 int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *format, int framems)
225 {
226         int x, idx = -1;
227         size_t f_len = 0;
228         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
229
230         for (x = 0; x < f_len; x++) {
231                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
232                         idx = x;
233                         break;
234                 }
235         }
236
237         if (idx < 0) {
238                 ast_format_list_destroy(f_list);
239                 return -1;
240         }
241
242         /* size validation */
243         if (!framems)
244                 framems = f_list[idx].def_ms;
245
246         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
247                 framems -= framems % f_list[idx].inc_ms;
248
249         if (framems < f_list[idx].min_ms)
250                 framems = f_list[idx].min_ms;
251
252         if (framems > f_list[idx].max_ms)
253                 framems = f_list[idx].max_ms;
254
255         for (x = 0; x < f_len; x++) {
256                 if (pref->order[x] == (idx + 1)) {
257                         pref->framing[x] = framems;
258                         break;
259                 }
260         }
261
262         ast_format_list_destroy(f_list);
263         return x;
264 }
265
266 /*! \brief Get packet size for codec */
267 struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struct ast_format *format)
268 {
269         int x, idx = -1, framems = 0;
270         struct ast_format_list fmt = { { 0, }, };
271         size_t f_len = 0;
272         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
273
274         for (x = 0; x < f_len; x++) {
275                 if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) {
276                         fmt = f_list[x];
277                         idx = x;
278                         break;
279                 }
280         }
281
282         if (idx < 0) {
283                 ast_log(AST_LOG_WARNING, "Format %s unknown; unable to get preferred codec packet size\n", ast_getformatname(format));
284                 return fmt;
285         }
286
287         for (x = 0; x < f_len; x++) {
288                 if (pref->order[x] == (idx + 1)) {
289                         framems = pref->framing[x];
290                         break;
291                 }
292         }
293
294         /* size validation */
295         if (!framems)
296                 framems = f_list[idx].def_ms;
297
298         if (f_list[idx].inc_ms && framems % f_list[idx].inc_ms) /* avoid division by zero */
299                 framems -= framems % f_list[idx].inc_ms;
300
301         if (framems < f_list[idx].min_ms)
302                 framems = f_list[idx].min_ms;
303
304         if (framems > f_list[idx].max_ms)
305                 framems = f_list[idx].max_ms;
306
307         fmt.cur_ms = framems;
308         ast_format_list_destroy(f_list);
309         return fmt;
310 }
311
312 /*! \brief Pick a codec */
313 struct ast_format *ast_codec_choose(struct ast_codec_pref *pref, struct ast_format_cap *cap, int find_best, struct ast_format *result)
314 {
315         int x, slot, found;
316         size_t f_len = 0;
317         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
318
319         for (x = 0; x < f_len; x++) {
320                 slot = pref->order[x];
321
322                 if (!slot)
323                         break;
324                 if (ast_format_cap_get_compatible_format(cap, &f_list[slot-1].format, result)) {
325                         found = 1; /*format is found and stored in result */
326                         break;
327                 }
328         }
329         ast_format_list_destroy(f_list);
330         if (found && (AST_FORMAT_GET_TYPE(result->id) == AST_FORMAT_TYPE_AUDIO)) {
331                 return result;
332         }
333         ast_format_clear(result);
334         ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec");
335
336         return find_best ? ast_best_codec(cap, result) : NULL;
337 }
338
339