static size_t strip_c0(const char *s, size_t len) { size_t c0 = memcspn(s, len, "\xc0\x00", 2); if (c0) c0--; return c0; }
/** * parse a single line of the TivoConnect protocol into a 'kkv' structure * "foo=bar\x0a" * "AAABCCCDDDD" */ static ptrdiff_t do_parse_kv(char *buf, size_t len, kkv *k) { const char *orig = buf; /* save original address */ size_t l = memcspn(buf, len, "=\x0a", 2); k->keystr.start = buf; k->keystr.len = l; strlower(k->keystr.start, k->keystr.len); k->key = str2key(&k->keystr); buf += l, len -= l; l = memspn(buf, len, "=", 1); buf += l, len -= l; k->val.start = buf; l = memcspn(buf, len, "\x0a", 1); k->val.len = l; buf += l, len -= l; l = memspn(buf, len, "\x0a", 1); buf += l, len -= l; return buf - orig; }
/** * consume a single token of the pattern "[^;]*;", write it's start and * length in 'p' and return the total number of bytes consumed */ static size_t do_parse_token(char *buf, size_t len, ptrlen *p) { size_t bytes; p->start = buf; bytes = memcspn(buf, len, "=\x0a\x00", 3); p->len = bytes; if (bytes < len) bytes++; return bytes; }
// FIXME: "GET /common/jquery.autocomplete.js HTTP/1.1\x0d\x0a" -> HTTP req meth=HTT uri=/1.1 304 Not Modified static ptrdiff_t do_parse_req(char *buf, size_t len, http_req *r) { const char *orig = buf; size_t l = memcspn(buf, len, " \r\n", 3); if (l > 0) { /* method */ r->meth.start = buf; r->meth.len = l; buf += l, len -= l; /* skip whitespace */ l = memspn(buf, len, " ", 1); buf += l, len -= l; /* uri */ l = memcspn(buf, len, " \r\n", 3); r->uri.start = buf; r->uri.len = l; buf += l, len -= l; /* skip whitespace */ l = memspn(buf, len, " ", 1); buf += l, len -= l; /* version */ l = memcspn(buf, len, "\r\n", 2); r->ver.start = buf; r->ver.len = l; printf("HTTP meth=<%.*s> uri=<%.*s> ver=<%.*s> (%u)\n", (int)r->meth.len, r->meth.start, (int)r->uri.len, r->uri.start, (int)r->ver.len, r->ver.start, r->ver.len); buf += l, len -= l; /* skip newline */ /* NOTE: we must skip only a single set of "\r\n" */ if (len >= 2 && '\r' == buf[0] && '\n' == buf[1]) buf += 2, len -= 2; } #if 1 /* debug */ printf("%s ver=<%.*s> buf=<%.*s>\n", __func__, (int)r->ver.len, r->ver.start, 20, buf); printf("%s -> %u\n", __func__, (unsigned)(buf-orig)); #endif return buf - orig; }
/** * 'buf' points at first char in headers, parse them into 'head' and * return bytes consumed * @note this function is used by other protocols, such as SSDP */ size_t http_parse_headers(char *buf, size_t len, http_headers *head) { struct head_kv *kv = head->h; const char *orig = buf, *end = buf + len; size_t skip = 2; head->cnt = 0; /* FIXME: make sure we don't go further than 'len' */ #if 0 printf("%s len=%u buf=%.*s\n", __func__, (unsigned)len, (unsigned)len, buf); #endif while ( buf < end && 2 == skip && head->cnt < sizeof head->h / sizeof head->h[0] ) { /* "<A><: ><B><\r\n>" */ ptrlen_list *pl = &kv->val; ptrlen *v = pl->p; pl->cnt = 0; kv->key.start = buf; kv->key.len = memcspn(buf, len, ": \r\n", 4); buf += kv->key.len; len -= kv->key.len; skip = memspn(buf, len, ": ", 2); buf += skip; len -= skip; v->start = buf; v->len = memcspn(buf, len, "\r\n", 2); pl->cnt++; buf += v->len; len -= v->len; skip = memspn(buf, len, "\r\n", 2); buf += skip; len -= skip; head->cnt++; v++; kv++; } return (size_t)(buf - orig); }
/** * test 'buf' for the beginnings of HTTP-based GNUTELLA protocol content */ static int test_header(const char *buf, size_t len, const parse_status *st) { char c[32]; /* temp sscanf buffers */ unsigned i; return len >= 32 /* minimum possible valid length */ && 'G' == buf[0] /* cheap initial test */ && memcspn(buf, len, "\r\n", 2) < len /* contains a newline somewhere */ && ( /* validate header line */ 3 == sscanf(buf, "GNUTELLA %31[^ /]/%u.%u\r\n", c, &i, &i) || 4 == sscanf(buf, "GNUTELLA/%u.%u %u %31[^\r\n]\r\n", &i, &i, &i, c) ); }
static int test_smb(const char *buf, size_t len, const parse_status *st) { const smb_hdr *h = (smb_hdr *)st->frame[st->frames-1].off; const smb_trans_req *r; size_t namelen; assert(PROT_SMB == st->frame[st->frames-1].id); if (st->frame[st->frames-1].len < sizeof *h) return 0; if (SMB_Cmd_TransReq != h->cmd) return 0; r = (smb_trans_req *)((char *)h + sizeof *h); namelen = memcspn((char *)r->name, len - sizeof *h + sizeof *r - 1, "\x00", 1); return (16 == namelen && 0 == memcmp(r->name, "\\MAILSLOT\\BROWSE", namelen)); }
static size_t parse_domwg(const browse *b, char *buf, size_t len, const parse_status *st) { const struct dom_wg *d = (struct dom_wg *)buf; size_t bytes, namelen; if (len < sizeof *d) return 0; namelen = memcspn((char *)d->master_name, len - sizeof *d, "\x00", 1); bytes = sizeof *d + namelen; printf("%s %s (bytes=%u):", __FILE__, __func__, (unsigned)bytes); dump_chars((char *)d, bytes, stdout); fputc('\n', stdout); return bytes; }
/** * parse host comment at the end; * NOTE: this *should* go until the end of the packet; however some instances * (i.e. BROWSER over LLC) pad the message with NUL bytes. */ static size_t parse_host(const browse *b, char *buf, size_t len, const parse_status *st) { const struct browse_hostann *h = (struct browse_hostann *)buf; size_t bytes, commentlen; if (len < sizeof *h) return 0; commentlen = memcspn((char *)h->host_comment, len - sizeof *h, "\x00", 1); bytes = sizeof *h + commentlen; if (bytes > len) bytes = len; printf("%s %s (bytes=%u):", __FILE__, __func__, (unsigned)bytes); dump_chars((char *)h, bytes, stdout); fputc('\n', stdout); return bytes; }
size_t dns_calc_len_qd(const char *buf, size_t len) { const char *name = (const char *)buf; size_t l = memcspn(name, len, "\x00", 1), bytes = l; if (l < len) { const dns_query *q = (dns_query *)(buf + l + 1); bytes += 1 + sizeof *q; } #if 0 assert(bytes <= len); #endif if (bytes > len) bytes = len; return bytes; }
void remove_from_htable(struct hashtable *ht, char *words, size_t sz){ uint8_t *ptr = (uint8_t*)words; uint32_t word_len; long i = 0; while(i < sz){ word_len = memcspn(ptr, sz-i, (uint8_t*)" \n\t", 3); //hashtable_add(ht, ptr, word_len, (void*)i); hashtable_remove(ht, ptr, word_len); i += word_len; ptr += word_len; while(isspace(*ptr)){ ptr++; i++; } } return; }
static size_t parse_elect(const browse *b, char *buf, size_t len, const parse_status *st) { struct electreq *e = (struct electreq *)buf; size_t bytes, namelen; if (len < sizeof *e) return 0; namelen = memcspn((char *)e->name, len - sizeof *e, "\x00", 1); bytes = sizeof *e + namelen; printf("%s %s (bytes=%u):", __FILE__, __func__, (unsigned)bytes); dump_chars((char *)e, bytes, stdout); fputc('\n', stdout); /* fix endianness */ e->uptime = ltohl(e->uptime); assert(bytes == len); return bytes; }
/** * calculate the length, in bytes, of the next layer after the header */ static size_t do_calc_len(const char *buf, size_t len, const smb_hdr *h) { size_t bytes = 0; switch ((enum SMB_Cmd)h->cmd) { case SMB_Cmd_TransReq: { /* TODO: break out into own function */ smb_trans_req *r = (smb_trans_req *)buf; if (sizeof *r > len) return len; /* calculate length of name */ bytes = memcspn((char *)r->name, len - sizeof *r - 1, "\x00", 1); bytes += sizeof *r; /* includes trailing \0 */ } break; default: break; } return bytes; }
static size_t dump_qd(const char *buf, size_t len, FILE *out) { char namebuf[1024]; const char *name = (const char *)buf; size_t namel = memcspn(name, len, "\x00", 1); int bytes = 0; const dns_query *q = (dns_query *)(buf + namel + 1); (void)dump_chars_buf(namebuf, sizeof namebuf, buf, namel); #if 0 printf("len=%u name[namel=%u]=%s name[10]=", (unsigned)len, (unsigned)namel, namebuf); dump_chars(name, 10, stdout); fputc('\n', stdout); #endif bytes = fprintf(out, " qd name=%s type=%hu(%s) class=%hu(%s)\n", namebuf, ntohs(q->type), type2str(ntohs(q->type)), ntohs(q->class_), class2str(ntohs(q->class_))); return bytes; }
/** * @return number of octets used by this protocol, or zero upon error */ size_t http_parse(char *buf, size_t len, parse_frame *f, const parse_status *st, http *h) { size_t olen = len; const char *start = buf, *end = buf + len; if (test_resphead(buf, len, st)) { http_resp *r = &h->data.resp; h->type = HTTP_Type_RESP; /* TODO: break out to separate function a la do_parse_req() */ /* something like "HTTP/1.1 301 Moved Permanently" */ r->ver.start = buf; r->ver.len = memcspn(r->ver.start, len, " ", 1); r->code.start = r->ver.start + r->ver.len + strspn(r->ver.start + r->ver.len, " "); r->code.len = strcspn(r->code.start, " "); r->desc.start = r->code.start + r->code.len + strspn(r->code.start + r->code.len, " "); r->desc.len = strcspn(r->desc.start, "\r\n"); buf = r->desc.start + r->desc.len + strspn(r->desc.start + r->desc.len, "\r\n"); buf += http_parse_headers(buf, len - (buf - start), &r->headers); r->contents.start = buf; r->contents.len = len-(buf-start); } else if (do_test_http_header(buf, len)) { /* something like "GET / HTTP/1.1" */ size_t consumed; h->type = HTTP_Type_REQ; consumed = do_parse_req(buf, len, &h->data.req); if (consumed != 0) { buf += consumed, len -= consumed; consumed = http_parse_headers(buf, len, &h->data.req.headers); buf += consumed, len -= consumed; h->data.req.contents.start = buf; h->data.req.contents.len = len; do_req_rep(&h->data.req, st); } } else { h->type = HTTP_Type_DATA; buf += len; } f->pass = h; printf("%s -> %u\n", __func__, (unsigned)(buf-start)); return olen; }
/** * SMB header has been parsed, parse rest of msg * @return number of bytes consumed */ static size_t do_parse(char *buf, size_t len, const smb_hdr *h) { size_t bytes = 0; printf("do_parse len=%u buf=", (unsigned)len); dump_chars(buf, len, stdout); fputc('\n', stdout); switch ((enum SMB_Cmd)h->cmd) { case SMB_Cmd_TransReq: { /* TODO: break out into own function */ size_t namelen; smb_trans_req *r = (smb_trans_req *)buf; if (sizeof *r > len) return 0; /* convert endianness */ r->totalparam = ltohs(r->totalparam); r->totaldata = ltohs(r->totaldata); r->maxparam = ltohs(r->maxparam); r->maxdata = ltohs(r->maxdata); r->param = ltohs(r->param); r->paramoffset = ltohs(r->paramoffset); r->data = ltohs(r->data); r->dataoffset = ltohs(r->dataoffset); r->mailslot.opcode = ltohs(r->mailslot.opcode); r->mailslot.priority= ltohs(r->mailslot.priority); r->mailslot.class_ = ltohs(r->mailslot.class_); r->mailslot.bytes = ltohs(r->mailslot.bytes); /* calculate length of name */ namelen = memcspn((char *)r->name, len - sizeof *r - 1, "\x00", 1); bytes = namelen + sizeof *r; /* includes trailing \0 */ } break; default: break; } return bytes; }
int cURLCamera::Capture( Image &image ) { bool frameComplete = false; /* MODE_STREAM specific variables */ bool SubHeadersParsingComplete = false; unsigned int frame_content_length = 0; std::string frame_content_type; bool need_more_data = false; int nRet; /* Grab the mutex to ensure exclusive access to the shared data */ lock(); while ( !frameComplete ) { /* If the work thread did a reset, reset our local variables */ if ( bReset ) { SubHeadersParsingComplete = false; frame_content_length = 0; frame_content_type.clear(); need_more_data = false; bReset = false; } if ( mode == MODE_UNSET ) { /* Don't have a mode yet. Sleep while waiting for data */ nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); if ( nRet != 0 ) { Error("Failed waiting for available data condition variable: %s",strerror(nRet)); return -20; } } if ( mode == MODE_STREAM ) { /* Subheader parsing */ while( !SubHeadersParsingComplete && !need_more_data ) { size_t crlf_start, crlf_end, crlf_size; std::string subheader; /* Check if the buffer contains something */ if ( databuffer.empty() ) { /* Empty buffer, wait for data */ need_more_data = true; break; } /* Find crlf start */ crlf_start = memcspn(databuffer,"\r\n",databuffer.size()); if ( crlf_start == databuffer.size() ) { /* Not found, wait for more data */ need_more_data = true; break; } /* See if we have enough data for determining crlf length */ if ( databuffer.size() < crlf_start+5 ) { /* Need more data */ need_more_data = true; break; } /* Find crlf end and calculate crlf size */ crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5); crlf_size = (crlf_start + crlf_end) - crlf_start; /* Is this the end of a previous stream? (This is just before the boundary) */ if ( crlf_start == 0 ) { databuffer.consume(crlf_size); continue; } /* Check for invalid CRLF size */ if ( crlf_size > 4 ) { Error("Invalid CRLF length"); } /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */ if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) { /* This is the last header */ SubHeadersParsingComplete = true; } /* Copy the subheader, excluding the crlf */ subheader.assign(databuffer, crlf_start); /* Advance the buffer past this one */ databuffer.consume(crlf_start+crlf_size); Debug(7,"Got subheader: %s",subheader.c_str()); /* Find where the data in this header starts */ size_t subheader_data_start = subheader.rfind(' '); if ( subheader_data_start == std::string::npos ) { subheader_data_start = subheader.find(':'); } /* Extract the data into a string */ std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos); Debug(8,"Got subheader data: %s",subheader_data.c_str()); /* Check the header */ if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) { /* Found the content-length header */ frame_content_length = atoi(subheader_data.c_str()); Debug(6,"Got content-length subheader: %d",frame_content_length); } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { /* Found the content-type header */ frame_content_type = subheader_data; Debug(6,"Got content-type subheader: %s",frame_content_type.c_str()); } } /* Attempt to extract the frame */ if(!need_more_data) { if(!SubHeadersParsingComplete) { /* We haven't parsed all headers yet */ need_more_data = true; } else if ( ! frame_content_length ) { /* Invalid frame */ Error("Invalid frame: invalid content length"); } else if ( frame_content_type != "image/jpeg" ) { /* Unsupported frame type */ Error("Unsupported frame: %s",frame_content_type.c_str()); } else if(frame_content_length > databuffer.size()) { /* Incomplete frame, wait for more data */ need_more_data = true; } else { /* All good. decode the image */ image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); frameComplete = true; } } /* Attempt to get more data */ if(need_more_data) { nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); if(nRet != 0) { Error("Failed waiting for available data condition variable: %s",strerror(nRet)); return -18; } need_more_data = false; } } else if(mode == MODE_SINGLE) { /* Check if we have anything */ if (!single_offsets.empty()) { if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { /* Extract frame */ image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); single_offsets.pop_front(); frameComplete = true; } else { /* This shouldn't happen */ Error("Internal error. Attempting recovery"); databuffer.consume(single_offsets.front()); single_offsets.pop_front(); } } else { /* Don't have a frame yet, wait for the request complete condition variable */ nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex); if(nRet != 0) { Error("Failed waiting for request complete condition variable: %s",strerror(nRet)); return -19; } } } else { /* Failed to match content-type */ Fatal("Unable to match Content-Type. Check URL, username and password"); } /* mode */ } /* frameComplete loop */ /* Release the mutex */ unlock(); if(!frameComplete) return -1; return 1; }