Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions parser/parse_supported.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
#include "parse_supported.h"

#define IS_DELIM(c) (*(c) == ' ' || *(c) == '\t' || *(c) == '\r' || *(c) == '\n' || *(c) == ',')
/* an option tag is also terminated by the end of the header body, in which
* case there is no next character to look at */
#define IS_TAG_END(c, off, pos, len) \
((pos) + (off) == (len) || IS_DELIM((c) + (off)))

/* from parser/parse_hname2.c: */
#define LOWER_BYTE(b) ((b) | 0x20)
Expand All @@ -52,12 +56,14 @@ int parse_supported_body(str *body, unsigned int *sup)
/* skip spaces and commas */
for (; pos < len && IS_DELIM(p); ++pos, ++p);

val = LOWER_DWORD(READ(p));
/* the shortest option tag below is 4 chars long, so reading the
* leading dword must not reach past the end of the body */
val = (len - pos >= 4) ? LOWER_DWORD(READ(p)) : 0;
switch (val) {

/* "path" */
case _path_:
if(pos + 4 <= len && IS_DELIM(p+4)) {
if(pos + 4 <= len && IS_TAG_END(p, 4, pos, len)) {
*sup |= F_SUPPORTED_PATH;
pos += 5; p += 5;
} else
Expand All @@ -66,7 +72,7 @@ int parse_supported_body(str *body, unsigned int *sup)

/* "gruu" */
case _gruu_:
if(pos + 4 <= len && IS_DELIM(p+4)) {
if(pos + 4 <= len && IS_TAG_END(p, 4, pos, len)) {
*sup |= F_SUPPORTED_GRUU;
pos += 5; p += 5;
} else
Expand All @@ -77,7 +83,7 @@ int parse_supported_body(str *body, unsigned int *sup)
case _100r_:
if ( pos+6 <= len
&& LOWER_BYTE(*(p+4))=='e' && LOWER_BYTE(*(p+5))=='l'
&& IS_DELIM(p+6)) {
&& IS_TAG_END(p, 6, pos, len)) {
*sup |= F_SUPPORTED_100REL;
pos += SUPPORTED_100REL_LEN + 1;
p += SUPPORTED_100REL_LEN + 1;
Expand All @@ -88,7 +94,7 @@ int parse_supported_body(str *body, unsigned int *sup)
/* "timer" */
case _time_:
if ( pos+5 <= len && LOWER_BYTE(*(p+4))=='r'
&& IS_DELIM(p+5) ) {
&& IS_TAG_END(p, 5, pos, len) ) {
*sup |= F_SUPPORTED_TIMER;
pos += SUPPORTED_TIMER_LEN + 1;
p += SUPPORTED_TIMER_LEN + 1;
Expand All @@ -99,7 +105,7 @@ int parse_supported_body(str *body, unsigned int *sup)
/* "eventlist" */
case _even_:
if ( pos+9 <= len && LOWER_DWORD(READ(p+4))==_tlis_ && LOWER_BYTE(*(p+8))=='t'
&& IS_DELIM(p+9) ) {
&& IS_TAG_END(p, 9, pos, len) ) {
*sup |= F_SUPPORTED_EVENTLIST;
pos += SUPPORTED_EVENTLIST_LEN + 1;
p += SUPPORTED_EVENTLIST_LEN + 1;
Expand Down
108 changes: 108 additions & 0 deletions parser/test/test_parse_supported.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2026 Sahana Bogar
*
* This file is part of opensips, a free SIP server.
*
* opensips 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 2 of the License, or
* (at your option) any later version
*
* opensips 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 <tap.h>

#include "../../str.h"
#include "../../ut.h"

#include "../parse_supported.h"

#include "test_parse_supported.h"
#include "test_oob.h"

static void test_parse_supported_body_oob(const str *, enum oob_position, void *);

/* parse @hdr as a Supported header body and return the option tag flags */
static int parse_sup(const char *hdr, unsigned int *sup)
{
str body;

init_str(&body, hdr);
return parse_supported_body(&body, sup);
}

/* each one is read with the body ending on a guard page, so any lookahead
* stepping outside it faults: a recognized option tag flush against the end
* of the body, an element too short to hold one, and a body made of nothing
* but delimiters */
static const str oob_tset[] = {
str_init("path"),
str_init("gruu"),
str_init("timer"),
str_init("100rel"),
str_init("eventlist"),
str_init("path,gruu"),
str_init("timer, path"),
str_init("eventlist,100rel"),
str_init("100rel,x"),
str_init("foo"),
str_init("x"),
str_init(","),
str_init(" \t"),
{NULL, 0}
};

void test_parse_supported(void)
{
unsigned int sup;
int i;

/* an option tag is recognized when it ends at the end of the body, with
* no delimiter of its own to close it */
ok(parse_sup("path", &sup) == 0 &&
sup == F_SUPPORTED_PATH, "sup-path");
ok(parse_sup("gruu", &sup) == 0 &&
sup == F_SUPPORTED_GRUU, "sup-gruu");
ok(parse_sup("timer", &sup) == 0 &&
sup == F_SUPPORTED_TIMER, "sup-timer");
ok(parse_sup("100rel", &sup) == 0 &&
sup == F_SUPPORTED_100REL, "sup-100rel");
ok(parse_sup("eventlist", &sup) == 0 &&
sup == F_SUPPORTED_EVENTLIST, "sup-eventlist");

/* ... and when it is followed by a delimiter */
ok(parse_sup("path,gruu", &sup) == 0 &&
sup == (F_SUPPORTED_PATH|F_SUPPORTED_GRUU), "sup-path-gruu");
ok(parse_sup("timer, 100rel , eventlist", &sup) == 0 &&
sup == (F_SUPPORTED_TIMER|F_SUPPORTED_100REL|F_SUPPORTED_EVENTLIST),
"sup-list");

/* unknown or truncated option tags are skipped */
ok(parse_sup("pat", &sup) == 0 && sup == 0, "sup-short");
ok(parse_sup("paths", &sup) == 0 && sup == 0, "sup-prefix");
ok(parse_sup("x,foo", &sup) == 0 && sup == 0, "sup-unknown");
ok(parse_sup(",", &sup) == 0 && sup == 0, "sup-delim-only");
ok(parse_sup("", &sup) == 0 && sup == 0, "sup-empty");
ok(parse_sup("x,path", &sup) == 0 &&
sup == F_SUPPORTED_PATH, "sup-unknown-then-path");

for (i = 0; oob_tset[i].s != NULL; i++)
test_oob(&oob_tset[i], test_parse_supported_body_oob, &sup);
}

static void test_parse_supported_body_oob(const str *tstr, enum oob_position where,
void *farg)
{
str body = *tstr;

parse_supported_body(&body, (unsigned int *)farg);
ok(1, OOB_CHECK_OK_MSG("parse_supported_body", tstr, where));
}
26 changes: 26 additions & 0 deletions parser/test/test_parse_supported.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2026 Sahana Bogar
*
* This file is part of opensips, a free SIP server.
*
* opensips 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 2 of the License, or
* (at your option) any later version
*
* opensips 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
*/

#ifndef __TEST_PARSE_SUPPORTED_H__
#define __TEST_PARSE_SUPPORTED_H__

void test_parse_supported(void);

#endif /* __TEST_PARSE_SUPPORTED_H__ */
2 changes: 2 additions & 0 deletions parser/test/test_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "test_parse_fcaps.h"
#include "test_parser.h"
#include "test_parse_authenticate_body.h"
#include "test_parse_supported.h"

void test_parse_uri(void)
{
Expand Down Expand Up @@ -328,4 +329,5 @@ void test_parser(void)
test_parse_qop_val();
test_parse_fcaps();
test_parse_authenticate_body();
test_parse_supported();
}