/* * Off-the-Record Messaging library * Copyright (C) 2004-2009 Ian Goldberg, Chris Alexander, Willy Lew, * Nikita Borisov * * * This library is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General * Public License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include "tlv.h" /* Make a single TLV, copying the supplied data */ OtrlTLV *otrl_tlv_new(unsigned short type, unsigned short len, const unsigned char *data) { OtrlTLV *tlv = malloc(sizeof(OtrlTLV)); assert(tlv != NULL); tlv->type = type; tlv->len = len; tlv->data = malloc(len + 1); assert(tlv->data != NULL); memmove(tlv->data, data, len); tlv->data[tlv->len] = '\0'; tlv->next = NULL; return tlv; } /* Construct a chain of TLVs from the given data */ OtrlTLV *otrl_tlv_parse(const unsigned char *serialized, size_t seriallen) { OtrlTLV *tlv = NULL; OtrlTLV **tlvp = &tlv; while (seriallen >= 4) { unsigned short type = (serialized[0] << 8) + serialized[1]; unsigned short len = (serialized[2] << 8) + serialized[3]; serialized += 4; seriallen -=4; if (seriallen < len) break; *tlvp = otrl_tlv_new(type, len, serialized); serialized += len; seriallen -= len; tlvp = &((*tlvp)->next); } return tlv; } /* Deallocate a chain of TLVs */ void otrl_tlv_free(OtrlTLV *tlv) { while(tlv) { OtrlTLV *next = tlv->next; free(tlv->data); free(tlv); tlv = next; } } /* Find the serialized length of a chain of TLVs */ size_t otrl_tlv_seriallen(const OtrlTLV *tlv) { size_t totlen = 0; while (tlv) { totlen += tlv->len + 4; tlv = tlv->next; } return totlen; } /* Serialize a chain of TLVs. The supplied buffer must already be large * enough. */ void otrl_tlv_serialize(unsigned char *buf, const OtrlTLV *tlv) { while (tlv) { buf[0] = (tlv->type >> 8) & 0xff; buf[1] = tlv->type & 0xff; buf[2] = (tlv->len >> 8) & 0xff; buf[3] = tlv->len & 0xff; buf += 4; memmove(buf, tlv->data, tlv->len); buf += tlv->len; tlv = tlv->next; } } /* Return the first TLV with the given type in the chain, or NULL if one * isn't found. (The tlvs argument isn't const because the return type * needs to be non-const.) */ OtrlTLV *otrl_tlv_find(OtrlTLV *tlvs, unsigned short type) { while (tlvs) { if (tlvs->type == type) return tlvs; tlvs = tlvs->next; } return NULL; } OtrlTLV * otrl_tlv_ft_data(const char * data, unsigned char id, size_t length, size_t offset) { if(offset >= length) return NULL; unsigned char tmp[USHRT_MAX]; tmp[0] = id; if(length - offset > USHRT_MAX - 1) { memcpy(tmp + 1, data + offset, USHRT_MAX - 1); return otrl_tlv_new(OTRL_TLV_INB_FT_DATA, USHRT_MAX, tmp); } else { memcpy(tmp + 1, data + offset, length - offset - 1); return otrl_tlv_new(OTRL_TLV_INB_FT_DATA, length - offset, tmp); } } // construct special TLV for given id OtrlTLV * otrl_tlv_ft_rack(unsigned char id, unsigned short type) { if(OTRL_TLV_INB_FT_ACK == type || OTRL_TLV_INB_FT_RST == type) { unsigned char tmp[1]; tmp[0] = id; if(OTRL_TLV_INB_FT_ACK == type) return otrl_tlv_new(OTRL_TLV_INB_FT_ACK, 1, tmp); return otrl_tlv_new(OTRL_TLV_INB_FT_RST, 1, tmp); } return NULL; } // return TLVs corresponding to given filename or NULL on errors OtrlTLV * otrl_tlv_ft_init(const char * filename, unsigned char id) { //get file length - we'll need it anyway int fd = open(filename, O_RDONLY); struct stat statbuf; if(-1 == fd) { fprintf(stderr, "failed to open file %s", filename); return NULL; } if(fstat(fd, &statbuf) < 0) { fprintf(stderr, "failed to stat file %s\n", filename); return NULL; } char * fmap = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); OtrlTLV * t; // we're expected to supply file's metadata assert(sizeof(unsigned short) == 2); assert(sizeof(size_t) >= 4); // make sure we'll have no integer overflow // additional bytes are for '\0' and ft_id unsigned short fnamelen = strnlen(rindex(filename, '/') + 1, 65526); unsigned short len = 4 + fnamelen; fprintf(stderr, "initial size %zd bytes\n", statbuf.st_size); if(statbuf.st_size < USHRT_MAX - len) {// file small enough to fit into single TLV // FIXME: avoid code duplication here unsigned char data[statbuf.st_size + len + 10]; data[0] = id; data[1] = ((statbuf.st_size) >> 56) & 0xff; data[2] = ((statbuf.st_size) >> 48) & 0xff; data[3] = ((statbuf.st_size) >> 40) & 0xff; data[4] = ((statbuf.st_size) >> 32) & 0xff; data[5] = ((statbuf.st_size) >> 24) & 0xff; data[6] = ((statbuf.st_size) >> 16) & 0xff; data[7] = ((statbuf.st_size) >> 8) & 0xff; data[8] = (statbuf.st_size) & 0xff; data[9] = ((fnamelen) >> 8) & 0xff; data[10] = (fnamelen) & 0xff; fprintf(stderr, "FULL {%zd, %zd, %zd} debug run: %x %x %x %x %x %x %x %x -- %x %x\n", sizeof(statbuf.st_size), sizeof(size_t), sizeof(ssize_t), data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]); fprintf(stderr, "FULL shift: %zd %zd %zd %zd %zd %zd %zd %zd -- %zd %zd %zd %zd %zd %zd %zd %zd\n", ((statbuf.st_size) >> 56), ((statbuf.st_size) >> 48), ((statbuf.st_size) >> 40),((statbuf.st_size) >> 32),((statbuf.st_size) >> 24),((statbuf.st_size) >> 16),((statbuf.st_size) >> 8), statbuf.st_size, ((statbuf.st_size) >> 56) & 0xff, ((statbuf.st_size) >> 48) & 0xff, ((statbuf.st_size) >> 40) & 0xff, ((statbuf.st_size) >> 32) & 0xff, ((statbuf.st_size) >> 24) & 0xff, ((statbuf.st_size) >> 16) & 0xff, ((statbuf.st_size) >> 8) & 0xff, statbuf.st_size & 0xff); memcpy(data + 11, rindex(filename, '/') + 1, fnamelen); memcpy(data + 11 + fnamelen, fmap, statbuf.st_size); t = otrl_tlv_new(OTRL_TLV_INB_FT_FULL, statbuf.st_size + len + 10, data); } else { unsigned char data[len + 10]; data[0] = id; if(sizeof(size_t) == 4) {// FIXME: evil hack to ensure interoperability i686 <-> x86_64 data[1] = 0; data[2] = 0; data[3] = 0; data[4] = 0; } else { data[1] = (((size_t)statbuf.st_size) >> 56) & 0xff; data[2] = (((size_t)statbuf.st_size) >> 48) & 0xff; data[3] = (((size_t)statbuf.st_size) >> 40) & 0xff; data[4] = (((size_t)statbuf.st_size) >> 32) & 0xff; } data[5] = ((statbuf.st_size) >> 24) & 0xff; data[6] = ((statbuf.st_size) >> 16) & 0xff; data[7] = ((statbuf.st_size) >> 8) & 0xff; data[8] = (statbuf.st_size) & 0xff; data[9] = ((fnamelen) >> 8) & 0xff; data[10] = (fnamelen) & 0xff; fprintf(stderr, "INIT {%zd, %zd, %zd} debug run: %x %x %x %x %x %x %x %x -- %x %x\n", sizeof(statbuf.st_size), sizeof(size_t), sizeof(ssize_t), data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10]); fprintf(stderr, "FULL shift: %zx %zx %zx %zx %zx %zx %zx %zx -- %zx %zx %zx %zx %zx %zx %zx %zx\n", ((statbuf.st_size) >> 56), ((statbuf.st_size) >> 48), ((statbuf.st_size) >> 40),((statbuf.st_size) >> 32),((statbuf.st_size) >> 24),((statbuf.st_size) >> 16),((statbuf.st_size) >> 8), statbuf.st_size, ((statbuf.st_size) >> 56) & 0xff, ((statbuf.st_size) >> 48) & 0xff, ((statbuf.st_size) >> 40) & 0xff, ((statbuf.st_size) >> 32) & 0xff, ((statbuf.st_size) >> 24) & 0xff, ((statbuf.st_size) >> 16) & 0xff, ((statbuf.st_size) >> 8) & 0xff, statbuf.st_size & 0xff); memcpy(data + 11, rindex(filename, '/') + 1, fnamelen); t = otrl_tlv_new(OTRL_TLV_INB_FT_INIT, len + 10, data); } close(fd); munmap(fmap, statbuf.st_size); return t; } // return size for DATA, FULL and INIT messages // return 0 otherwise size_t otrl_tlv_ft_get_size(const OtrlTLV *tlv) { if(NULL == tlv) return 0; struct stat statbuf; fprintf(stderr, "statbuf structure filed size is %zd\n", sizeof(statbuf.st_size)); if(tlv->type == OTRL_TLV_INB_FT_INIT || tlv->type == OTRL_TLV_INB_FT_FULL) { assert(sizeof(size_t) >= 4); fprintf(stderr, "returning size for FULL or INIT...\n"); fprintf(stderr, "{%zd, %zd} debug run: %x %x %x %x %x %x %x %x\n", sizeof(size_t), sizeof(ssize_t), tlv->data[1], tlv->data[2], tlv->data[3], tlv->data[4], tlv->data[5], tlv->data[6], tlv->data[7], tlv->data[8]); return ((size_t)tlv->data[1] << 56) | ((size_t)tlv->data[2] << 48) | ((size_t)tlv->data[3] << 40) | ((size_t)tlv->data[4] << 32) | (tlv->data[5] << 24) | (tlv->data[6] << 16) | (tlv->data[7] << 8) | tlv->data[8]; } if(tlv->type == OTRL_TLV_INB_FT_DATA) { fprintf(stderr, "returning size for DATA... %x %x\n", tlv->data[1], tlv->data[2]); return (tlv->data[2] << 8) | tlv->data[1]; } return 0; } // return filename for INIT message or NULL char * otrl_tlv_ft_get_name(const OtrlTLV *tlv) { if(NULL == tlv) return NULL; if(tlv->type != OTRL_TLV_INB_FT_INIT && tlv->type != OTRL_TLV_INB_FT_FULL) return NULL; assert(sizeof(unsigned short) == 2); size_t len = tlv->data[9] << 8 | tlv->data[10]; fprintf(stderr, "filenamelen = %zd\n", len); char tmp[len + 1]; tmp[len] = '\0'; return strdup(memcpy(tmp, tlv->data + 11, len)); } // caller have to take care of freeing memory after use char * otrl_tlv_ft_get_data(const OtrlTLV * tlv) { if(NULL == tlv) return NULL; if(tlv->type != OTRL_TLV_INB_FT_INIT && tlv->type != OTRL_TLV_INB_FT_FULL) return NULL; size_t namelen = tlv->data[9] << 8 | tlv->data[10]; size_t datalen = otrl_tlv_ft_get_size(tlv); fprintf(stderr, "filenamelen = %zd datalen = %zd\n", namelen, datalen); char * data = malloc(datalen); if(NULL == data) return NULL; memcpy(data, tlv->data + 11 + namelen, datalen); return data; } // extract ft_id from TLV unsigned char otrl_tlv_ft_get_id(const OtrlTLV *tlv) { if(NULL == tlv) return 0; switch(tlv->type) { case OTRL_TLV_INB_FT_ERR: case OTRL_TLV_INB_FT_INIT: case OTRL_TLV_INB_FT_FULL: case OTRL_TLV_INB_FT_DATA: case OTRL_TLV_INB_FT_RST: case OTRL_TLV_INB_FT_ACK: return tlv->data[0]; default: return 0; } } inline void bindump(unsigned char * data, unsigned short data_len, size_t print_len) { size_t i; // FIXME: use standard min definition size_t min = (data_len > print_len) ? print_len : data_len; // for(i = 0; i < min; i++) if(isprint(data[i])) fprintf(stderr, "%c", data[i]); else fprintf(stderr, "."); for(i = 0; i < min; i++) fprintf(stderr, "%x", data[i]); if(i == print_len) fprintf(stderr, "[skipped]"); } // TLV pretty-printing facility for debugging purposes void otrl_tlv_print(const OtrlTLV *tlv) { if(NULL == tlv) return; switch(tlv->type) { case OTRL_TLV_PADDING: fprintf(stderr, "OTRL_TLV_PADDING::"); break; case OTRL_TLV_DISCONNECTED: fprintf(stderr, "OTRL_TLV_DISCONNECTED::"); break; case OTRL_TLV_SMP1: fprintf(stderr, "OTRL_TLV_SMP1::"); break; case OTRL_TLV_SMP2: fprintf(stderr, "OTRL_TLV_SMP2::"); break; case OTRL_TLV_SMP3: fprintf(stderr, "OTRL_TLV_SMP3::"); break; case OTRL_TLV_SMP4: fprintf(stderr, "OTRL_TLV_SMP4::"); break; case OTRL_TLV_SMP_ABORT: fprintf(stderr, "OTRL_TLV_SMP_ABORT::"); break; case OTRL_TLV_SMP1Q: fprintf(stderr, "OTRL_TLV_SMP1Q::"); break; case OTRL_TLV_SYMKEY: fprintf(stderr, "OTRL_TLV_SYMKEY::"); break; case OTRL_TLV_INB_FT_INIT: fprintf(stderr, "OTRL_TLV_INB_FT_INIT::"); break; case OTRL_TLV_INB_FT_FULL: fprintf(stderr, "OTRL_TLV_INB_FT_FULL::"); break; case OTRL_TLV_INB_FT_DATA: fprintf(stderr, "OTRL_TLV_INB_FT_DATA::"); break; case OTRL_TLV_INB_FT_ERR: fprintf(stderr, "OTRL_TLV_INB_FT_ERR::"); break; case OTRL_TLV_INB_FT_RST: fprintf(stderr, "OTRL_TLV_INB_FT_RST::"); break; case OTRL_TLV_INB_FT_ACK: fprintf(stderr, "OTRL_TLV_INB_FT_ACK::"); break; default: fprintf(stderr, "unknown TLV type - wtf is %d?!\n", tlv->type); } fprintf(stderr, "%d(%ld):#", tlv->len, sizeof(tlv->len)); bindump(tlv->data, tlv->len, 32); fprintf(stderr, "#\n"); }