sip-options.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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. /**@page sip-options Query SIP OPTIONS
  25. *
  26. * @section synopsis Synopsis
  27. * <tt>sip-options [OPTIONS] target-uri </tt>
  28. *
  29. * @section description Description
  30. * The @em sip-options utility sends a SIP OPTIONS request (or any other SIP
  31. * request) to a SIP server.
  32. *
  33. * The @em sip-options tool will print out status line and interesting headers
  34. * from the response, excluding From, Via, Call-ID, and CSeq. The message
  35. * body is also printed.
  36. *
  37. * @section options Command Line Options
  38. * The @e options utility accepts following command line options:
  39. * <dl>
  40. * <dt>-m url | --contact=url | --bind=url</dt>
  41. * <dd>Specifies the SIP URL to which the @em options utility binds.
  42. * </dd>
  43. * <dt>--1XX | -1</dt>
  44. * <dd>Print also preliminary responses. If this option is not present,
  45. * preliminary responses are silently discarded.
  46. * </dd>
  47. * <dt>--all | -a</dt>
  48. * <dd>All SIP headers will be printed. If the --all option is given,
  49. * the @em options utility also prints @b From, @b Via, @b Call-ID or
  50. * @b CSeq headers.
  51. * </dd>
  52. * <dt>--from=url</dt>
  53. * <dd>Specifies the @b From header. Unless this option is used or the
  54. * environment variable @c SIPADDRESS is set, local Contact URL is used
  55. * as @b From header as well.
  56. * </dd>
  57. * <dt>--mf=n</dt>
  58. * <dd>Specify the initial Max-Forwards count (defaults to 70, stack default).
  59. * </dd>
  60. * <dt>--method=s</dt>
  61. * <dd>Specify the request method (defaults to OPTIONS).
  62. * </dd>
  63. * <dt>--extra | -x/dt>
  64. * <dd>Read extra headers (and optionally a message body) from the standard
  65. * input
  66. * </dd>
  67. * </dl>
  68. *
  69. * @section return Return Codes
  70. * <table>
  71. * <tr><td>0<td>when successful (a 2XX-series response is received)
  72. * <tr><td>1<td>when unsuccessful (a 3XX..6XX-series response is received)
  73. * <tr><td>2<td>initialization failure
  74. * </table>
  75. *
  76. * @section examples Examples
  77. * You want to query supported features of sip:essip00net.nokia.com:
  78. * @code
  79. * $ options sip:essip00net.nokia.com
  80. * @endcode
  81. *
  82. * @section environment Environment
  83. * #SIPADDRESS, #sip_proxy, #NTA_DEBUG, #TPORT_DEBUG, #TPORT_LOG.
  84. *
  85. * @section bugs Reporting Bugs
  86. * Report bugs to <sofia-sip-devel@lists.sourceforge.net>.
  87. *
  88. * @section author Author
  89. * Written by Pekka Pessi <pekka -dot pessi -at- nokia -dot- com>
  90. *
  91. * @section copyright Copyright
  92. * Copyright (C) 2005 Nokia Corporation.
  93. *
  94. * This program is free software; see the source for copying conditions.
  95. * There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
  96. * PARTICULAR PURPOSE.
  97. */
  98. #include "config.h"
  99. #include <stddef.h>
  100. #include <stdlib.h>
  101. #include <string.h>
  102. #include <stdio.h>
  103. #include <assert.h>
  104. typedef struct context_s context_t;
  105. #define NTA_OUTGOING_MAGIC_T context_t
  106. #include <sofia-sip/nta.h>
  107. #include <sofia-sip/sip_header.h>
  108. #include <sofia-sip/sip_tag.h>
  109. #include <sofia-sip/sl_utils.h>
  110. #include <sofia-sip/sip_util.h>
  111. #include <sofia-sip/auth_client.h>
  112. #include <sofia-sip/tport_tag.h>
  113. struct context_s {
  114. su_home_t c_home[1];
  115. su_root_t *c_root;
  116. nta_agent_t *c_agent;
  117. url_t *c_proxy;
  118. char const *c_username;
  119. char const *c_password;
  120. nta_leg_t *c_leg;
  121. nta_outgoing_t *c_orq;
  122. auth_client_t *c_proxy_auth;
  123. auth_client_t *c_auth;
  124. unsigned c_proxy_auth_retries;
  125. unsigned c_auth_retries;
  126. int c_all;
  127. int c_pre;
  128. int c_retval;
  129. };
  130. char const name[] = "sip-options";
  131. static
  132. void usage(int rc)
  133. {
  134. fprintf(rc ? stderr : stdout,
  135. "usage: %s OPTIONS url [extra-file]\n"
  136. "where OPTIONS are\n"
  137. " --mf=count | --max-forwards=count\n"
  138. " --contact=url | --bind=url | -m=url\n"
  139. " --from=url\n"
  140. " --ua=user-agent\n"
  141. " --all | -a\n"
  142. " --1XX | -1\n"
  143. " --x | --extra \n",
  144. name);
  145. exit(rc);
  146. }
  147. #include "apps_utils.h"
  148. static int response_to_options(context_t *context,
  149. nta_outgoing_t *oreq,
  150. sip_t const *sip);
  151. static char *readfile(FILE *f);
  152. int main(int argc, char *argv[])
  153. {
  154. su_home_t *home;
  155. context_t context[1] = {{{SU_HOME_INIT(context)}}};
  156. char
  157. *extra = NULL,
  158. *o_bind = "sip:*:*",
  159. *o_from = getenv("SIPADDRESS"),
  160. *o_http_proxy = NULL,
  161. *o_max_forwards = NULL,
  162. *o_method = NULL,
  163. *o_to = NULL;
  164. char *s, *v;
  165. sip_method_t method = sip_method_options;
  166. #define MATCH(s, o) \
  167. ((strncmp(s, o, strlen(o)) == 0))
  168. #define MATCH1(s, o) \
  169. ((strncmp(s, o, strlen(o)) == 0) && \
  170. (v = (s[strlen(o)] ? s + strlen(o) : argv++[1])))
  171. #define MATCH2(s, o) \
  172. ((strncmp(s, o, strlen(o)) == 0) && \
  173. (s[strlen(o)] == '=' || s[strlen(o)] == '\0') && \
  174. (v = s[strlen(o)] ? s + strlen(o) + 1 : argv++[1]))
  175. while ((s = argv++[1])) {
  176. if (!MATCH(s, "-")) { o_to = s; break; }
  177. else if (strcmp(s, "") == 0) { o_to = argv++[1]; break; }
  178. else if (MATCH(s, "-a") || MATCH(s, "--all"))
  179. { context->c_all = 1; }
  180. else if (MATCH(s, "-x") || MATCH(s, "--extra"))
  181. { extra = "-"; }
  182. else if (MATCH(s, "-1") || MATCH(s, "--1XX"))
  183. { context->c_pre = 1; }
  184. else if (MATCH2(s, "--mf")) { o_max_forwards = v; }
  185. else if (MATCH2(s, "--http-proxy")) { o_http_proxy = v; }
  186. else if (MATCH2(s, "--max-forwards")) { o_max_forwards = v; }
  187. else if (MATCH2(s, "--bind")) { o_bind = v; }
  188. else if (MATCH1(s, "-m")) { o_bind = v; }
  189. else if (MATCH2(s, "--contact")){ o_bind = v; }
  190. else if (MATCH2(s, "--from")) { o_from = v; }
  191. else if (MATCH2(s, "--method")) { o_method = v; }
  192. else if (MATCH(s, "--help")) { usage(0); }
  193. else
  194. usage(2);
  195. }
  196. if (!o_to)
  197. usage(2);
  198. if (argv[1])
  199. extra = argv++[1];
  200. su_init();
  201. su_home_init(home = context->c_home);
  202. context->c_root = su_root_create(context);
  203. context->c_retval = 2;
  204. if (context->c_root) {
  205. url_string_t *r_uri;
  206. context->c_agent =
  207. nta_agent_create(context->c_root,
  208. URL_STRING_MAKE(o_bind),
  209. NULL, NULL, /* Ignore incoming messages */
  210. TPTAG_HTTP_CONNECT(o_http_proxy),
  211. TAG_END());
  212. if (context->c_agent) {
  213. sip_addr_t *from, *to;
  214. sip_contact_t const *m = nta_agent_contact(context->c_agent);
  215. to = sip_to_create(home, (url_string_t *)o_to);
  216. if (o_from)
  217. from = sip_from_make(home, o_from);
  218. else
  219. from = sip_from_create(home, (url_string_t const *)m->m_url);
  220. if (!from) {
  221. fprintf(stderr, "%s: no valid From address\n", name);
  222. exit(2);
  223. }
  224. tag_from_header(context->c_agent, context->c_home, from);
  225. if (o_method) {
  226. method = sip_method_code(o_method);
  227. } else {
  228. isize_t len;
  229. char const *params = to->a_url->url_params;
  230. len = url_param(params, "method", NULL, 0);
  231. if (len > 0) {
  232. o_method = su_alloc(home, len + 1);
  233. if (o_method == 0 ||
  234. url_param(params, "method", o_method, len + 1) != len) {
  235. fprintf(stderr, "%s: %s\n", name,
  236. o_method ? "internal error" : strerror(errno));
  237. exit(2);
  238. }
  239. method = sip_method_code(o_method);
  240. }
  241. }
  242. r_uri = (url_string_t *)url_hdup(home, to->a_url);
  243. sip_aor_strip(to->a_url);
  244. sip_aor_strip(from->a_url);
  245. context->c_username = from->a_url->url_user;
  246. context->c_password = from->a_url->url_password;
  247. from->a_url->url_password = NULL;
  248. if (extra) {
  249. FILE *hf;
  250. if (strcmp(extra, "-"))
  251. hf = fopen(extra, "rb");
  252. else
  253. hf = stdin;
  254. extra = readfile(hf);
  255. }
  256. context->c_proxy = url_hdup(context->c_home,
  257. (url_t *)getenv("sip_proxy"));
  258. nta_agent_set_params(context->c_agent,
  259. NTATAG_SIPFLAGS(MSG_FLG_EXTRACT_COPY),
  260. NTATAG_DEFAULT_PROXY(context->c_proxy),
  261. TAG_END());
  262. context->c_leg =
  263. nta_leg_tcreate(context->c_agent,
  264. NULL, NULL, /* ignore incoming requests */
  265. SIPTAG_FROM(from), /* who is sending OPTIONS? */
  266. SIPTAG_TO(to), /* whom we are sending OPTIONS? */
  267. TAG_END());
  268. if (context->c_leg) {
  269. context->c_orq =
  270. nta_outgoing_tcreate(context->c_leg,
  271. response_to_options, context,
  272. NULL,
  273. method, o_method, r_uri,
  274. SIPTAG_USER_AGENT_STR("options"),
  275. SIPTAG_MAX_FORWARDS_STR(o_max_forwards),
  276. SIPTAG_HEADER_STR(extra),
  277. TAG_END());
  278. if (context->c_orq) {
  279. su_root_run(context->c_root);
  280. nta_outgoing_destroy(context->c_orq), context->c_orq = NULL;
  281. }
  282. nta_leg_destroy(context->c_leg), context->c_leg = NULL;
  283. }
  284. nta_agent_destroy(context->c_agent), context->c_agent = NULL;
  285. }
  286. su_root_destroy(context->c_root);
  287. }
  288. su_deinit();
  289. return context->c_retval;
  290. }
  291. /** Handle responses to OPTIONS request */
  292. static
  293. int response_to_options(context_t *context,
  294. nta_outgoing_t *oreq,
  295. sip_t const *sip)
  296. {
  297. if (proxy_authenticate(context, oreq, sip, response_to_options))
  298. return 0;
  299. if (server_authenticate(context, oreq, sip, response_to_options))
  300. return 0;
  301. if (sip->sip_status->st_status >= 200 || context->c_pre) {
  302. sip_header_t *h = (sip_header_t *)sip->sip_status;
  303. char hname[64];
  304. for (; h; h = (sip_header_t *)h->sh_succ) {
  305. if (!context->c_all) {
  306. if (sip_is_from(h) ||
  307. sip_is_via(h) ||
  308. sip_is_call_id(h) ||
  309. sip_is_cseq(h) ||
  310. sip_is_content_length(h))
  311. continue;
  312. }
  313. if (h->sh_class->hc_name == NULL) {
  314. sl_header_print(stdout, NULL, h);
  315. }
  316. else if (h->sh_class->hc_name[0] == '\0') {
  317. sl_header_print(stdout, "%s\n", h);
  318. }
  319. else {
  320. snprintf(hname, sizeof hname, "%s: %%s\n", h->sh_class->hc_name);
  321. sl_header_print(stdout, hname, h);
  322. }
  323. }
  324. }
  325. if (sip->sip_status->st_status >= 200) {
  326. context->c_retval = sip->sip_status->st_status >= 300;
  327. su_root_break(context->c_root);
  328. }
  329. return 0;
  330. }
  331. /* Read in whole (binary!) file */
  332. char *readfile(FILE *f)
  333. {
  334. char *buffer = NULL;
  335. long size;
  336. size_t len;
  337. if (f == NULL)
  338. return NULL;
  339. for (size = 8192, buffer = NULL, len = 0; !feof(f); size += 8192) {
  340. buffer = realloc(buffer, size + 1);
  341. if (!buffer)
  342. exit(2);
  343. len += fread(buffer + len, 1, 8192, f);
  344. }
  345. if (buffer)
  346. buffer[len] = '\0';
  347. return buffer;
  348. }