Asterisk - The Open Source Telephony Project  18.5.0
firmware.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Spencer <[email protected]>
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 /*! \file
20  *
21  * \brief IAX Firmware Support
22  *
23  * \author Mark Spencer <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <dirent.h>
37 #include <sys/mman.h>
38 #include <arpa/inet.h>
39 
40 #include "asterisk/linkedlists.h"
41 #include "asterisk/md5.h"
42 #include "asterisk/paths.h"
43 #include "asterisk/utils.h"
44 
45 #include "include/firmware.h"
46 
47 #define IAX_FIRMWARE_SUBDIR "/firmware/iax"
48 
49 struct iax_firmware {
51  int fd;
52  int mmaplen;
53  int dead;
55  unsigned char *buf;
56 };
57 
59 
60 static int try_firmware(char *s)
61 {
62  struct stat stbuf;
63  struct iax_firmware *cur = NULL;
64  int ifd, fd, res, len, chunk;
65  struct ast_iax2_firmware_header *fwh, fwh2;
66  struct MD5Context md5;
67  unsigned char sum[16], buf[1024];
68  char *s2, *last;
69 
70  s2 = ast_alloca(strlen(s) + 100);
71 
72  last = strrchr(s, '/');
73  if (last)
74  last++;
75  else
76  last = s;
77 
78  snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, ast_random());
79 
80  if (stat(s, &stbuf) < 0) {
81  ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
82  return -1;
83  }
84 
85  /* Make sure it's not a directory */
86  if (S_ISDIR(stbuf.st_mode))
87  return -1;
88  ifd = open(s, O_RDONLY);
89  if (ifd < 0) {
90  ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
91  return -1;
92  }
93  fd = open(s2, O_RDWR | O_CREAT | O_EXCL, AST_FILE_MODE);
94  if (fd < 0) {
95  ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
96  close(ifd);
97  return -1;
98  }
99  /* Unlink our newly created file */
100  unlink(s2);
101 
102  /* Now copy the firmware into it */
103  len = stbuf.st_size;
104  while(len) {
105  chunk = len;
106  if (chunk > sizeof(buf))
107  chunk = sizeof(buf);
108  res = read(ifd, buf, chunk);
109  if (res != chunk) {
110  ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
111  close(ifd);
112  close(fd);
113  return -1;
114  }
115  res = write(fd, buf, chunk);
116  if (res != chunk) {
117  ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
118  close(ifd);
119  close(fd);
120  return -1;
121  }
122  len -= chunk;
123  }
124  close(ifd);
125  /* Return to the beginning */
126  lseek(fd, 0, SEEK_SET);
127  if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
128  ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
129  close(fd);
130  return -1;
131  }
132  if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
133  ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
134  close(fd);
135  return -1;
136  }
137  if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
138  ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
139  close(fd);
140  return -1;
141  }
142  if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
143  ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
144  close(fd);
145  return -1;
146  }
147  fwh = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
148  if (fwh == MAP_FAILED) {
149  ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
150  close(fd);
151  return -1;
152  }
153  MD5Init(&md5);
154  MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
155  MD5Final(sum, &md5);
156  if (memcmp(sum, fwh->chksum, sizeof(sum))) {
157  ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
158  munmap((void*)fwh, stbuf.st_size);
159  close(fd);
160  return -1;
161  }
162 
164  if (!strcmp((const char *) cur->fwh->devname, (const char *) fwh->devname)) {
165  /* Found a candidate */
166  if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
167  /* The version we have on loaded is older, load this one instead */
168  break;
169  /* This version is no newer than what we have. Don't worry about it.
170  We'll consider it a proper load anyhow though */
171  munmap((void*)fwh, stbuf.st_size);
172  close(fd);
173  return 0;
174  }
175  }
176 
177  if (!cur && ((cur = ast_calloc(1, sizeof(*cur))))) {
178  cur->fd = -1;
180  }
181 
182  if (cur) {
183  if (cur->fwh)
184  munmap((void*)cur->fwh, cur->mmaplen);
185  if (cur->fd > -1)
186  close(cur->fd);
187  cur->fwh = fwh;
188  cur->fd = fd;
189  cur->mmaplen = stbuf.st_size;
190  cur->dead = 0;
191  }
192 
193  return 0;
194 }
195 
196 static void destroy_firmware(struct iax_firmware *cur)
197 {
198  /* Close firmware */
199  if (cur->fwh) {
200  munmap((void*)cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
201  }
202  close(cur->fd);
203  ast_free(cur);
204 }
205 
207 {
208  struct iax_firmware *cur = NULL;
209  DIR *fwd;
210  struct dirent *de;
211  char fn[PATH_MAX + sizeof(IAX_FIRMWARE_SUBDIR) + sizeof(de->d_name)];
212 
214 
215  /* Mark all as dead */
217  cur->dead = 1;
218  }
219 
220  /* Now that we have marked them dead... load new ones */
221  snprintf(fn, sizeof(fn), "%s%s", ast_config_AST_DATA_DIR, IAX_FIRMWARE_SUBDIR);
222  fwd = opendir(fn);
223  if (fwd) {
224  while((de = readdir(fwd))) {
225  if (de->d_name[0] != '.') {
226  snprintf(fn, sizeof(fn), "%s%s/%s",
228  if (!try_firmware(fn)) {
229  ast_verb(2, "Loaded firmware '%s'\n", de->d_name);
230  }
231  }
232  }
233  closedir(fwd);
234  } else {
235  ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", fn, strerror(errno));
236  }
237 
238  /* Clean up leftovers */
240  if (!cur->dead)
241  continue;
243  destroy_firmware(cur);
244  }
246 
248 }
249 
251 {
252  struct iax_firmware *cur = NULL;
253 
257  destroy_firmware(cur);
258  }
261 }
262 
263 int iax_firmware_get_version(const char *dev, uint16_t *version)
264 {
265  struct iax_firmware *cur = NULL;
266 
267  if (ast_strlen_zero(dev))
268  return 0;
269 
272  if (!strcmp(dev, (const char *) cur->fwh->devname)) {
273  *version = ntohs(cur->fwh->version);
275  return 1;
276  }
277  }
279 
280  return 0;
281 }
282 
283 int iax_firmware_append(struct iax_ie_data *ied, const char *dev, unsigned int desc)
284 {
285  int res = -1;
286  unsigned int bs = desc & 0xff;
287  unsigned int start = (desc >> 8) & 0xffffff;
288  unsigned int bytes;
289  struct iax_firmware *cur;
290 
291  if (ast_strlen_zero((char *)dev) || !bs)
292  return -1;
293 
294  start *= bs;
295 
298  if (strcmp(dev, (const char *) cur->fwh->devname))
299  continue;
301  if (start < ntohl(cur->fwh->datalen)) {
302  bytes = ntohl(cur->fwh->datalen) - start;
303  if (bytes > bs)
304  bytes = bs;
305  iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
306  } else {
307  bytes = 0;
309  }
310  if (bytes == bs)
311  res = 0;
312  else
313  res = 1;
314  break;
315  }
317 
318  return res;
319 }
320 
322  const char *filter,
323  int (*callback)(struct ast_iax2_firmware_header *header, void *data),
324  void *data)
325 {
326  struct iax_firmware *cur = NULL;
327 
328  if (!callback) {
329  return;
330  }
331 
334  if (!filter || !strcasecmp(filter, (const char *) cur->fwh->devname)) {
335  if (callback(cur->fwh, data)) {
336  break;
337  }
338  }
339  }
341 }
unsigned char data[0]
Definition: iax2.h:299
unsigned int datalen
Definition: iax2.h:297
void iax_firmware_reload(void)
Definition: firmware.c:206
int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value)
Definition: parser.c:755
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk main include file. File version handling, generic pbx functions.
unsigned int magic
Definition: iax2.h:294
int iax_firmware_get_version(const char *dev, uint16_t *version)
Definition: firmware.c:263
unsigned short version
Definition: iax2.h:295
#define LOG_WARNING
Definition: logger.h:274
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
void MD5Final(unsigned char digest[16], struct MD5Context *context)
Definition: md5.c:120
static const char desc[]
Definition: cdr_mysql.c:73
int mmaplen
Definition: firmware.c:52
static int try_firmware(char *s)
Definition: firmware.c:60
#define NULL
Definition: resample.c:96
#define AST_FILE_MODE
Definition: asterisk.h:32
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
#define ast_verb(level,...)
Definition: logger.h:463
void MD5Init(struct MD5Context *context)
Definition: md5.c:57
int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen)
Definition: parser.c:725
char * bs
Definition: eagi_proxy.c:73
Utility functions.
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_log
Definition: astobj2.c:42
Asterisk file paths, configured in asterisk.conf.
struct sla_ringing_trunk * last
Definition: app_meetme.c:1092
IAX Firmware Support header file.
#define IAX_FIRMWARE_MAGIC
Definition: iax2.h:291
int iax_ie_append(struct iax_ie_data *ied, unsigned char ie)
Definition: parser.c:779
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
long int ast_random(void)
Definition: main/utils.c:2064
A set of macros to manage forward-linked lists.
static void destroy_firmware(struct iax_firmware *cur)
Definition: firmware.c:196
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len)
Definition: md5.c:72
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
void iax_firmware_unload(void)
Definition: firmware.c:250
struct iax_firmware::@155 list
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
unsigned char devname[16]
Definition: iax2.h:296
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define IAX_IE_FWBLOCKDESC
Definition: iax2.h:165
static char version[AST_MAX_EXTENSION]
Definition: chan_ooh323.c:391
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
int iax_firmware_append(struct iax_ie_data *ied, const char *dev, unsigned int desc)
Definition: firmware.c:283
Definition: md5.h:26
void iax_firmware_traverse(const char *filter, int(*callback)(struct ast_iax2_firmware_header *header, void *data), void *data)
Definition: firmware.c:321
unsigned char chksum[16]
Definition: iax2.h:298
#define IAX_IE_FWBLOCKDATA
Definition: iax2.h:166
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
#define PATH_MAX
Definition: asterisk.h:40
static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:709
struct ast_iax2_firmware_header * fwh
Definition: firmware.c:54
MD5 digest functions.
unsigned char * buf
Definition: firmware.c:55
#define IAX_FIRMWARE_SUBDIR
Definition: firmware.c:47