2
0

sdp_parse.c 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955
  1. /*
  2. * This file is part of the Sofia-SIP package
  3. *
  4. * Copyright (C) 2005 Nokia Corporation.
  5. *
  6. * Contact: Pekka Pessi <pekka.pessi@nokia.com>
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * as published by the Free Software Foundation; either version 2.1 of
  11. * the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21. * 02110-1301 USA
  22. *
  23. */
  24. /**@ingroup sdp_parser
  25. * @CFILE sdp_parse.c
  26. * @brief Simple SDP parser interface.
  27. *
  28. * @author Pekka Pessi <Pekka.Pessi@nokia.com>
  29. * @author Kai Vehmanen <kai.vehmanen@nokia.com>
  30. *
  31. * @date Created: Fri Feb 18 10:25:08 2000 ppessi
  32. *
  33. * @sa @RFC4566, @RFC2327.
  34. */
  35. #include "config.h"
  36. #include <sofia-sip/su_alloc.h>
  37. #include <sofia-sip/su_string.h>
  38. #include "sofia-sip/sdp.h"
  39. #include <stddef.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <stdarg.h>
  43. #include <stdio.h>
  44. #include <limits.h>
  45. #include <assert.h>
  46. /** @typedef struct sdp_parser_s sdp_parser_t;
  47. *
  48. * SDP parser handle.
  49. *
  50. * The SDP parser handle returned by sdp_parse() contains either
  51. * a successfully parsed SDP session #sdp_session_t or an error message.
  52. * If sdp_session() returns non-NULL, parsing was successful.
  53. *
  54. * @sa #sdp_session_t, sdp_parse(), sdp_session(), sdp_parsing_error(),
  55. * sdp_sanity_check(), sdp_parser_home(), sdp_parser_free(), @RFC4566,
  56. * @RFC2327.
  57. */
  58. struct sdp_parser_s {
  59. su_home_t pr_home[1];
  60. union {
  61. char pru_error[128];
  62. sdp_session_t pru_session[1];
  63. } pr_output;
  64. char *pr_message;
  65. sdp_mode_t pr_session_mode;
  66. unsigned pr_ok : 1;
  67. unsigned pr_strict : 1;
  68. unsigned pr_anynet : 1;
  69. unsigned pr_mode_0000 : 1;
  70. unsigned pr_mode_manual : 1;
  71. unsigned pr_insane : 1;
  72. unsigned pr_c_missing : 1;
  73. unsigned pr_config : 1;
  74. };
  75. #define is_posdigit(c) ((c) >= '1' && (c) <= '9')
  76. #define is_digit(c) ((c) >= '0' && (c) <= '9')
  77. #define is_space(c) ((c) == ' ')
  78. #define is_tab(c) ((c) == '\t')
  79. #define pr_error pr_output.pru_error
  80. #define pr_session pr_output.pru_session
  81. #ifdef _MSC_VER
  82. #undef STRICT
  83. #endif
  84. #define STRICT(pr) (pr->pr_strict)
  85. /* Static parser object used when running out of memory */
  86. static const struct sdp_parser_s no_mem_error =
  87. {
  88. { SU_HOME_INIT(no_mem_error) },
  89. { "sdp: not enough memory" }
  90. };
  91. /* Internal prototypes */
  92. static void parse_message(sdp_parser_t *p);
  93. static int parsing_error(sdp_parser_t *p, char const *fmt, ...);
  94. /** Parse an SDP message.
  95. *
  96. * The function sdp_parse() parses an SDP message @a msg of size @a
  97. * msgsize. If msgsize is -1, the size of message is calculated using
  98. * strlen().
  99. *
  100. * Parsing is done according to the given @a flags.
  101. *
  102. * The SDP message may not contain a NUL.
  103. *
  104. * The parsing result is stored to an #sdp_session_t structure.
  105. *
  106. * @param home memory home
  107. * @param msg pointer to message
  108. * @param msgsize size of the message (excluding final NUL, if any)
  109. * @param flags flags affecting the parsing.
  110. *
  111. * The following flags are used by parser:
  112. *
  113. * @li #sdp_f_strict Parser should accept only messages conforming strictly
  114. * to the specification.
  115. * @li #sdp_f_anynet Parser accepts unknown network or address types.
  116. * @li #sdp_f_insane Do not run sanity check.
  117. * @li #sdp_f_c_missing Sanity check does not require c= for each m= line
  118. * @li #sdp_f_mode_0000 Parser regards "c=IN IP4 0.0.0.0" as "a=inactive"
  119. * (likewise with c=IN IP6 ::)
  120. * @li #sdp_f_mode_manual Do not generate or parse SDP mode
  121. * @li #sdp_f_config Parse config files (any line can be missing)
  122. *
  123. * @return
  124. * Always a valid parser handle.
  125. *
  126. * @todo Parser accepts some non-conforming SDP even with #sdp_f_strict.
  127. *
  128. * @sa sdp_session(), sdp_parsing_error(), sdp_sanity_check(),
  129. * sdp_parser_home(), sdp_parser_free(), @RFC4566, @RFC2327.
  130. */
  131. sdp_parser_t *
  132. sdp_parse(su_home_t *home, char const msg[], issize_t msgsize, int flags)
  133. {
  134. sdp_parser_t *p;
  135. char *b;
  136. size_t len;
  137. if (msg == NULL) {
  138. p = su_home_clone(home, sizeof(*p));
  139. if (p)
  140. parsing_error(p, "invalid input message");
  141. else
  142. p = (sdp_parser_t*)&no_mem_error;
  143. return p;
  144. }
  145. if (msgsize == -1)
  146. len = strlen(msg);
  147. else
  148. len = msgsize;
  149. if (len > ISSIZE_MAX)
  150. len = ISSIZE_MAX;
  151. p = su_home_clone(home, sizeof(*p) + len + 1);
  152. if (p) {
  153. b = strncpy((void *)(p + 1), msg, len);
  154. b[len] = 0;
  155. p->pr_message = b;
  156. p->pr_strict = (flags & sdp_f_strict) != 0;
  157. p->pr_anynet = (flags & sdp_f_anynet) != 0;
  158. p->pr_mode_0000 = (flags & sdp_f_mode_0000) != 0;
  159. p->pr_insane = (flags & sdp_f_insane) != 0;
  160. p->pr_c_missing = (flags & sdp_f_c_missing) != 0;
  161. if (flags & sdp_f_config)
  162. p->pr_c_missing = 1, p->pr_config = 1;
  163. p->pr_mode_manual = (flags & sdp_f_mode_manual) != 0;
  164. p->pr_session_mode = sdp_sendrecv;
  165. parse_message(p);
  166. return p;
  167. }
  168. if (p)
  169. sdp_parser_free(p);
  170. return (sdp_parser_t*)&no_mem_error;
  171. }
  172. /** Obtain memory home used by parser */
  173. su_home_t *sdp_parser_home(sdp_parser_t *parser)
  174. {
  175. if (parser != &no_mem_error)
  176. return parser->pr_home;
  177. else
  178. return NULL;
  179. }
  180. /** Retrieve an SDP session structure.
  181. *
  182. * The function sdp_session() returns a pointer to the SDP session
  183. * structure associated with the SDP parser @a p. The pointer and all the
  184. * data in the structure are valid until sdp_parser_free() is called.
  185. *
  186. * @param p SDP parser
  187. *
  188. * @return
  189. * The function sdp_session() returns a pointer to an parsed SDP message
  190. * or NULL, if an error has occurred. */
  191. sdp_session_t *
  192. sdp_session(sdp_parser_t *p)
  193. {
  194. return p && p->pr_ok ? p->pr_session : NULL;
  195. }
  196. /** Get a parsing error message.
  197. *
  198. * The function sdp_parsing_error() returns the error message associated
  199. * with an SDP parser @a p.
  200. *
  201. * @param p SDP parser
  202. *
  203. * @return
  204. * The function sdp_parsing_error() returns a C string describing parsing
  205. * error, or NULL if no error occurred.
  206. */
  207. char const *sdp_parsing_error(sdp_parser_t *p)
  208. {
  209. return !p->pr_ok ? p->pr_error : NULL;
  210. }
  211. /** Free an SDP parser.
  212. *
  213. * The function sdp_parser_free() frees an SDP parser object along with
  214. * the memory blocks associated with it.
  215. *
  216. * @param p pointer to the SDP parser to be freed
  217. */
  218. void sdp_parser_free(sdp_parser_t *p)
  219. {
  220. if (p && p != &no_mem_error)
  221. su_home_unref(p->pr_home);
  222. }
  223. /* ========================================================================= */
  224. /* =========================================================================
  225. * Private part
  226. */
  227. /* Parsing tokens */
  228. #define SPACE " "
  229. #define TAB "\011"
  230. #define CRLF "\015\012"
  231. #define ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  232. #define DIGIT "0123456789"
  233. #define TOKEN ALPHA DIGIT "-!#$%&'*+.^_`{|}~"
  234. /* ========================================================================= */
  235. /* Parsing functions */
  236. static void post_session(sdp_parser_t *p, sdp_session_t *sdp);
  237. static void parse_origin(sdp_parser_t *p, char *r, sdp_origin_t **result);
  238. static void parse_subject(sdp_parser_t *p, char *r, sdp_text_t **result);
  239. static void parse_information(sdp_parser_t *p, char *r, sdp_text_t **result);
  240. static void parse_uri(sdp_parser_t *p, char *r, sdp_text_t **result);
  241. static void parse_email(sdp_parser_t *p, char *r, sdp_list_t **result);
  242. static void parse_phone(sdp_parser_t *p, char *r, sdp_list_t **result);
  243. static void parse_connection(sdp_parser_t *p, char *r, sdp_connection_t **result);
  244. static void parse_bandwidth(sdp_parser_t *p, char *r, sdp_bandwidth_t **result);
  245. static void parse_time(sdp_parser_t *p, char *r, sdp_time_t **result);
  246. static void parse_repeat(sdp_parser_t *p, char *r, sdp_repeat_t **result);
  247. static void parse_zone(sdp_parser_t *p, char *r, sdp_zone_t **result);
  248. static void parse_key(sdp_parser_t *p, char *r, sdp_key_t **result);
  249. static void parse_session_attr(sdp_parser_t *p, char *r, sdp_attribute_t **result);
  250. static void parse_media(sdp_parser_t *p, char *r, sdp_media_t **result);
  251. static void parse_payload(sdp_parser_t *p, char *r, sdp_rtpmap_t **result);
  252. static void parse_media_attr(sdp_parser_t *p, char *r, sdp_media_t *m,
  253. sdp_attribute_t **result);
  254. static int parse_rtpmap(sdp_parser_t *p, char *r, sdp_media_t *m);
  255. static int parse_fmtp(sdp_parser_t *p, char *r, sdp_media_t *m);
  256. static void parse_text_list(sdp_parser_t *p, char *r, sdp_list_t **result);
  257. static void parse_descs(sdp_parser_t *p, char *r, char *m, sdp_media_t **result);
  258. static int parse_ul(sdp_parser_t *p, char **r, unsigned long *result,
  259. unsigned long max_value);
  260. static int parse_ull(sdp_parser_t *p, char **r, uint64_t *result,
  261. uint64_t max_value);
  262. static void parse_alloc_error(sdp_parser_t *p, const char *typename);
  263. static char *next(char **message, const char *sep, const char *strip);
  264. static char *token(char **message, const char *sep, const char *legal,
  265. const char *strip);
  266. #if 0
  267. static void check_mandatory(sdp_parser_t *p, sdp_session_t *sdp);
  268. #endif
  269. /* -------------------------------------------------------------------------
  270. * Macro PARSE_ALLOC
  271. *
  272. * Description:
  273. * This macro declares a pointer (v) of given type (t). It then allocates
  274. * an structure of given type (t). If allocation was succesful, it assigns
  275. * the XX_size member with appropriate value.
  276. */
  277. #define PARSE_ALLOC(p, t, v) \
  278. t *v = su_salloc(p->pr_home, sizeof(*v)); \
  279. if (!v && (parse_alloc_error(p, #t), 1)) return;
  280. /* -------------------------------------------------------------------------
  281. * Macro PARSE_CHECK_REST
  282. *
  283. * Description:
  284. * This macro check if there is extra data at the end of field.
  285. */
  286. #define PARSE_CHECK_REST(p, s, n)\
  287. if (*s && (parsing_error(p, "extra data after %s (\"%.04s\")", n, s), 1)) \
  288. return
  289. /* -------------------------------------------------------------------------
  290. * Function parse_message() - parse an SDP message
  291. *
  292. * Description:
  293. * This function parses an SDP message, which is copied into the
  294. * p->pr_message. The p->pr_message is modified during the parsing,
  295. * and parts of it are returned in p->pr_session.
  296. *
  297. * Parameters:
  298. * p - pointer to SDP parser object
  299. */
  300. static void parse_message(sdp_parser_t *p)
  301. {
  302. /*
  303. announcement = proto-version
  304. origin-field
  305. session-name-field
  306. information-field
  307. uri-field
  308. email-fields
  309. phone-fields
  310. connection-field
  311. bandwidth-fields
  312. time-fields
  313. key-field
  314. attribute-fields
  315. media-descriptions
  316. */
  317. sdp_session_t *sdp = p->pr_session;
  318. char *record, *rest;
  319. char const *strip;
  320. char *message = p->pr_message;
  321. char field = '\0';
  322. sdp_list_t **emails = &sdp->sdp_emails;
  323. sdp_list_t **phones = &sdp->sdp_phones;
  324. sdp_bandwidth_t **bandwidths = &sdp->sdp_bandwidths;
  325. sdp_time_t **times = &sdp->sdp_time;
  326. sdp_repeat_t **repeats = NULL;
  327. sdp_zone_t **zones = NULL;
  328. sdp_attribute_t **attributes = &sdp->sdp_attributes;
  329. if (!STRICT(p))
  330. strip = SPACE TAB; /* skip initial whitespace */
  331. else
  332. strip = "";
  333. p->pr_ok = 1;
  334. p->pr_session->sdp_size = sizeof(p->pr_session);
  335. /* Require that version comes first */
  336. record = next(&message, CRLF, strip);
  337. if (!su_strmatch(record, "v=0")) {
  338. if (!p->pr_config || !record || record[1] != '=') {
  339. parsing_error(p, "bad SDP message");
  340. return;
  341. }
  342. }
  343. else {
  344. record = next(&message, CRLF, strip);
  345. }
  346. /*
  347. XXX - the lines in SDP are in certain order, which we don't check here.
  348. For stricter parsing we might want to parse o= and s= next.
  349. */
  350. for (;
  351. record && p->pr_ok;
  352. record = next(&message, CRLF, strip)) {
  353. field = record[0];
  354. if (strlen(record) < 2) {
  355. return;
  356. }
  357. rest = record + 2; rest += strspn(rest, strip);
  358. if (record[1] != '=') {
  359. parsing_error(p, "bad line \"%s\"", record);
  360. return;
  361. }
  362. switch (field) {
  363. case 'o':
  364. parse_origin(p, rest, &sdp->sdp_origin);
  365. break;
  366. case 's':
  367. parse_subject(p, rest, &sdp->sdp_subject);
  368. break;
  369. case 'i':
  370. parse_information(p, rest, &sdp->sdp_information);
  371. break;
  372. case 'u':
  373. parse_uri(p, rest, &sdp->sdp_uri);
  374. break;
  375. case 'e':
  376. parse_email(p, rest, emails);
  377. emails = &(*emails)->l_next;
  378. break;
  379. case 'p':
  380. parse_phone(p, rest, phones);
  381. phones = &(*phones)->l_next;
  382. break;
  383. case 'c':
  384. parse_connection(p, rest, &sdp->sdp_connection);
  385. break;
  386. case 'b':
  387. parse_bandwidth(p, rest, bandwidths);
  388. bandwidths = &(*bandwidths)->b_next;
  389. break;
  390. case 't':
  391. parse_time(p, rest, times);
  392. repeats = &(*times)->t_repeat;
  393. zones = &(*times)->t_zone;
  394. times = &(*times)->t_next;
  395. break;
  396. case 'r':
  397. if (repeats)
  398. parse_repeat(p, rest, repeats);
  399. else
  400. parsing_error(p, "repeat field without time field");
  401. break;
  402. case 'z':
  403. if (zones)
  404. parse_zone(p, rest, zones), zones = NULL;
  405. else
  406. parsing_error(p, "zone field without time field");
  407. break;
  408. case 'k':
  409. parse_key(p, rest, &sdp->sdp_key);
  410. break;
  411. case 'a':
  412. parse_session_attr(p, rest, attributes);
  413. if (*attributes)
  414. attributes = &(*attributes)->a_next;
  415. break;
  416. case 'm':
  417. parse_descs(p, record, message, &sdp->sdp_media);
  418. post_session(p, sdp);
  419. return;
  420. default:
  421. parsing_error(p, "unknown field \"%s\"", record);
  422. return;
  423. }
  424. }
  425. post_session(p, sdp);
  426. }
  427. #ifdef SOFIA_AUTO_CORRECT_INADDR_ANY
  428. int sdp_connection_is_inaddr_any(sdp_connection_t const *c)
  429. {
  430. return
  431. c &&
  432. c->c_nettype == sdp_net_in &&
  433. ((c->c_addrtype == sdp_addr_ip4 && su_strmatch(c->c_address, "0.0.0.0")) ||
  434. (c->c_addrtype == sdp_addr_ip6 && su_strmatch(c->c_address, "::")));
  435. }
  436. #endif
  437. /**Postprocess session description.
  438. *
  439. * Postprocessing includes setting the session backpointer for each media,
  440. * doing sanity checks and setting rejected and mode flags.
  441. */
  442. static void post_session(sdp_parser_t *p, sdp_session_t *sdp)
  443. {
  444. sdp_media_t *m;
  445. #ifdef SOFIA_AUTO_CORRECT_INADDR_ANY
  446. sdp_connection_t const *c;
  447. #endif
  448. if (!p->pr_ok)
  449. return;
  450. /* Set session back-pointer */
  451. for (m = sdp->sdp_media; m; m = m->m_next) {
  452. m->m_session = sdp;
  453. }
  454. if (p->pr_config) {
  455. if (sdp->sdp_version[0] != 0)
  456. parsing_error(p, "Incorrect version");
  457. return;
  458. }
  459. /* Go through all media and set mode */
  460. for (m = sdp->sdp_media; m; m = m->m_next) {
  461. if (m->m_port == 0) {
  462. m->m_mode = sdp_inactive;
  463. m->m_rejected = 1;
  464. continue;
  465. }
  466. #ifdef SOFIA_AUTO_CORRECT_INADDR_ANY
  467. c = sdp_media_connections(m);
  468. if (p->pr_mode_0000 && sdp_connection_is_inaddr_any(c)) {
  469. /* Reset recvonly flag */
  470. m->m_mode &= ~sdp_recvonly;
  471. }
  472. #endif
  473. }
  474. if (p->pr_insane)
  475. return;
  476. /* Verify that all mandatory fields are present */
  477. if (sdp_sanity_check(p) < 0)
  478. return;
  479. }
  480. /** Validates that all mandatory fields exist
  481. *
  482. * Checks that all necessary fields (v=, o=) exists in the parsed sdp. If
  483. * strict, check that all mandatory fields (c=, o=, s=, t=) are present.
  484. * This function also goes through all media, marks rejected media as such,
  485. * and updates the mode accordingly.
  486. *
  487. * @retval 0 if parsed SDP description is valid
  488. * @retval -1 if some SDP line is missing
  489. * @retval -2 if c= line is missing
  490. */
  491. int sdp_sanity_check(sdp_parser_t *p)
  492. {
  493. sdp_session_t *sdp = p->pr_session;
  494. sdp_media_t *m;
  495. if (!p || !p->pr_ok)
  496. return -1;
  497. else if (sdp->sdp_version[0] != 0)
  498. return parsing_error(p, "Incorrect version");
  499. else if (!sdp->sdp_origin)
  500. return parsing_error(p, "No o= present");
  501. else if (p->pr_strict && !sdp->sdp_subject)
  502. return parsing_error(p, "No s= present");
  503. else if (p->pr_strict && !sdp->sdp_time)
  504. return parsing_error(p, "No t= present");
  505. /* If there is no session level c= check that one exists for all media */
  506. /* c= line may be missing if this is a RTSP description */
  507. if (!p->pr_c_missing && !sdp->sdp_connection) {
  508. for (m = sdp->sdp_media ; m ; m = m->m_next) {
  509. if (!m->m_connections && !m->m_rejected) {
  510. parsing_error(p, "No c= on either session level or all mediums");
  511. return -2;
  512. }
  513. }
  514. }
  515. return 0;
  516. }
  517. #if 0
  518. /**
  519. * Parse a "v=" field
  520. *
  521. * The function parser_version() parses the SDP version field.
  522. *
  523. * @param p pointer to SDP parser object
  524. * @param r pointer to record data
  525. * @param result pointer to which parsed record is assigned
  526. */
  527. static void parse_version(sdp_parser_t *p, char *r, sdp_version_t *result)
  528. {
  529. /*
  530. proto-version = "v=" 1*DIGIT CRLF
  531. ;[RFC2327] describes version 0
  532. */
  533. if (parse_ul(p, &r, result, 0))
  534. parsing_error(p, "version \"%s\" is invalid", r);
  535. else if (*result > 0)
  536. parsing_error(p, "unknown version v=%s", r);
  537. }
  538. #endif
  539. /* -------------------------------------------------------------------------
  540. * Function parse_origin() - parse an "o=" field
  541. *
  542. * Description:
  543. * This function parses an SDP origin field.
  544. *
  545. * Parameters:
  546. * p - pointer to SDP parser object
  547. * r - pointer to record data
  548. * result - pointer to which parsed record is assigned
  549. */
  550. static void parse_origin(sdp_parser_t *p, char *r, sdp_origin_t **result)
  551. {
  552. /*
  553. origin-field = "o=" username space
  554. sess-id space sess-version space
  555. nettype space addrtype space
  556. addr CRLF
  557. username = safe
  558. ;pretty wide definition, but doesn't include space
  559. sess-id = 1*(DIGIT)
  560. ;should be unique for this originating username/host
  561. sess-version = 1*(DIGIT)
  562. ;0 is a new session
  563. */
  564. PARSE_ALLOC(p, sdp_origin_t, o);
  565. *result = o;
  566. o->o_username = token(&r, SPACE TAB, NULL, SPACE TAB);
  567. if (!o->o_username) {
  568. parsing_error(p, "invalid username");
  569. return;
  570. }
  571. if (parse_ull(p, &r, &o->o_id, 0)) {
  572. parsing_error(p, "invalid session id");
  573. return;
  574. }
  575. if (parse_ull(p, &r, &o->o_version, 0)) {
  576. parsing_error(p, "invalid session version");
  577. return;
  578. }
  579. parse_connection(p, r, &o->o_address);
  580. }
  581. /* -------------------------------------------------------------------------
  582. * Function parse_subject() - parse an "s=" field
  583. *
  584. * Description:
  585. * This function parses an SDP subject field.
  586. *
  587. * Parameters:
  588. * p - pointer to SDP parser object
  589. * r - pointer to record data
  590. * result - pointer to which parsed record is assigned
  591. */
  592. static void parse_subject(sdp_parser_t *p, char *r, sdp_text_t **result)
  593. {
  594. /*
  595. session-name-field = "s=" text CRLF
  596. text = byte-string
  597. */
  598. *result = r;
  599. }
  600. /* -------------------------------------------------------------------------
  601. * Function parse_information() - parse an "i=" field
  602. *
  603. * Description:
  604. * This function parses an SDP information field.
  605. *
  606. * Parameters:
  607. * p - pointer to SDP parser object
  608. * r - pointer to record data
  609. * result - pointer to which parsed record is assigned
  610. */
  611. static void parse_information(sdp_parser_t *p, char *r, sdp_text_t **result)
  612. {
  613. /*
  614. information-field = ["i=" text CRLF]
  615. */
  616. if (result) *result = r;
  617. }
  618. /* -------------------------------------------------------------------------
  619. * Function parse_uri() - parse an "u=" field
  620. *
  621. * Description:
  622. * This function parses an SDP URI field.
  623. *
  624. * Parameters:
  625. * p - pointer to SDP parser object
  626. * r - pointer to record data
  627. * result - pointer to which parsed record is assigned
  628. */
  629. static void parse_uri(sdp_parser_t *p, char *r, sdp_text_t **result)
  630. {
  631. /*
  632. uri-field = ["u=" uri CRLF]
  633. uri= ;defined in RFC1630
  634. */
  635. /* XXX - no syntax checking here */
  636. *result = r;
  637. }
  638. /* -------------------------------------------------------------------------
  639. * Function parse_email() - parse an "e=" field
  640. *
  641. * Description:
  642. * This function parses an SDP email field.
  643. *
  644. * Parameters:
  645. * p - pointer to SDP parser object
  646. * r - pointer to record data
  647. * result - pointer to which parsed record is assigned
  648. */
  649. static void parse_email(sdp_parser_t *p, char *r, sdp_list_t **result)
  650. {
  651. /*
  652. email-fields = *("e=" email-address CRLF)
  653. email-address = email | email "(" email-safe ")" |
  654. email-safe "<" email ">"
  655. email = ;defined in RFC822 */
  656. parse_text_list(p, r, result);
  657. }
  658. /* -------------------------------------------------------------------------
  659. * Function parse_phone() - parse an "p=" field
  660. *
  661. * Description:
  662. * This function parses an SDP phone field.
  663. *
  664. * Parameters:
  665. * p - pointer to SDP parser object
  666. * r - pointer to record data
  667. * result - pointer to which parsed record is assigned
  668. */
  669. static void parse_phone(sdp_parser_t *p, char *r, sdp_list_t **result)
  670. {
  671. /*
  672. phone-fields = *("p=" phone-number CRLF)
  673. phone-number = phone | phone "(" email-safe ")" |
  674. email-safe "<" phone ">"
  675. phone = "+" POS-DIGIT 1*(space | "-" | DIGIT)
  676. ;there must be a space or hyphen between the
  677. ;international code and the rest of the number.
  678. */
  679. parse_text_list(p, r, result);
  680. }
  681. /* -------------------------------------------------------------------------
  682. * Function parse_connection() - parse an "c=" field
  683. *
  684. * Description:
  685. * This function parses an SDP connection field.
  686. *
  687. * Parameters:
  688. * p - pointer to SDP parser object
  689. * r - pointer to record data
  690. * result - pointer to which parsed record is assigned
  691. */
  692. static void parse_connection(sdp_parser_t *p, char *r, sdp_connection_t **result)
  693. {
  694. /*
  695. connection-field = ["c=" nettype space addrtype space
  696. connection-address CRLF]
  697. ;a connection field must be present
  698. ;in every media description or at the
  699. ;session-level
  700. nettype = "IN"
  701. ;list to be extended
  702. addrtype = "IP4" | "IP6"
  703. ;list to be extended
  704. connection-address = multicast-address
  705. | addr
  706. multicast-address = 3*(decimal-uchar ".") decimal-uchar "/" ttl
  707. [ "/" integer ]
  708. ;multicast addresses may be in the range
  709. ;224.0.0.0 to 239.255.255.255
  710. ttl = decimal-uchar
  711. addr = FQDN | unicast-address
  712. FQDN = 4*(alpha-numeric|"-"|".")
  713. ;fully qualified domain name as specified in RFC1035
  714. unicast-address = IP4-address | IP6-address
  715. IP4-address = b1 "." decimal-uchar "." decimal-uchar "." b4
  716. b1 = decimal-uchar
  717. ;less than "224"; not "0" or "127"
  718. b4 = decimal-uchar
  719. ;not "0"
  720. IP6-address = ;to be defined
  721. */
  722. PARSE_ALLOC(p, sdp_connection_t, c);
  723. *result = c;
  724. if (su_casenmatch(r, "IN", 2)) {
  725. char *s;
  726. /* nettype is internet */
  727. c->c_nettype = sdp_net_in;
  728. s = token(&r, SPACE TAB, NULL, NULL);
  729. /* addrtype */
  730. s = token(&r, SPACE TAB, NULL, NULL);
  731. if (su_casematch(s, "IP4"))
  732. c->c_addrtype = sdp_addr_ip4;
  733. else if (su_casematch(s, "IP6"))
  734. c->c_addrtype = sdp_addr_ip6;
  735. else {
  736. parsing_error(p, "unknown IN address type: %s", s);
  737. return;
  738. }
  739. /* address */
  740. s = next(&r, SPACE TAB, SPACE TAB);
  741. c->c_address = s;
  742. if (!s || !*s) {
  743. parsing_error(p, "invalid address");
  744. return;
  745. }
  746. /* ttl */
  747. s = strchr(s, '/');
  748. if (s) {
  749. unsigned long value;
  750. *s++ = 0;
  751. if (parse_ul(p, &s, &value, 256) ||
  752. (*s && *s != '/')) {
  753. parsing_error(p, "invalid ttl");
  754. return;
  755. }
  756. c->c_ttl = value;
  757. c->c_mcast = 1;
  758. /* multiple groups */
  759. value = 1;
  760. if (*s++ == '/')
  761. if (parse_ul(p, &s, &value, 0) || *s) {
  762. parsing_error(p, "invalid number of multicast groups");
  763. return;
  764. }
  765. c->c_groups = value;
  766. }
  767. else
  768. c->c_groups = 1;
  769. }
  770. else if (p->pr_anynet) {
  771. c->c_nettype = sdp_net_x;
  772. c->c_addrtype = sdp_addr_x;
  773. c->c_address = r;
  774. c->c_ttl = 0;
  775. c->c_groups = 1;
  776. }
  777. else
  778. parsing_error(p, "invalid address");
  779. }
  780. /* -------------------------------------------------------------------------
  781. * Function parse_bandwidth() - parse an "b=" field
  782. *
  783. * Description:
  784. * This function parses an SDP bandwidth field.
  785. *
  786. * Parameters:
  787. * p - pointer to SDP parser object
  788. * r - pointer to record data
  789. * result - pointer to which parsed record is assigned
  790. */
  791. static void parse_bandwidth(sdp_parser_t *p, char *r, sdp_bandwidth_t **result)
  792. {
  793. /*
  794. bandwidth-fields = *("b=" bwtype ":" bandwidth CRLF)
  795. bwtype = token
  796. bandwidth = 1*(DIGIT)
  797. */
  798. /* NOTE: bwtype can also be like X-barf */
  799. sdp_bandwidth_e modifier;
  800. char *name;
  801. unsigned long value;
  802. name = token(&r, ":", TOKEN, SPACE TAB);
  803. if (name == NULL || parse_ul(p, &r, &value, 0)) {
  804. parsing_error(p, "invalid bandwidth");
  805. return;
  806. }
  807. if (su_casematch(name, "CT"))
  808. modifier = sdp_bw_ct, name = "CT";
  809. else if (su_casematch(name, "TIAS") == 1)
  810. modifier = sdp_bw_tias, name = "TIAS";
  811. else if (su_casematch(name, "AS") == 1)
  812. modifier = sdp_bw_as, name = "AS";
  813. else if (su_casematch(name, "RS") == 1)
  814. modifier = sdp_bw_rs, name = "RS";
  815. else if (su_casematch(name, "RR") == 1)
  816. modifier = sdp_bw_rr, name = "RR";
  817. else
  818. modifier = sdp_bw_x, name = "BW-X";
  819. if (STRICT(p))
  820. PARSE_CHECK_REST(p, r, "b");
  821. {
  822. PARSE_ALLOC(p, sdp_bandwidth_t, b);
  823. *result = b;
  824. b->b_modifier = modifier;
  825. b->b_modifier_name = name;
  826. b->b_value = value;
  827. }
  828. }
  829. /* -------------------------------------------------------------------------
  830. * Function parse_time() - parse an "t=" field
  831. *
  832. * Description:
  833. * This function parses an SDP time field.
  834. *
  835. * Parameters:
  836. * p - pointer to SDP parser object
  837. * r - pointer to record data
  838. * result - pointer to which parsed record is assigned
  839. */
  840. static void parse_time(sdp_parser_t *p, char *r, sdp_time_t **result)
  841. {
  842. /*
  843. time-fields = 1*( "t=" start-time SP stop-time
  844. *(CRLF repeat-fields) CRLF)
  845. [zone-adjustments CRLF]
  846. start-time = time / "0"
  847. stop-time = time / "0"
  848. time = POS-DIGIT 9*DIGIT
  849. ; Decimal representation of NTP time in
  850. ; seconds since 1900. The representation
  851. ; of NTP time is an unbounded length field
  852. ; containing at least 10 digits. Unlike the
  853. ; 64-bit representation used elsewhere, time
  854. ; in SDP does not wrap in the year 2036.
  855. */
  856. PARSE_ALLOC(p, sdp_time_t, t);
  857. *result = t;
  858. if (parse_ul(p, &r, &t->t_start, 0) ||
  859. parse_ul(p, &r, &t->t_stop, 0))
  860. parsing_error(p, "invalid time");
  861. else if (STRICT(p)) {
  862. PARSE_CHECK_REST(p, r, "t");
  863. }
  864. }
  865. /**
  866. * Parse an "r=" field
  867. *
  868. * The function parse_repeat() parses an SDP repeat field.
  869. *
  870. * @param p pointer to SDP parser object
  871. * @param r pointer to record data
  872. * @param result pointer to which parsed record is assigned
  873. *
  874. */
  875. static void parse_repeat(sdp_parser_t *p, char *d, sdp_repeat_t **result)
  876. {
  877. /*
  878. repeat-fields = %x72 "=" repeat-interval 2*(SP typed-time)
  879. repeat-interval = POS-DIGIT *DIGIT [fixed-len-time-unit]
  880. typed-time = 1*DIGIT [fixed-len-time-unit]
  881. fixed-len-time-unit = %x64 / %x68 / %x6d / %x73 ; "d" | "h" | "m" | "s"
  882. */
  883. unsigned long tt, *interval;
  884. size_t i;
  885. int n, N;
  886. char *s;
  887. sdp_repeat_t *r;
  888. int strict = STRICT(p);
  889. /** Count number of intervals */
  890. for (N = 0, s = d; *s; ) {
  891. if (!(is_posdigit(*s) || (!strict && (*s) == '0')))
  892. break;
  893. do { s++; } while (is_digit(*s));
  894. if (*s && strchr(strict ? "dhms" : "dhmsDHMS", *s))
  895. s++;
  896. N++;
  897. if (!(i = strict ? is_space(*s) : strspn(s, SPACE TAB)))
  898. break;
  899. s += i;
  900. }
  901. PARSE_CHECK_REST(p, s, "r");
  902. if (N < 2) {
  903. parsing_error(p, "invalid repeat");
  904. return;
  905. }
  906. if (!(r = su_salloc(p->pr_home, offsetof(sdp_repeat_t, r_offsets[N - 1])))) {
  907. parse_alloc_error(p, "sdp_repeat_t");
  908. return;
  909. }
  910. r->r_number_of_offsets = N - 2;
  911. r->r_offsets[N - 2] = 0;
  912. for (n = 0, interval = &r->r_interval; n < N; n++) {
  913. tt = strtoul(d, &d, 10);
  914. switch (*d) {
  915. case 'd': case 'D': tt *= 24;
  916. case 'h': case 'H': tt *= 60;
  917. case 'm': case 'M': tt *= 60;
  918. case 's': case 'S': d++;
  919. break;
  920. }
  921. interval[n] = tt;
  922. while (is_space(*d))
  923. d++;
  924. }
  925. *result = r;
  926. }
  927. /* -------------------------------------------------------------------------
  928. * Function parse_zone() - parse an "z=" field
  929. *
  930. * Description:
  931. * This function parses an SDP time zone field.
  932. *
  933. * Parameters:
  934. * p - pointer to SDP parser object
  935. * r - pointer to record data
  936. * result - pointer to which parsed record is assigned
  937. *
  938. */
  939. static void parse_zone(sdp_parser_t *p, char *r, sdp_zone_t **result)
  940. {
  941. char *s;
  942. size_t i;
  943. int n, N;
  944. sdp_zone_t *z;
  945. /*
  946. zone-adjustments = time space ["-"] typed-time
  947. *(space time space ["-"] typed-time)
  948. */
  949. /** Count number of timezones, check syntax */
  950. for (N = 0, s = r; *s;) {
  951. if (!(is_posdigit(*s) || (!STRICT(p) && (*s) == '0')))
  952. break;
  953. do { s++; } while (is_digit(*s));
  954. if (!(i = STRICT(p) ? is_space(*s) : strspn(s, SPACE TAB)))
  955. break;
  956. s += i;
  957. if (!(*s == '-' || is_posdigit(*s) || (!STRICT(p) && (*s) == '0')))
  958. break;
  959. do { s++; } while (is_digit(*s));
  960. if (*s && strchr("dhms", *s))
  961. s++;
  962. N++;
  963. if (!(i = STRICT(p) ? is_space(*s) : strspn(s, SPACE TAB)))
  964. break;
  965. s += i;
  966. }
  967. PARSE_CHECK_REST(p, s, "z");
  968. if (N < 1) {
  969. parsing_error(p, "invalid timezone");
  970. return;
  971. }
  972. if (!(z = su_salloc(p->pr_home, offsetof(sdp_zone_t, z_adjustments[N])))) {
  973. parse_alloc_error(p, "sdp_zone_t");
  974. return;
  975. }
  976. z->z_number_of_adjustments = N;
  977. for (n = 0; n < N; n++) {
  978. unsigned long at = strtoul(r, &r, 10);
  979. long offset = strtol(r, &r, 10);
  980. switch (*r) {
  981. case 'd': offset *= 24;
  982. case 'h': offset *= 60;
  983. case 'm': offset *= 60;
  984. case 's': r++;
  985. break;
  986. }
  987. z->z_adjustments[n].z_at = at;
  988. z->z_adjustments[n].z_offset = offset;
  989. }
  990. *result = z;
  991. }
  992. /* -------------------------------------------------------------------------
  993. * Function parse_key() - parse an "k=" field
  994. *
  995. * Description:
  996. * This function parses an SDP key field.
  997. *
  998. * Parameters:
  999. * p - pointer to SDP parser object
  1000. * r - pointer to record data
  1001. * result - pointer to which parsed record is assigned
  1002. *
  1003. */
  1004. static void parse_key(sdp_parser_t *p, char *r, sdp_key_t **result)
  1005. {
  1006. char *s;
  1007. /*
  1008. key-field = ["k=" key-type CRLF]
  1009. key-type = "prompt" |
  1010. "clear:" key-data |
  1011. "base64:" key-data |
  1012. "uri:" uri
  1013. key-data = email-safe | "~" | "
  1014. */
  1015. s = token(&r, ":", TOKEN, SPACE TAB);
  1016. if (!s) {
  1017. parsing_error(p, "invalid key method");
  1018. return;
  1019. }
  1020. {
  1021. PARSE_ALLOC(p, sdp_key_t, k);
  1022. if (result) *result = k;
  1023. /* These are defined as key-sensitive in RFC 4566 */
  1024. #define MATCH(s, tok) \
  1025. (STRICT(p) ? su_strmatch((s), (tok)) : su_casematch((s), (tok)))
  1026. if (MATCH(s, "clear"))
  1027. k->k_method = sdp_key_clear, k->k_method_name = "clear";
  1028. else if (MATCH(s, "base64"))
  1029. k->k_method = sdp_key_base64, k->k_method_name = "base64";
  1030. else if (MATCH(s, "uri"))
  1031. k->k_method = sdp_key_uri, k->k_method_name = "uri";
  1032. else if (MATCH(s, "prompt"))
  1033. k->k_method = sdp_key_prompt, k->k_method_name = "prompt";
  1034. else if (!STRICT(p))
  1035. k->k_method = sdp_key_x, k->k_method_name = s;
  1036. else {
  1037. parsing_error(p, "invalid key method");
  1038. return;
  1039. }
  1040. k->k_material = r;
  1041. }
  1042. }
  1043. /* -------------------------------------------------------------------------
  1044. * Function parse_session_attr() - parse a session "a=" field
  1045. *
  1046. * Description:
  1047. * This function parses an SDP attribute field regarding whole session.
  1048. *
  1049. * Parameters:
  1050. * p - pointer to SDP parser object
  1051. * r - pointer to record data
  1052. * result - pointer to which parsed record is assigned
  1053. */
  1054. static void parse_session_attr(sdp_parser_t *p, char *r, sdp_attribute_t **result)
  1055. {
  1056. /*
  1057. attribute-fields = *("a=" attribute CRLF)
  1058. attribute = (att-field ":" att-value) / att-field
  1059. att-field = token
  1060. att-value = byte-string
  1061. */
  1062. char *name = NULL, *value = NULL;
  1063. if (!(name = token(&r, ":", TOKEN, SPACE TAB))) {
  1064. parsing_error(p,"invalid attribute name");
  1065. return;
  1066. }
  1067. if (*r)
  1068. value = r;
  1069. else
  1070. PARSE_CHECK_REST(p, r, "a");
  1071. if (su_casematch(name, "charset")) {
  1072. p->pr_session->sdp_charset = value;
  1073. return;
  1074. }
  1075. if (p->pr_mode_manual)
  1076. ;
  1077. else if (su_casematch(name, "inactive"))
  1078. p->pr_session_mode = sdp_inactive;
  1079. else if (su_casematch(name, "sendonly"))
  1080. p->pr_session_mode = sdp_sendonly;
  1081. else if (su_casematch(name, "recvonly"))
  1082. p->pr_session_mode = sdp_recvonly;
  1083. else if (su_casematch(name, "sendrecv"))
  1084. p->pr_session_mode = sdp_sendrecv;
  1085. {
  1086. PARSE_ALLOC(p, sdp_attribute_t, a);
  1087. *result = a;
  1088. a->a_name = name;
  1089. a->a_value = value;
  1090. }
  1091. }
  1092. /* -------------------------------------------------------------------------
  1093. * Function parse_media() - parse an "m=" field
  1094. *
  1095. * Description:
  1096. * This function parses an SDP media field.
  1097. *
  1098. * Parameters:
  1099. * p - pointer to SDP parser object
  1100. * r - pointer to record data
  1101. * result - pointer to which parsed record is assigned
  1102. */
  1103. static void parse_media(sdp_parser_t *p, char *r, sdp_media_t **result)
  1104. {
  1105. /*
  1106. media-descriptions = *( media-field
  1107. information-field
  1108. *(connection-field)
  1109. bandwidth-fields
  1110. key-field
  1111. attribute-fields )
  1112. media-field = "m=" media space port ["/" integer]
  1113. space proto 1*(space fmt) CRLF
  1114. media = token
  1115. ;typically "audio", "video", "application"
  1116. ;or "data" or "text"
  1117. fmt = token
  1118. ;typically an RTP payload type for audio
  1119. ;and video media
  1120. proto = token *("/" token)
  1121. ;typically "RTP/AVP" or "udp" for IP4
  1122. port = 1*(DIGIT)
  1123. ;should in the range "1024" to "65535" inclusive
  1124. */
  1125. char *s;
  1126. unsigned long value;
  1127. PARSE_ALLOC(p, sdp_media_t, m);
  1128. *result = m;
  1129. m->m_mode = sdp_sendrecv;
  1130. s = token(&r, SPACE, TOKEN, NULL);
  1131. if (!s) {
  1132. parsing_error(p, "m= invalid media field");
  1133. return;
  1134. }
  1135. sdp_media_type(m, s);
  1136. /* Accept m=* in configuration file */
  1137. if (p->pr_config && m->m_type == sdp_media_any) {
  1138. r += strspn(r, SPACE TAB);
  1139. if (r[0] == '\0') {
  1140. m->m_proto = sdp_proto_any, m->m_proto_name = "*";
  1141. return;
  1142. }
  1143. }
  1144. if (parse_ul(p, &r, &value, 0)) {
  1145. parsing_error(p, "m= invalid port number");
  1146. return;
  1147. }
  1148. m->m_port = value;
  1149. if (*r == '/') {
  1150. r++;
  1151. if (parse_ul(p, &r, &value, 0)) {
  1152. parsing_error(p, "m= invalid port specification");
  1153. return;
  1154. }
  1155. m->m_number_of_ports = value;
  1156. }
  1157. s = token(&r, SPACE, "/" TOKEN, SPACE);
  1158. if (s == NULL) {
  1159. parsing_error(p, "m= missing protocol");
  1160. return;
  1161. }
  1162. if (!STRICT(p) && su_casematch(s, "RTP"))
  1163. m->m_proto = sdp_proto_rtp, m->m_proto_name = "RTP/AVP";
  1164. else
  1165. sdp_media_transport(m, s);
  1166. /* RTP format list */
  1167. if (*r && sdp_media_has_rtp(m)) {
  1168. parse_payload(p, r, &m->m_rtpmaps);
  1169. return;
  1170. }
  1171. /* "normal" format list */
  1172. if (*r) {
  1173. sdp_list_t **fmt = &m->m_format;
  1174. while (r && *r) {
  1175. PARSE_ALLOC(p, sdp_list_t, l);
  1176. *fmt = l;
  1177. l->l_text = token(&r, SPACE TAB, TOKEN, SPACE TAB);
  1178. if (!l->l_text) {
  1179. parsing_error(p, "m= invalid");
  1180. return;
  1181. }
  1182. fmt = &l->l_next;
  1183. }
  1184. }
  1185. }
  1186. /** Set media type */
  1187. void sdp_media_type(sdp_media_t *m, char const *s)
  1188. {
  1189. if (su_strmatch(s, "*"))
  1190. m->m_type = sdp_media_any, m->m_type_name = "*";
  1191. else if (su_casematch(s, "audio"))
  1192. m->m_type = sdp_media_audio, m->m_type_name = "audio";
  1193. else if (su_casematch(s, "video"))
  1194. m->m_type = sdp_media_video, m->m_type_name = "video";
  1195. else if (su_casematch(s, "application"))
  1196. m->m_type = sdp_media_application, m->m_type_name = "application";
  1197. else if (su_casematch(s, "data"))
  1198. m->m_type = sdp_media_data, m->m_type_name = "data";
  1199. else if (su_casematch(s, "control"))
  1200. m->m_type = sdp_media_control, m->m_type_name = "control";
  1201. else if (su_casematch(s, "message"))
  1202. m->m_type = sdp_media_message, m->m_type_name = "message";
  1203. else if (su_casematch(s, "image"))
  1204. m->m_type = sdp_media_image, m->m_type_name = "image";
  1205. else if (su_casematch(s, "red"))
  1206. m->m_type = sdp_media_red, m->m_type_name = "red";
  1207. else if (su_casematch(s, "text"))
  1208. m->m_type = sdp_media_text, m->m_type_name = "text";
  1209. else
  1210. m->m_type = sdp_media_x, m->m_type_name = s;
  1211. }
  1212. /** Set transport protocol.
  1213. *
  1214. * Set the @m->m_proto to a well-known protocol type as
  1215. * well as canonize case of @a m_proto_name.
  1216. */
  1217. void sdp_media_transport(sdp_media_t *m, char const *s)
  1218. {
  1219. if (m == NULL || s == NULL)
  1220. ;
  1221. else if (su_strmatch(s, "*"))
  1222. m->m_proto = sdp_proto_any, m->m_proto_name = "*";
  1223. else if (su_casematch(s, "RTP/AVP"))
  1224. m->m_proto = sdp_proto_rtp, m->m_proto_name = "RTP/AVP";
  1225. else if (su_casematch(s, "RTP/SAVP"))
  1226. m->m_proto = sdp_proto_srtp, m->m_proto_name = "RTP/SAVP";
  1227. else if (su_casematch(s, "UDP/TLS/RTP/SAVP"))
  1228. m->m_proto = sdp_proto_srtp, m->m_proto_name = "RTP/SAVP";
  1229. else if (su_casematch(s, "RTP/SAVPF"))
  1230. m->m_proto = sdp_proto_extended_srtp, m->m_proto_name = "RTP/SAVPF";
  1231. else if (su_casematch(s, "UDP/TLS/RTP/SAVPF"))
  1232. m->m_proto = sdp_proto_extended_srtp, m->m_proto_name = "UDP/TLS/RTP/SAVPF";
  1233. else if (su_casematch(s, "RTP/AVPF"))
  1234. m->m_proto = sdp_proto_extended_rtp, m->m_proto_name = "RTP/AVPF";
  1235. else if (su_casematch(s, "UDP/RTP/AVPF"))
  1236. m->m_proto = sdp_proto_extended_rtp, m->m_proto_name = "UDP/RTP/AVPF";
  1237. else if (su_casematch(s, "udptl"))
  1238. /* Lower case - be compatible with people living by T.38 examples */
  1239. m->m_proto = sdp_proto_udptl, m->m_proto_name = "udptl";
  1240. else if (su_casematch(s, "TCP/MSRP"))
  1241. m->m_proto = sdp_proto_msrp, m->m_proto_name = "TCP/MSRP";
  1242. else if (su_casematch(s, "TCP/TLS/MSRP"))
  1243. m->m_proto = sdp_proto_msrps, m->m_proto_name = "TCP/TLS/MSRP";
  1244. else if (su_casematch(s, "UDP"))
  1245. m->m_proto = sdp_proto_udp, m->m_proto_name = "UDP";
  1246. else if (su_casematch(s, "TCP"))
  1247. m->m_proto = sdp_proto_tcp, m->m_proto_name = "TCP";
  1248. else if (su_casematch(s, "TLS"))
  1249. m->m_proto = sdp_proto_tls, m->m_proto_name = "TLS";
  1250. else
  1251. m->m_proto = sdp_proto_x, m->m_proto_name = s;
  1252. }
  1253. /** Check if media uses RTP as its transport protocol. */
  1254. int sdp_media_has_rtp(sdp_media_t const *m)
  1255. {
  1256. return m && (m->m_proto == sdp_proto_rtp || m->m_proto == sdp_proto_srtp || m->m_proto == sdp_proto_extended_srtp || m->m_proto == sdp_proto_extended_rtp);
  1257. }
  1258. #define RTPMAP(pt, encoding, rate, params) \
  1259. { sizeof(sdp_rtpmap_t), NULL, encoding, rate, (char *)params, NULL, 1, pt, 0 }
  1260. /* rtpmaps for well-known codecs */
  1261. static sdp_rtpmap_t const
  1262. sdp_rtpmap_pcmu = RTPMAP(0, "PCMU", 8000, 0),
  1263. sdp_rtpmap_1016 = RTPMAP(1, "1016", 8000, 0),
  1264. sdp_rtpmap_g721 = RTPMAP(2, "G721", 8000, 0),
  1265. sdp_rtpmap_gsm = RTPMAP(3, "GSM", 8000, 0),
  1266. sdp_rtpmap_g723 = RTPMAP(4, "G723", 8000, 0),
  1267. sdp_rtpmap_dvi4_8000 = RTPMAP(5, "DVI4", 8000, 0),
  1268. sdp_rtpmap_dvi4_16000 = RTPMAP(6, "DVI4", 16000, 0),
  1269. sdp_rtpmap_lpc = RTPMAP(7, "LPC", 8000, 0),
  1270. sdp_rtpmap_pcma = RTPMAP(8, "PCMA", 8000, 0),
  1271. sdp_rtpmap_g722 = RTPMAP(9, "G722", 8000, 0),
  1272. sdp_rtpmap_l16_2 = RTPMAP(10, "L16", 44100, "2"),
  1273. sdp_rtpmap_l16 = RTPMAP(11, "L16", 44100, 0),
  1274. sdp_rtpmap_qcelp = RTPMAP(12, "QCELP", 8000, 0),
  1275. sdp_rtpmap_cn = RTPMAP(13, "CN", 8000, 0),
  1276. sdp_rtpmap_mpa = RTPMAP(14, "MPA", 90000, 0),
  1277. sdp_rtpmap_g728 = RTPMAP(15, "G728", 8000, 0),
  1278. sdp_rtpmap_dvi4_11025 = RTPMAP(16, "DVI4", 11025, 0),
  1279. sdp_rtpmap_dvi4_22050 = RTPMAP(17, "DVI4", 22050, 0),
  1280. sdp_rtpmap_g729 = RTPMAP(18, "G729", 8000, 0),
  1281. sdp_rtpmap_reserved_cn = RTPMAP(19, "CN", 8000, 0),
  1282. /* video codecs */
  1283. sdp_rtpmap_celb = RTPMAP(25, "CelB", 90000, 0),
  1284. sdp_rtpmap_jpeg = RTPMAP(26, "JPEG", 90000, 0),
  1285. sdp_rtpmap_nv = RTPMAP(28, "nv", 90000, 0),
  1286. sdp_rtpmap_h261 = RTPMAP(31, "H261", 90000, 0),
  1287. sdp_rtpmap_mpv = RTPMAP(32, "MPV", 90000, 0),
  1288. sdp_rtpmap_mp2t = RTPMAP(33, "MP2T", 90000, 0),
  1289. sdp_rtpmap_h263 = RTPMAP(34, "H263", 90000, 0);
  1290. /** Table of rtpmap structures by payload type numbers.
  1291. *
  1292. * The table of reserved payload numbers is constructed from @RFC3551
  1293. * and @RFC1890. Note the clock rate of G722.
  1294. *
  1295. * Use sdp_rtpmap_dup() to copy these structures.
  1296. */
  1297. sdp_rtpmap_t const * const sdp_rtpmap_well_known[128] =
  1298. {
  1299. &sdp_rtpmap_pcmu, /* 0 */
  1300. &sdp_rtpmap_1016, /* 1 */
  1301. &sdp_rtpmap_g721, /* 2 */
  1302. &sdp_rtpmap_gsm, /* 3 */
  1303. &sdp_rtpmap_g723, /* 4 */
  1304. &sdp_rtpmap_dvi4_8000, /* 5 */
  1305. &sdp_rtpmap_dvi4_16000, /* 6 */
  1306. &sdp_rtpmap_lpc, /* 7 */
  1307. &sdp_rtpmap_pcma, /* 8 */
  1308. &sdp_rtpmap_g722, /* 9 */
  1309. &sdp_rtpmap_l16_2, /* 10 */
  1310. &sdp_rtpmap_l16, /* 11 */
  1311. &sdp_rtpmap_qcelp, /* 12 */
  1312. &sdp_rtpmap_cn, /* 13 */
  1313. &sdp_rtpmap_mpa, /* 14 */
  1314. &sdp_rtpmap_g728, /* 15 */
  1315. &sdp_rtpmap_dvi4_11025, /* 16 */
  1316. &sdp_rtpmap_dvi4_22050, /* 17 */
  1317. &sdp_rtpmap_g729, /* 18 */
  1318. &sdp_rtpmap_reserved_cn, /* 19 */
  1319. NULL, /* 20 */
  1320. NULL, /* 21 */
  1321. NULL, /* 22 */
  1322. NULL, /* 23 */
  1323. NULL, /* 24 */
  1324. &sdp_rtpmap_celb, /* 25 */
  1325. &sdp_rtpmap_jpeg, /* 26 */
  1326. NULL, /* 27 */
  1327. &sdp_rtpmap_nv, /* 28 */
  1328. NULL, /* 29 */
  1329. NULL, /* 30 */
  1330. &sdp_rtpmap_h261, /* 31 */
  1331. &sdp_rtpmap_mpv, /* 32 */
  1332. &sdp_rtpmap_mp2t, /* 33 */
  1333. &sdp_rtpmap_h263, /* 34 */
  1334. NULL,
  1335. };
  1336. /**
  1337. * The function parse_payload() parses an RTP payload type list, and
  1338. * creates an rtpmap structure for each payload type.
  1339. *
  1340. * @param p pointer to SDP parser object
  1341. * @param r pointer to record data
  1342. * @param result pointer to which parsed record is assigned
  1343. */
  1344. static void parse_payload(sdp_parser_t *p, char *r, sdp_rtpmap_t **result)
  1345. {
  1346. while (*r) {
  1347. unsigned long value;
  1348. if (parse_ul(p, &r, &value, 128) == 0) {
  1349. PARSE_ALLOC(p, sdp_rtpmap_t, rm);
  1350. assert(0 <= value && value < 128);
  1351. *result = rm; result = &rm->rm_next;
  1352. if (sdp_rtpmap_well_known[value]) {
  1353. *rm = *sdp_rtpmap_well_known[value];
  1354. }
  1355. else {
  1356. rm->rm_predef = 1;
  1357. rm->rm_pt = value;
  1358. rm->rm_encoding = "";
  1359. rm->rm_rate = 0;
  1360. }
  1361. }
  1362. else if (p->pr_config && r[0] == '*' && (r[1] == ' ' || r[1] == '\0')) {
  1363. PARSE_ALLOC(p, sdp_rtpmap_t, rm);
  1364. *result = rm;
  1365. rm->rm_predef = 1;
  1366. rm->rm_any = 1;
  1367. rm->rm_encoding = "*";
  1368. rm->rm_rate = 0;
  1369. return;
  1370. }
  1371. else {
  1372. parsing_error(p, "m= invalid format for RTP/AVT");
  1373. return;
  1374. }
  1375. }
  1376. }
  1377. /* -------------------------------------------------------------------------
  1378. * Function parse_media_attr() - parse a media-specific "a=" field
  1379. *
  1380. * Description:
  1381. * This function parses a media-specific attribute field.
  1382. *
  1383. * Parameters:
  1384. * p - pointer to SDP parser object
  1385. * r - pointer to record data
  1386. * result - pointer to which parsed record is assigned
  1387. */
  1388. static void parse_media_attr(sdp_parser_t *p, char *r, sdp_media_t *m,
  1389. sdp_attribute_t **result)
  1390. {
  1391. /*
  1392. attribute-fields = *("a=" attribute CRLF)
  1393. attribute = (att-field ":" att-value) / att-field
  1394. att-field = token
  1395. att-value = byte-string
  1396. a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]
  1397. a=fmtp:<payload type> <parameters>
  1398. */
  1399. int rtp = sdp_media_has_rtp(m);
  1400. char *name = NULL, *value = NULL;
  1401. int n;
  1402. if (!(name = token(&r, ":", TOKEN, SPACE TAB))) {
  1403. parsing_error(p,"invalid attribute name");
  1404. return;
  1405. }
  1406. if (*r)
  1407. value = r;
  1408. else
  1409. PARSE_CHECK_REST(p, r, "a");
  1410. if (p->pr_mode_manual)
  1411. ;
  1412. else if (m->m_port == 0 || su_casematch(name, "inactive")) {
  1413. m->m_mode = sdp_inactive;
  1414. return;
  1415. }
  1416. else if (su_casematch(name, "sendonly")) {
  1417. m->m_mode = sdp_sendonly;
  1418. return;
  1419. }
  1420. else if (su_casematch(name, "recvonly")) {
  1421. m->m_mode = sdp_recvonly;
  1422. return;
  1423. }
  1424. else if (su_casematch(name, "sendrecv")) {
  1425. m->m_mode = sdp_sendrecv;
  1426. return;
  1427. }
  1428. if (rtp && su_casematch(name, "rtpmap")) {
  1429. if ((n = parse_rtpmap(p, r, m)) == 0 || n < -1)
  1430. return;
  1431. }
  1432. else if (rtp && su_casematch(name, "fmtp")) {
  1433. if ((n = parse_fmtp(p, r, m)) == 0 || n < -1)
  1434. return;
  1435. }
  1436. else {
  1437. PARSE_ALLOC(p, sdp_attribute_t, a);
  1438. *result = a;
  1439. a->a_name = name;
  1440. a->a_value = value;
  1441. }
  1442. }
  1443. /** Parse rtpmap attribute.
  1444. *
  1445. * a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]
  1446. */
  1447. static int parse_rtpmap(sdp_parser_t *p, char *r, sdp_media_t *m)
  1448. {
  1449. unsigned long pt, rate;
  1450. char *encoding, *params;
  1451. sdp_rtpmap_t *rm;
  1452. int strict = STRICT(p);
  1453. if (parse_ul(p, &r, &pt, 128)) {
  1454. if (strict)
  1455. parsing_error(p, "a=rtpmap: invalid payload type");
  1456. return -1;
  1457. }
  1458. for (rm = m->m_rtpmaps; rm; rm = rm->rm_next)
  1459. if (rm->rm_pt == pt)
  1460. break;
  1461. if (!rm) {
  1462. if (strict)
  1463. parsing_error(p, "a=rtpmap:%lu: unknown payload type", pt);
  1464. return -1;
  1465. }
  1466. encoding = token(&r, "/", TOKEN, NULL);
  1467. if (!r) {
  1468. parsing_error(p, "a=rtpmap:%lu: missing <clock rate>", pt);
  1469. return -2;
  1470. }
  1471. if (parse_ul(p, &r, &rate, 0)) {
  1472. parsing_error(p, "a=rtpmap:%lu %s: invalid <clock rate>", pt, encoding);
  1473. return -2;
  1474. }
  1475. if (*r == '/')
  1476. params = ++r;
  1477. else
  1478. params = 0;
  1479. rm->rm_predef = 0;
  1480. rm->rm_encoding = encoding;
  1481. rm->rm_rate = rate;
  1482. rm->rm_params = params;
  1483. return 0;
  1484. }
  1485. /** Parse fmtp attribute.
  1486. *
  1487. * a=fmtp:<payload type> <parameters>
  1488. */
  1489. static int parse_fmtp(sdp_parser_t *p, char *r, sdp_media_t *m)
  1490. {
  1491. unsigned long pt;
  1492. sdp_rtpmap_t *rm;
  1493. int strict = STRICT(p);
  1494. if (parse_ul(p, &r, &pt, 128)) {
  1495. if (strict)
  1496. parsing_error(p, "a=rtpmap: invalid payload type");
  1497. return -1;
  1498. }
  1499. for (rm = m->m_rtpmaps; rm; rm = rm->rm_next)
  1500. if (rm->rm_pt == pt)
  1501. break;
  1502. if (!rm) {
  1503. if (strict)
  1504. parsing_error(p, "a=fmtp:%lu: unknown payload type", pt);
  1505. return -1;
  1506. }
  1507. rm->rm_fmtp = r;
  1508. return 0;
  1509. }
  1510. /* -------------------------------------------------------------------------
  1511. * Function parse_descs() - parse media descriptors
  1512. *
  1513. * Description:
  1514. * This function parses media descriptors at the end of SDP message.
  1515. *
  1516. * Parameters:
  1517. * p - pointer to SDP parser object
  1518. * record - pointer to first media field
  1519. * message - pointer to rest
  1520. * medias - pointer to which parsed media structures are assigned
  1521. */
  1522. static void parse_descs(sdp_parser_t *p,
  1523. char *record,
  1524. char *message,
  1525. sdp_media_t **medias)
  1526. {
  1527. char *rest;
  1528. const char *strip;
  1529. sdp_media_t *m = NULL;
  1530. sdp_connection_t **connections = NULL;
  1531. sdp_bandwidth_t **bandwidths = NULL;
  1532. sdp_attribute_t **attributes = NULL;
  1533. if (!STRICT(p))
  1534. strip = SPACE TAB; /* skip initial whitespace */
  1535. else
  1536. strip = "";
  1537. for (;
  1538. record && p->pr_ok;
  1539. record = next(&message, CRLF, strip)) {
  1540. char field = record[0];
  1541. if (strlen(record) < 2) {
  1542. return;
  1543. }
  1544. rest = record + 2; rest += strspn(rest, strip);
  1545. if (record[1] == '=') switch (field) {
  1546. case 'c':
  1547. assert(connections);
  1548. parse_connection(p, rest, connections);
  1549. connections = &(*connections)->c_next;
  1550. break;
  1551. case 'b':
  1552. assert(bandwidths);
  1553. parse_bandwidth(p, rest, bandwidths);
  1554. bandwidths = &(*bandwidths)->b_next;
  1555. break;
  1556. case 'i':
  1557. parse_information(p, rest, &m->m_information);
  1558. break;
  1559. case 'k':
  1560. parse_key(p, rest, &m->m_key);
  1561. break;
  1562. case 'a':
  1563. assert(attributes);
  1564. parse_media_attr(p, rest, m, attributes);
  1565. if (*attributes)
  1566. attributes = &(*attributes)->a_next;
  1567. break;
  1568. case 'm':
  1569. parse_media(p, rest, medias);
  1570. m = *medias;
  1571. if (m) {
  1572. m->m_mode = p->pr_session_mode;
  1573. medias = &m->m_next;
  1574. connections = &m->m_connections;
  1575. bandwidths = &m->m_bandwidths;
  1576. attributes = &m->m_attributes;
  1577. }
  1578. }
  1579. }
  1580. }
  1581. static void parse_text_list(sdp_parser_t *p, char *r, sdp_list_t **result)
  1582. {
  1583. PARSE_ALLOC(p, sdp_list_t, l);
  1584. *result = l;
  1585. l->l_text = r;
  1586. }
  1587. /*
  1588. * parse_ul: parse an unsigned long
  1589. */
  1590. static int parse_ul(sdp_parser_t *p, char **r,
  1591. unsigned long *result, unsigned long max)
  1592. {
  1593. char *ul = *r;
  1594. ul += strspn(ul, SPACE TAB);
  1595. *result = strtoul(ul, r, 10);
  1596. if (ul != *r && !(max && max <= *result)) {
  1597. *r += strspn(*r, SPACE TAB);
  1598. return 0;
  1599. }
  1600. return -1;
  1601. }
  1602. #if !HAVE_STRTOULL
  1603. #if !((defined(WIN32) || defined(_WIN32)) && (_MSC_VER >= 1800))
  1604. unsigned long long strtoull(char const *string, char **return_end, int base);
  1605. #endif
  1606. #endif
  1607. /*
  1608. * parse_ull: parse an unsigned long long
  1609. */
  1610. static int parse_ull(sdp_parser_t *p, char **r,
  1611. uint64_t *result, uint64_t max)
  1612. {
  1613. unsigned long long ull;
  1614. char *s = *r;
  1615. s += strspn(s, SPACE TAB);
  1616. ull = strtoull(s, r, 10);
  1617. if (s != *r && !(max && max <= ull)) {
  1618. *result = (uint64_t)ull;
  1619. *r += strspn(*r, SPACE TAB);
  1620. return 0;
  1621. }
  1622. return -1;
  1623. }
  1624. static char *token(char **message,
  1625. const char *sep,
  1626. const char *legal,
  1627. const char *strip)
  1628. {
  1629. size_t n;
  1630. char *retval = *message;
  1631. if (strip)
  1632. retval += strspn(retval, strip);
  1633. if (legal)
  1634. n = strspn(retval, legal);
  1635. else
  1636. n = strcspn(retval, sep);
  1637. if (n == 0)
  1638. return NULL;
  1639. if (retval[n]) {
  1640. retval[n++] = '\0';
  1641. n += strspn(retval + n, sep);
  1642. }
  1643. *message = retval + n;
  1644. if (*retval == '\0')
  1645. return NULL;
  1646. return retval;
  1647. }
  1648. static char *next(char **message, const char *sep, const char *strip)
  1649. {
  1650. size_t n;
  1651. char *retval = *message;
  1652. if (strip[0])
  1653. retval += strspn(retval, strip);
  1654. n = strcspn(retval, sep);
  1655. if (n == 0)
  1656. return NULL;
  1657. if (retval[n]) {
  1658. retval[n++] = '\0';
  1659. n += strspn(retval + n, sep);
  1660. }
  1661. *message = retval + n;
  1662. if (*retval == '\0')
  1663. return NULL;
  1664. return retval;
  1665. }
  1666. static int parsing_error(sdp_parser_t *p, char const *fmt, ...)
  1667. {
  1668. va_list ap;
  1669. va_start(ap, fmt);
  1670. memset(p->pr_error, 0, sizeof(p->pr_error));
  1671. vsnprintf(p->pr_error, sizeof(p->pr_error), fmt, ap);
  1672. va_end(ap);
  1673. p->pr_ok = 0;
  1674. return -1;
  1675. }
  1676. static void parse_alloc_error(sdp_parser_t *p, const char *typename)
  1677. {
  1678. parsing_error(p, "memory exhausted (while allocating memory for %s)",
  1679. typename);
  1680. }