1/* libasf - An Advanced Systems Format media file parser 2 * Copyright (C) 2006-2010 Juho Vähä-Herttua 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19#include <stdlib.h> 20#include <string.h> 21 22#include "asf.h" 23#include "asfint.h" 24#include "byteio.h" 25#include "header.h" 26#include "guid.h" 27#include "debug.h" 28 29/** 30 * Read next object from buffer pointed by data. Notice that 31 * no buffer overflow checks are done! This function always 32 * expects to have 24 bytes available, which is the size of 33 * the object header (GUID + data size) 34 */ 35static void 36asf_parse_read_object(asfint_object_t *obj, uint8_t *data) 37{ 38 GetGUID(data, &obj->guid); 39 obj->type = asf_guid_get_type(&obj->guid); 40 obj->size = GetQWLE(data + 16); 41 obj->full_data = data; 42 obj->datalen = 0; 43 obj->data = NULL; 44 obj->next = NULL; 45 46 if (obj->type == GUID_UNKNOWN) { 47 debug_printf("unknown object: %x-%x-%x-%02x%02x%02x%02x%02x%02x%02x%02x, %ld bytes", 48 obj->guid.v1, obj->guid.v2, obj->guid.v3, obj->guid.v4[0], 49 obj->guid.v4[1], obj->guid.v4[2], obj->guid.v4[3], obj->guid.v4[4], 50 obj->guid.v4[5], obj->guid.v4[6], obj->guid.v4[7], (long) obj->size); 51 } 52} 53 54/** 55 * Parse header extension object. Takes a pointer to a newly allocated 56 * header extension structure, a pointer to the data buffer and the 57 * length of the data buffer as its parameters. Subobject contents are 58 * not parsed, but they are added as a linked list to the header object. 59 */ 60static int 61asf_parse_headerext(asf_object_headerext_t *header, uint8_t *buf, uint64_t buflen) 62{ 63 int64_t datalen; 64 uint8_t *data; 65 66 if (header->size < 46) { 67 /* invalide size for headerext */ 68 return ASF_ERROR_INVALID_OBJECT_SIZE; 69 } 70 71 /* Read reserved and datalen fields from the buffer */ 72 GetGUID(buf + 24, &header->reserved1); 73 header->reserved2 = GetWLE(buf + 40); 74 header->datalen = GetDWLE(buf + 42); 75 76 if (header->datalen != header->size - 46) { 77 /* invalid header extension data length value */ 78 return ASF_ERROR_INVALID_LENGTH; 79 } 80 header->data = buf + 46; 81 82 debug_printf("parsing header extension subobjects"); 83 84 datalen = header->datalen; 85 data = header->data; 86 while (datalen > 0) { 87 asfint_object_t *current; 88 89 if (datalen < 24) { 90 /* not enough data for reading a new object */ 91 break; 92 } 93 94 /* Allocate a new subobject */ 95 current = malloc(sizeof(asfint_object_t)); 96 if (!current) { 97 return ASF_ERROR_OUTOFMEM; 98 } 99 100 asf_parse_read_object(current, data); 101 if (current->size > datalen || current->size < 24) { 102 /* invalid object size */ 103 break; 104 } 105 current->datalen = current->size - 24; 106 current->data = data + 24; 107 108 /* add to the list of subobjects */ 109 if (!header->first) { 110 header->first = current; 111 header->last = current; 112 } else { 113 header->last->next = current; 114 header->last = current; 115 } 116 117 data += current->size; 118 datalen -= current->size; 119 } 120 121 if (datalen != 0) { 122 /* not enough data for reading the whole object */ 123 return ASF_ERROR_INVALID_LENGTH; 124 } 125 126 debug_printf("header extension subobjects parsed successfully"); 127 128 return header->size; 129} 130 131/** 132 * Takes an initialized asf_file_t structure file as a parameter. Allocates 133 * a new asf_object_header_t in file->header and uses the file->iostream to 134 * read all fields and subobjects into it. Finally calls the 135 * asf_parse_header_validate function to validate the values and parse the 136 * commonly used values into the asf_file_t struct itself. 137 */ 138int 139asf_parse_header(asf_file_t *file) 140{ 141 asf_object_header_t *header; 142 asf_iostream_t *iostream; 143 uint8_t hdata[30]; 144 int tmp; 145 146 file->header = NULL; 147 iostream = &file->iostream; 148 149 /* object minimum is 24 bytes and header needs to have 150 * the subobject count field and two reserved fields */ 151 tmp = asf_byteio_read(iostream, hdata, 30); 152 if (tmp < 0) { 153 /* not enough data to read the header object */ 154 return tmp; 155 } 156 157 file->header = malloc(sizeof(asf_object_header_t)); 158 header = file->header; 159 if (!header) { 160 return ASF_ERROR_OUTOFMEM; 161 } 162 163 /* read the object and check its size value */ 164 asf_parse_read_object((asfint_object_t *) header, hdata); 165 if (header->size < 30) { 166 /* invalid size for header object */ 167 return ASF_ERROR_INVALID_OBJECT_SIZE; 168 } 169 170 /* read header object specific compulsory fields */ 171 header->subobjects = GetDWLE(hdata + 24); 172 header->reserved1 = hdata[28]; 173 header->reserved2 = hdata[29]; 174 175 /* clear header extension object and subobject list */ 176 header->ext = NULL; 177 header->first = NULL; 178 header->last = NULL; 179 180 /* the header data needs to be allocated for reading */ 181 header->datalen = header->size - 30; 182 header->data = malloc(header->datalen * sizeof(uint8_t)); 183 if (!header->data) { 184 return ASF_ERROR_OUTOFMEM; 185 } 186 187 tmp = asf_byteio_read(iostream, header->data, header->datalen); 188 if (tmp < 0) { 189 return tmp; 190 } 191 192 if (header->subobjects > 0) { 193 uint64_t datalen; 194 uint8_t *data; 195 int i; 196 197 debug_printf("starting to read subobjects"); 198 199 /* use temporary variables for use during the read */ 200 datalen = header->datalen; 201 data = header->data; 202 for (i=0; i<header->subobjects; i++) { 203 asfint_object_t *current; 204 205 if (datalen < 24) { 206 /* not enough data for reading object */ 207 break; 208 } 209 210 current = malloc(sizeof(asfint_object_t)); 211 if (!current) { 212 return ASF_ERROR_OUTOFMEM; 213 } 214 215 asf_parse_read_object(current, data); 216 if (current->size > datalen || current->size < 24) { 217 /* invalid object size */ 218 break; 219 } 220 221 /* Check if the current subobject is a header extension 222 * object or just a normal subobject */ 223 if (current->type == GUID_HEADER_EXTENSION && !header->ext) { 224 int ret; 225 asf_object_headerext_t *headerext; 226 227 /* we handle header extension separately because it has 228 * some subobjects as well */ 229 current = realloc(current, sizeof(asf_object_headerext_t)); 230 headerext = (asf_object_headerext_t *) current; 231 headerext->first = NULL; 232 headerext->last = NULL; 233 ret = asf_parse_headerext(headerext, data, datalen); 234 235 if (ret < 0) { 236 /* error parsing header extension */ 237 return ret; 238 } 239 240 header->ext = headerext; 241 } else { 242 if (current->type == GUID_HEADER_EXTENSION) { 243 debug_printf("WARNING! Second header extension object found, ignoring it!"); 244 } 245 246 current->datalen = current->size - 24; 247 current->data = data + 24; 248 249 /* add to list of subobjects */ 250 if (!header->first) { 251 header->first = current; 252 header->last = current; 253 } else { 254 header->last->next = current; 255 header->last = current; 256 } 257 } 258 259 data += current->size; 260 datalen -= current->size; 261 } 262 263 if (i != header->subobjects || datalen != 0) { 264 /* header data size doesn't match given subobject count */ 265 return ASF_ERROR_INVALID_VALUE; 266 } 267 268 debug_printf("%d subobjects read successfully", i); 269 } 270 271 tmp = asf_parse_header_validate(file, file->header); 272 if (tmp < 0) { 273 /* header read ok but doesn't validate correctly */ 274 return tmp; 275 } 276 277 debug_printf("header validated correctly"); 278 279 return header->size; 280} 281 282/** 283 * Takes an initialized asf_file_t structure file as a parameter. Allocates 284 * a new asf_object_data_t in file->data and uses the file->iostream to 285 * read all its compulsory fields into it. Notice that the actual data is 286 * not read in any way, because we need to be able to work with non-seekable 287 * streams as well. 288 */ 289int 290asf_parse_data(asf_file_t *file) 291{ 292 asf_object_data_t *data; 293 asf_iostream_t *iostream; 294 uint8_t ddata[50]; 295 int tmp; 296 297 file->data = NULL; 298 iostream = &file->iostream; 299 300 /* object minimum is 24 bytes and data object needs to have 301 * 26 additional bytes for its internal fields */ 302 tmp = asf_byteio_read(iostream, ddata, 50); 303 if (tmp < 0) { 304 return tmp; 305 } 306 307 file->data = malloc(sizeof(asf_object_data_t)); 308 data = file->data; 309 if (!data) { 310 return ASF_ERROR_OUTOFMEM; 311 } 312 313 /* read the object and check its size value */ 314 asf_parse_read_object((asfint_object_t *) data, ddata); 315 if (data->size < 50) { 316 /* invalid size for data object */ 317 return ASF_ERROR_INVALID_OBJECT_SIZE; 318 } 319 320 /* read data object specific compulsory fields */ 321 GetGUID(ddata + 24, &data->file_id); 322 data->total_data_packets = GetQWLE(ddata + 40); 323 data->reserved = GetWLE(ddata + 48); 324 data->packets_position = file->position + 50; 325 326 /* If the file_id GUID in data object doesn't match the 327 * file_id GUID in headers, the file is corrupted */ 328 if (!asf_guid_equals(&data->file_id, &file->file_id)) { 329 return ASF_ERROR_INVALID_VALUE; 330 } 331 332 /* if data->total_data_packets is non-zero (not a stream) and 333 the data packets count doesn't match, return error */ 334 if (data->total_data_packets && 335 data->total_data_packets != file->data_packets_count) { 336 return ASF_ERROR_INVALID_VALUE; 337 } 338 339 return 50; 340} 341 342/** 343 * Takes an initialized asf_file_t structure file as a parameter. Allocates 344 * a new asf_object_index_t in file->index and uses the file->iostream to 345 * read all its compulsory fields into it. Notice that the actual data is 346 * not read in any way, because we need to be able to work with non-seekable 347 * streams as well. 348 */ 349int 350asf_parse_index(asf_file_t *file) 351{ 352 asf_object_index_t *index; 353 asf_iostream_t *iostream; 354 uint8_t idata[56]; 355 uint64_t entry_data_size; 356 uint8_t *entry_data = NULL; 357 int tmp, i; 358 359 file->index = NULL; 360 iostream = &file->iostream; 361 362 /* read the raw data of an index header */ 363 tmp = asf_byteio_read(iostream, idata, 56); 364 if (tmp < 0) { 365 printf("Could not read index header\n"); 366 return tmp; 367 } 368 369 /* allocate the index object */ 370 index = malloc(sizeof(asf_object_index_t)); 371 if (!index) { 372 return ASF_ERROR_OUTOFMEM; 373 } 374 375 asf_parse_read_object((asfint_object_t *) index, idata); 376 if (index->type != GUID_INDEX) { 377 tmp = index->size; 378 free(index); 379 380 /* The guid type was wrong, just return the bytes to skip */ 381 return tmp; 382 } 383 384 if (index->size < 56) { 385 /* invalid size for index object */ 386 free(index); 387 return ASF_ERROR_INVALID_OBJECT_SIZE; 388 } 389 390 GetGUID(idata + 24, &index->file_id); 391 index->entry_time_interval = GetQWLE(idata + 40); 392 index->max_packet_count = GetDWLE(idata + 48); 393 index->entry_count = GetDWLE(idata + 52); 394 395 printf("INDEX\n"); 396 printf("Total Index Entries %d\n",index->entry_count); 397 printf("Index Size in bytes %Ld\n",index->size); 398 printf("Index Max Packet Count %d\n",index->max_packet_count); 399 printf("Index Entry Time Interval %Ld\n",index->entry_time_interval); 400 401 if (index->entry_count == 0) { 402 printf("Index has no entries\n"); 403 file->index = index; 404 return index->size; 405 } 406 407 if (index->entry_count * 6 + 56 > index->size) { 408 free(index); 409 return ASF_ERROR_INVALID_LENGTH; 410 } 411 412 entry_data_size = index->entry_count * 6; 413 entry_data = malloc(entry_data_size * sizeof(uint8_t)); 414 if (!entry_data) { 415 free(index); 416 return ASF_ERROR_OUTOFMEM; 417 } 418 tmp = asf_byteio_read(iostream, entry_data, entry_data_size); 419 if (tmp < 0) { 420 printf("Could not read entry data\n"); 421 free(index); 422 free(entry_data); 423 return tmp; 424 } 425 426 index->entries = malloc(index->entry_count * sizeof(asf_index_entry_t)); 427 if (!index->entries) { 428 free(index); 429 free(entry_data); 430 return ASF_ERROR_OUTOFMEM; 431 } 432 433 for (i=0; i<index->entry_count; i++) { 434 index->entries[i].packet_index = GetDWLE(entry_data + i*6); 435 index->entries[i].packet_count = GetWLE(entry_data + i*6 + 4); 436 } 437 438 free(entry_data); 439 file->index = index; 440 441 return index->size; 442} 443