Commit da7e54e4 authored by Olivier Dion's avatar Olivier Dion Committed by Adrien Béraud
Browse files

fuzzing/sipf-fmt: Add SIP format class and Flex lexer

Change-Id: Icecdfddd200932eb7f4f5fdd7e6a406472f85c18
parent c79a38c6
include $(top_srcdir)/globals.mk
if ENABLE_FUZZING
%.cpp: %.ll
flex --outfile $@ $^
AM_CXXFLAGS += -I$(top_srcdir)/src -I. -include common.h
AM_LDFLAGS += $(top_builddir)/src/libring.la
check_PROGRAMS =
lib_LTLIBRARIES = libfuzz.la
libfuzz_la_SOURCES = lib/utils.cpp lib/supervisor.cpp lib/gnutls.cpp lib/rand.cpp lib/syslog.cpp
libfuzz_la_SOURCES = lib/utils.cpp lib/supervisor.cpp lib/gnutls.cpp lib/rand.cpp lib/syslog.cpp lib/sip-fmt.cpp lib/sip-parser.cpp
endif
/*
* Copyright (C) 2021 Savoir-faire Linux Inc.
*
* Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cassert>
#include "lib/sip-fmt.h"
SIPFmt::SIPFmt(const std::vector<uint8_t>& data)
: isValid_(false)
{
parse(data);
}
void
SIPFmt::pushBody(char *bytes, size_t len)
{
for (size_t i=0; i<len; ++i) {
body_.emplace_back(bytes[i]);
}
}
void
SIPFmt::setField(const std::string& field)
{
size_t at = field.find_first_of(':');
assert(at != std::string::npos);
setFieldValue(field.substr(0, at), field.substr(at + 1));
}
void
SIPFmt::setFieldValue(const std::string& field, const std::string& value)
{
std::string fieldLow;
fieldLow.reserve(field.size());
for (auto it = field.cbegin(); it != field.cend(); ++it) {
fieldLow.push_back(tolower(*it));
}
fields_[fieldLow] = value;
}
const std::string&
SIPFmt::getField(const std::string& field) const
{
static std::string emptyString("");
std::string fieldLow;
fieldLow.reserve(field.size());
for (auto it = field.cbegin(); it != field.cend(); ++it) {
fieldLow.push_back(tolower(*it));
}
try {
return fields_.at(fieldLow);
} catch (...) {
return emptyString;
}
}
const std::vector<uint8_t>&
SIPFmt::getBody()
{
return body_;
}
void
SIPFmt::swapBody(std::vector<uint8_t>& newBody)
{
body_.swap(newBody);
}
void
SIPFmt::swap(std::vector<uint8_t>& with)
{
if (not isValid_) {
return;
}
std::vector<uint8_t> data;
auto push_str = [&](const std::string& str) {
for (auto it = str.cbegin(); it != str.cend(); ++it) {
data.emplace_back((uint8_t) *it);
}
};
auto push_CRLN = [&] {
data.emplace_back((uint8_t) '\r');
data.emplace_back((uint8_t) '\n');
};
if (isResponse()) {
push_str(version_);
data.emplace_back((uint8_t) ' ');
push_str(status_);
data.emplace_back((uint8_t) ' ');
push_str(msg_);
push_CRLN();
} else {
push_str(method_);
data.emplace_back(' ');
push_str(URI_);
data.emplace_back(' ');
push_str(version_);
push_CRLN();
}
setFieldValue("content-length", std::to_string(body_.size()));
for (auto it = fields_.cbegin(); it != fields_.cend(); ++it) {
push_str(it->first);
data.emplace_back((uint8_t) ':');
data.emplace_back((uint8_t) ' ');
push_str(it->second);
push_CRLN();
}
push_CRLN();
for (auto it = body_.begin(); it != body_.end(); ++it) {
data.emplace_back((uint8_t) *it);
}
data.shrink_to_fit();
data.swap(with);
}
/*
* Copyright (C) 2021 Savoir-faire Linux Inc.
*
* Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <vector>
#include <iostream>
#include <string>
#include <map>
class SIPFmt
{
public:
SIPFmt(const std::vector<uint8_t>& data);
bool parse(const std::vector<uint8_t>& blob);
void pushBody(char *bytes, size_t len);
const std::string& getField(const std::string& field) const ;
const std::vector<uint8_t>& getBody();
void setVersion(const std::string& version) { version_ = version; }
void setField(const std::string& field);
void setFieldValue(const std::string& field, const std::string& value);
void setMethod(const std::string& method) { method_ = method; }
void setURI(const std::string& URI) { URI_ = URI; }
void setStatus(const std::string& status) { status_ = status; }
void setMsg(const std::string& msg) { msg_ = msg; }
void swapBody(std::vector<uint8_t>& body);
bool isValid() const { return isValid_; }
bool isRequest() const { return isValid_ and isRequest_; }
bool isResponse() const { return isValid_ and not isRequest_; }
bool isApplication(const std::string& app) const
{
return isValid() and (std::string::npos != getField("content-type").find("application/" + app));
}
void setAsRequest() { isRequest_ = true; };
void setAsResponse() { isRequest_ = false; };
void swap(std::vector<uint8_t>& with);
private:
std::vector<uint8_t> body_ {};
std::string status_ {};
std::string version_ {};
std::string msg_ {};
std::string URI_ {};
std::string method_ {};
std::map<std::string, std::string> fields_ {};
bool isValid_;
bool isRequest_ {};
};
/*
* Copyright (C) 2021 Savoir-faire Linux Inc.
*
* Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
%{
#include <vector>
#include <cstdint>
#include "lib/sip-fmt.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
#pragma GCC diagnostic ignored "-Wsuggest-attribute=malloc"
#pragma GCC diagnostic ignored "-Wnull-dereference"
#pragma GCC diagnostic ignored "-Wstrict-overflow"
%}
/* Definitions */
%option 8bit
%option warn
%option always-interactive
%option pointer
%option reentrant
%option noyywrap
%option noyylineno
%option nounput
%option nodefault
%option noinput
%option debug
%option prefix="sip_yy"
%option extra-type="SIPFmt*"
%x RESPONSE_STATUS
%x RESPONSE_MSG
%x REQUEST_URI
%x REQUEST_VERSION
%x FIELD_SEQUENCE
%x BODY
CRLF "\r\n"
SIP_METHOD REGISTER|INVITE|ACK|CANCEL|BYE|OPTIONS|MESSAGE|INFO
SIP_VERSION "SIP/2.0"
SIP_STATUS [0-9]{3}
SIP_MSG [^\r\n]*
SIP_URI ("sip"|"sips")":"[^\r\n ]+
FIELD_NAME ([a-zA-Z0-9]|[-_ ])+
FIELD_VALUE [^\r\n]+
/* Rules */
%%
/* Reponse line */
<INITIAL>^{SIP_VERSION} {
BEGIN(RESPONSE_STATUS);
yyextra->setAsResponse();
yyextra->setVersion(yytext);
}
<INITIAL>^{SIP_METHOD} {
BEGIN(REQUEST_URI);
yyextra->setAsRequest();
yyextra->setMethod(yytext);
}
<REQUEST_URI>{SIP_URI} {
BEGIN(REQUEST_VERSION);
yyextra->setURI(yytext);
}
<REQUEST_VERSION>{SIP_VERSION} {
yyextra->setVersion(yytext);
}
<RESPONSE_STATUS>{SIP_STATUS} {
BEGIN(RESPONSE_MSG);
yyextra->setStatus(yytext);
}
<RESPONSE_MSG>{SIP_MSG} {
yyextra->setMsg(yytext);
}
<REQUEST_VERSION,RESPONSE_MSG>{CRLF} {
BEGIN(FIELD_SEQUENCE);
}
/*
* Field sequence
*
* We don't support multi-line field value.
*/
<FIELD_SEQUENCE>{
{FIELD_NAME}":"{FIELD_VALUE}{CRLF} {
yytext[yyleng - 2] = '\0';
yyextra->setField(yytext);
yytext[yyleng - 2] = '\r';
}
{CRLF} {
BEGIN(BODY);
}
}
<BODY>{
.+|\n {
yyextra->pushBody(yytext, yyleng);
}
}
/* Don't care about spaces */
<INITIAL,RESPONSE_MSG,RESPONSE_STATUS,REQUEST_VERSION,REQUEST_URI>[ \t]+ { }
<INITIAL,BODY><<EOF>> { return 0; }
<RESPONSE_STATUS,RESPONSE_MSG,FIELD_SEQUENCE><<EOF>> { return -1; }
/* Default rule */
<*>.|\n { return -1; }
%%
/* END LEX */
bool
SIPFmt::parse(const std::vector<uint8_t>& blob)
{
yyscan_t scanner = NULL;
int err;
YY_BUFFER_STATE state;
isValid_ = false;
if (sip_yylex_init_extra(this, &scanner)) {
return isValid_;
}
// sip_yyset_debug(1, scanner);
state = sip_yy_scan_bytes((const char*)blob.data(),
blob.size(),
scanner);
sip_yy_switch_to_buffer(state, scanner);
err = sip_yylex(scanner);
sip_yy_delete_buffer(state, scanner);
sip_yylex_destroy(scanner);
if (err >= 0) {
isValid_ = true;
}
return isValid_;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment