urlmatch.c 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523
  1. /*********************************************************************
  2. *
  3. * File : $Source: /cvsroot/ijbswa/current/urlmatch.c,v $
  4. *
  5. * Purpose : Declares functions to match URLs against URL
  6. * patterns.
  7. *
  8. * Copyright : Written by and Copyright (C) 2001-2020
  9. * the Privoxy team. https://www.privoxy.org/
  10. *
  11. * Based on the Internet Junkbuster originally written
  12. * by and Copyright (C) 1997 Anonymous Coders and
  13. * Junkbusters Corporation. http://www.junkbusters.com
  14. *
  15. * This program is free software; you can redistribute it
  16. * and/or modify it under the terms of the GNU General
  17. * Public License as published by the Free Software
  18. * Foundation; either version 2 of the License, or (at
  19. * your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will
  22. * be useful, but WITHOUT ANY WARRANTY; without even the
  23. * implied warranty of MERCHANTABILITY or FITNESS FOR A
  24. * PARTICULAR PURPOSE. See the GNU General Public
  25. * License for more details.
  26. *
  27. * The GNU General Public License should be included with
  28. * this file. If not, you can view it at
  29. * http://www.gnu.org/copyleft/gpl.html
  30. * or write to the Free Software Foundation, Inc., 59
  31. * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  32. *
  33. *********************************************************************/
  34. #include "config.h"
  35. #ifndef _WIN32
  36. #include <stdio.h>
  37. #include <sys/types.h>
  38. #endif
  39. #include <stdlib.h>
  40. #include <ctype.h>
  41. #include <assert.h>
  42. #include <string.h>
  43. #if !defined(_WIN32)
  44. #include <unistd.h>
  45. #endif
  46. #include "project.h"
  47. #include "urlmatch.h"
  48. #include "ssplit.h"
  49. #include "miscutil.h"
  50. #include "errlog.h"
  51. enum regex_anchoring
  52. {
  53. NO_ANCHORING,
  54. LEFT_ANCHORED,
  55. RIGHT_ANCHORED,
  56. RIGHT_ANCHORED_HOST
  57. };
  58. static jb_err compile_vanilla_host_pattern(struct pattern_spec *url, const char *host_pattern);
  59. #ifdef FEATURE_PCRE_HOST_PATTERNS
  60. static jb_err compile_pcre_host_pattern(struct pattern_spec *url, const char *host_pattern);
  61. #endif
  62. /*********************************************************************
  63. *
  64. * Function : free_http_request
  65. *
  66. * Description : Freez a http_request structure
  67. *
  68. * Parameters :
  69. * 1 : http = points to a http_request structure to free
  70. *
  71. * Returns : N/A
  72. *
  73. *********************************************************************/
  74. void free_http_request(struct http_request *http)
  75. {
  76. assert(http);
  77. freez(http->cmd);
  78. freez(http->ocmd);
  79. freez(http->gpc);
  80. freez(http->host);
  81. freez(http->url);
  82. freez(http->hostport);
  83. freez(http->path);
  84. freez(http->version);
  85. freez(http->host_ip_addr_str);
  86. freez(http->dbuffer);
  87. freez(http->dvec);
  88. http->dcount = 0;
  89. }
  90. /*********************************************************************
  91. *
  92. * Function : init_domain_components
  93. *
  94. * Description : Splits the domain name so we can compare it
  95. * against wildcards. It used to be part of
  96. * parse_http_url, but was separated because the
  97. * same code is required in chat in case of
  98. * intercepted requests.
  99. *
  100. * Parameters :
  101. * 1 : http = pointer to the http structure to hold elements.
  102. *
  103. * Returns : JB_ERR_OK on success
  104. * JB_ERR_PARSE on malformed command/URL
  105. * or >100 domains deep.
  106. *
  107. *********************************************************************/
  108. jb_err init_domain_components(struct http_request *http)
  109. {
  110. char *vec[BUFFER_SIZE];
  111. size_t size;
  112. char *p;
  113. http->dbuffer = strdup_or_die(http->host);
  114. /* map to lower case */
  115. for (p = http->dbuffer; *p ; p++)
  116. {
  117. *p = (char)privoxy_tolower(*p);
  118. }
  119. /* split the domain name into components */
  120. http->dcount = ssplit(http->dbuffer, ".", vec, SZ(vec));
  121. if (http->dcount <= 0)
  122. {
  123. /*
  124. * Error: More than SZ(vec) components in domain
  125. * or: no components in domain
  126. */
  127. log_error(LOG_LEVEL_ERROR, "More than SZ(vec) components in domain or none at all.");
  128. return JB_ERR_PARSE;
  129. }
  130. /* save a copy of the pointers in dvec */
  131. size = (size_t)http->dcount * sizeof(*http->dvec);
  132. http->dvec = malloc_or_die(size);
  133. memcpy(http->dvec, vec, size);
  134. return JB_ERR_OK;
  135. }
  136. /*********************************************************************
  137. *
  138. * Function : url_requires_percent_encoding
  139. *
  140. * Description : Checks if an URL contains invalid characters
  141. * according to RFC 3986 that should be percent-encoded.
  142. * Does not verify whether or not the passed string
  143. * actually is a valid URL.
  144. *
  145. * Parameters :
  146. * 1 : url = URL to check
  147. *
  148. * Returns : True in case of valid URLs, false otherwise
  149. *
  150. *********************************************************************/
  151. int url_requires_percent_encoding(const char *url)
  152. {
  153. static const char allowed_characters[128] = {
  154. '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
  155. '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
  156. '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
  157. '\0', '\0', '\0', '!', '\0', '#', '$', '%', '&', '\'',
  158. '(', ')', '*', '+', ',', '-', '.', '/', '0', '1',
  159. '2', '3', '4', '5', '6', '7', '8', '9', ':', ';',
  160. '\0', '=', '\0', '?', '@', 'A', 'B', 'C', 'D', 'E',
  161. 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  162. 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
  163. 'Z', '[', '\0', ']', '\0', '_', '\0', 'a', 'b', 'c',
  164. 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  165. 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
  166. 'x', 'y', 'z', '\0', '\0', '\0', '~', '\0'
  167. };
  168. while (*url != '\0')
  169. {
  170. const unsigned int i = (unsigned char)*url++;
  171. if (i >= sizeof(allowed_characters) || '\0' == allowed_characters[i])
  172. {
  173. return TRUE;
  174. }
  175. }
  176. return FALSE;
  177. }
  178. /*********************************************************************
  179. *
  180. * Function : parse_http_url
  181. *
  182. * Description : Parse out the host and port from the URL. Find the
  183. * hostname & path, port (if ':'), and/or password (if '@')
  184. *
  185. * Parameters :
  186. * 1 : url = URL (or is it URI?) to break down
  187. * 2 : http = pointer to the http structure to hold elements.
  188. * Must be initialized with valid values (like NULLs).
  189. * 3 : require_protocol = Whether or not URLs without
  190. * protocol are acceptable.
  191. *
  192. * Returns : JB_ERR_OK on success
  193. * JB_ERR_PARSE on malformed command/URL
  194. * or >100 domains deep.
  195. *
  196. *********************************************************************/
  197. jb_err parse_http_url(const char *url, struct http_request *http, int require_protocol)
  198. {
  199. int host_available = 1; /* A proxy can dream. */
  200. /*
  201. * Save our initial URL
  202. */
  203. http->url = strdup_or_die(url);
  204. /*
  205. * Check for * URI. If found, we're done.
  206. */
  207. if (*http->url == '*')
  208. {
  209. http->path = strdup_or_die("*");
  210. http->hostport = strdup_or_die("");
  211. if (http->url[1] != '\0')
  212. {
  213. return JB_ERR_PARSE;
  214. }
  215. return JB_ERR_OK;
  216. }
  217. /*
  218. * Split URL into protocol,hostport,path.
  219. */
  220. {
  221. char *buf;
  222. char *url_noproto;
  223. char *url_path;
  224. buf = strdup_or_die(url);
  225. /* Find the start of the URL in our scratch space */
  226. url_noproto = buf;
  227. if (strncmpic(url_noproto, "http://", 7) == 0)
  228. {
  229. url_noproto += 7;
  230. }
  231. else if (strncmpic(url_noproto, "https://", 8) == 0)
  232. {
  233. /*
  234. * Should only happen when called from cgi_show_url_info().
  235. */
  236. url_noproto += 8;
  237. http->ssl = 1;
  238. }
  239. else if (*url_noproto == '/')
  240. {
  241. /*
  242. * Short request line without protocol and host.
  243. * Most likely because the client's request
  244. * was intercepted and redirected into Privoxy.
  245. */
  246. http->host = NULL;
  247. host_available = 0;
  248. }
  249. else if (require_protocol)
  250. {
  251. freez(buf);
  252. return JB_ERR_PARSE;
  253. }
  254. url_path = strchr(url_noproto, '/');
  255. if (url_path != NULL)
  256. {
  257. /*
  258. * Got a path.
  259. *
  260. * If FEATURE_HTTPS_INSPECTION isn't available, ignore the
  261. * path for https URLs so that we get consistent behaviour
  262. * if a https URL is parsed. When the URL is actually
  263. * retrieved, https hides the path part.
  264. */
  265. http->path = strdup_or_die(
  266. #ifndef FEATURE_HTTPS_INSPECTION
  267. http->ssl ? "/" :
  268. #endif
  269. url_path
  270. );
  271. *url_path = '\0';
  272. http->hostport = string_tolower(url_noproto);
  273. }
  274. else
  275. {
  276. /*
  277. * Repair broken HTTP requests that don't contain a path,
  278. * or CONNECT requests
  279. */
  280. http->path = strdup_or_die("/");
  281. http->hostport = string_tolower(url_noproto);
  282. }
  283. freez(buf);
  284. if (http->hostport == NULL)
  285. {
  286. return JB_ERR_PARSE;
  287. }
  288. }
  289. if (!host_available)
  290. {
  291. /* Without host, there is nothing left to do here */
  292. return JB_ERR_OK;
  293. }
  294. /*
  295. * Split hostport into user/password (ignored), host, port.
  296. */
  297. {
  298. char *buf;
  299. char *host;
  300. char *port;
  301. buf = strdup_or_die(http->hostport);
  302. /* check if url contains username and/or password */
  303. host = strchr(buf, '@');
  304. if (host != NULL)
  305. {
  306. /* Contains username/password, skip it and the @ sign. */
  307. host++;
  308. }
  309. else
  310. {
  311. /* No username or password. */
  312. host = buf;
  313. }
  314. /* Move after hostname before port number */
  315. if (*host == '[')
  316. {
  317. /* Numeric IPv6 address delimited by brackets */
  318. host++;
  319. port = strchr(host, ']');
  320. if (port == NULL)
  321. {
  322. /* Missing closing bracket */
  323. freez(buf);
  324. return JB_ERR_PARSE;
  325. }
  326. *port++ = '\0';
  327. if (*port == '\0')
  328. {
  329. port = NULL;
  330. }
  331. else if (*port != ':')
  332. {
  333. /* Garbage after closing bracket */
  334. freez(buf);
  335. return JB_ERR_PARSE;
  336. }
  337. }
  338. else
  339. {
  340. /* Plain non-escaped hostname */
  341. port = strchr(host, ':');
  342. }
  343. /* check if url contains port */
  344. if (port != NULL)
  345. {
  346. /* Contains port */
  347. char *endptr;
  348. long parsed_port;
  349. /* Terminate hostname and point to start of port string */
  350. *port++ = '\0';
  351. parsed_port = strtol(port, &endptr, 10);
  352. if ((parsed_port <= 0) || (parsed_port > 65535) || (*endptr != '\0'))
  353. {
  354. log_error(LOG_LEVEL_ERROR, "Invalid port in URL: %s.", url);
  355. freez(buf);
  356. return JB_ERR_PARSE;
  357. }
  358. http->port = (int)parsed_port;
  359. }
  360. else
  361. {
  362. /* No port specified. */
  363. http->port = (http->ssl ? 443 : 80);
  364. }
  365. http->host = strdup_or_die(host);
  366. freez(buf);
  367. }
  368. /* Split domain name so we can compare it against wildcards */
  369. return init_domain_components(http);
  370. }
  371. /*********************************************************************
  372. *
  373. * Function : unknown_method
  374. *
  375. * Description : Checks whether a method is unknown.
  376. *
  377. * Parameters :
  378. * 1 : method = points to a http method
  379. *
  380. * Returns : TRUE if it's unknown, FALSE otherwise.
  381. *
  382. *********************************************************************/
  383. static int unknown_method(const char *method)
  384. {
  385. static const char * const known_http_methods[] = {
  386. /* Basic HTTP request type */
  387. "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "TRACE", "CONNECT",
  388. /* webDAV extensions (RFC2518) */
  389. "PROPFIND", "PROPPATCH", "MOVE", "COPY", "MKCOL", "LOCK", "UNLOCK",
  390. /*
  391. * Microsoft webDAV extension for Exchange 2000. See:
  392. * http://lists.w3.org/Archives/Public/w3c-dist-auth/2002JanMar/0001.html
  393. * http://msdn.microsoft.com/library/en-us/wss/wss/_webdav_methods.asp
  394. */
  395. "BCOPY", "BMOVE", "BDELETE", "BPROPFIND", "BPROPPATCH",
  396. /*
  397. * Another Microsoft webDAV extension for Exchange 2000. See:
  398. * http://systems.cs.colorado.edu/grunwald/MobileComputing/Papers/draft-cohen-gena-p-base-00.txt
  399. * http://lists.w3.org/Archives/Public/w3c-dist-auth/2002JanMar/0001.html
  400. * http://msdn.microsoft.com/library/en-us/wss/wss/_webdav_methods.asp
  401. */
  402. "SUBSCRIBE", "UNSUBSCRIBE", "NOTIFY", "POLL",
  403. /*
  404. * Yet another WebDAV extension, this time for
  405. * Web Distributed Authoring and Versioning (RFC3253)
  406. */
  407. "VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
  408. "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY",
  409. /*
  410. * The PATCH method is defined by RFC5789, the format of the
  411. * actual patch in the body depends on the application, but from
  412. * Privoxy's point of view it doesn't matter.
  413. */
  414. "PATCH",
  415. };
  416. int i;
  417. for (i = 0; i < SZ(known_http_methods); i++)
  418. {
  419. if (0 == strcmpic(method, known_http_methods[i]))
  420. {
  421. return FALSE;
  422. }
  423. }
  424. return TRUE;
  425. }
  426. /*********************************************************************
  427. *
  428. * Function : normalize_http_version
  429. *
  430. * Description : Take a supported HTTP version string and remove
  431. * leading zeroes etc., reject unsupported versions.
  432. *
  433. * This is an explicit RFC 2616 (3.1) MUST and
  434. * RFC 7230 mandates that intermediaries send their
  435. * own HTTP-version in forwarded messages.
  436. *
  437. * Parameters :
  438. * 1 : http_version = HTTP version string
  439. *
  440. * Returns : JB_ERR_OK on success
  441. * JB_ERR_PARSE if the HTTP version is unsupported
  442. *
  443. *********************************************************************/
  444. static jb_err normalize_http_version(char *http_version)
  445. {
  446. unsigned int major_version;
  447. unsigned int minor_version;
  448. if (2 != sscanf(http_version, "HTTP/%u.%u", &major_version, &minor_version))
  449. {
  450. log_error(LOG_LEVEL_ERROR, "Unsupported HTTP version: %s", http_version);
  451. return JB_ERR_PARSE;
  452. }
  453. if (major_version != 1 || (minor_version != 0 && minor_version != 1))
  454. {
  455. log_error(LOG_LEVEL_ERROR, "The only supported HTTP "
  456. "versions are 1.0 and 1.1. This rules out: %s", http_version);
  457. return JB_ERR_PARSE;
  458. }
  459. assert(strlen(http_version) >= 8);
  460. snprintf(http_version, 9, "HTTP/%u.%u", major_version, minor_version);
  461. return JB_ERR_OK;
  462. }
  463. /*********************************************************************
  464. *
  465. * Function : parse_http_request
  466. *
  467. * Description : Parse out the host and port from the URL. Find the
  468. * hostname & path, port (if ':'), and/or password (if '@')
  469. *
  470. * Parameters :
  471. * 1 : req = HTTP request line to break down
  472. * 2 : http = pointer to the http structure to hold elements
  473. *
  474. * Returns : JB_ERR_OK on success
  475. * JB_ERR_CGI_PARAMS on malformed command/URL
  476. * or >100 domains deep.
  477. *
  478. *********************************************************************/
  479. jb_err parse_http_request(const char *req, struct http_request *http)
  480. {
  481. char *buf;
  482. char *v[3];
  483. int n;
  484. jb_err err;
  485. memset(http, '\0', sizeof(*http));
  486. buf = strdup_or_die(req);
  487. n = ssplit(buf, " \r\n", v, SZ(v));
  488. if (n != 3)
  489. {
  490. freez(buf);
  491. return JB_ERR_PARSE;
  492. }
  493. /*
  494. * Fail in case of unknown methods
  495. * which we might not handle correctly.
  496. *
  497. * XXX: There should be a config option
  498. * to forward requests with unknown methods
  499. * anyway. Most of them don't need special
  500. * steps.
  501. */
  502. if (unknown_method(v[0]))
  503. {
  504. log_error(LOG_LEVEL_ERROR, "Unknown HTTP method detected: %s", v[0]);
  505. freez(buf);
  506. return JB_ERR_PARSE;
  507. }
  508. if (JB_ERR_OK != normalize_http_version(v[2]))
  509. {
  510. freez(buf);
  511. return JB_ERR_PARSE;
  512. }
  513. http->ssl = !strcmpic(v[0], "CONNECT");
  514. err = parse_http_url(v[1], http, !http->ssl);
  515. if (err)
  516. {
  517. freez(buf);
  518. return err;
  519. }
  520. /*
  521. * Copy the details into the structure
  522. */
  523. http->cmd = strdup_or_die(req);
  524. http->gpc = strdup_or_die(v[0]);
  525. http->version = strdup_or_die(v[2]);
  526. http->ocmd = strdup_or_die(http->cmd);
  527. freez(buf);
  528. return JB_ERR_OK;
  529. }
  530. /*********************************************************************
  531. *
  532. * Function : compile_pattern
  533. *
  534. * Description : Compiles a host, domain or TAG pattern.
  535. *
  536. * Parameters :
  537. * 1 : pattern = The pattern to compile.
  538. * 2 : anchoring = How the regex should be modified
  539. * before compilation. Can be either
  540. * one of NO_ANCHORING, LEFT_ANCHORED,
  541. * RIGHT_ANCHORED or RIGHT_ANCHORED_HOST.
  542. * 3 : url = In case of failures, the spec member is
  543. * logged and the structure freed.
  544. * 4 : regex = Where the compiled regex should be stored.
  545. *
  546. * Returns : JB_ERR_OK - Success
  547. * JB_ERR_PARSE - Cannot parse regex
  548. *
  549. *********************************************************************/
  550. static jb_err compile_pattern(const char *pattern, enum regex_anchoring anchoring,
  551. struct pattern_spec *url, regex_t **regex)
  552. {
  553. int errcode;
  554. const char *fmt = NULL;
  555. char *rebuf;
  556. size_t rebuf_size;
  557. assert(pattern);
  558. if (pattern[0] == '\0')
  559. {
  560. *regex = NULL;
  561. return JB_ERR_OK;
  562. }
  563. switch (anchoring)
  564. {
  565. case NO_ANCHORING:
  566. fmt = "%s";
  567. break;
  568. case RIGHT_ANCHORED:
  569. fmt = "%s$";
  570. break;
  571. case RIGHT_ANCHORED_HOST:
  572. fmt = "%s\\.?$";
  573. break;
  574. case LEFT_ANCHORED:
  575. fmt = "^%s";
  576. break;
  577. default:
  578. log_error(LOG_LEVEL_FATAL,
  579. "Invalid anchoring in compile_pattern %d", anchoring);
  580. }
  581. rebuf_size = strlen(pattern) + strlen(fmt);
  582. rebuf = malloc_or_die(rebuf_size);
  583. *regex = zalloc_or_die(sizeof(**regex));
  584. snprintf(rebuf, rebuf_size, fmt, pattern);
  585. errcode = regcomp(*regex, rebuf, (REG_EXTENDED|REG_NOSUB|REG_ICASE));
  586. if (errcode)
  587. {
  588. size_t errlen = regerror(errcode, *regex, rebuf, rebuf_size);
  589. if (errlen > (rebuf_size - (size_t)1))
  590. {
  591. errlen = rebuf_size - (size_t)1;
  592. }
  593. rebuf[errlen] = '\0';
  594. log_error(LOG_LEVEL_ERROR, "error compiling %s from %s: %s",
  595. pattern, url->spec, rebuf);
  596. free_pattern_spec(url);
  597. freez(rebuf);
  598. return JB_ERR_PARSE;
  599. }
  600. freez(rebuf);
  601. return JB_ERR_OK;
  602. }
  603. /*********************************************************************
  604. *
  605. * Function : compile_url_pattern
  606. *
  607. * Description : Compiles the three parts of an URL pattern.
  608. *
  609. * Parameters :
  610. * 1 : url = Target pattern_spec to be filled in.
  611. * 2 : buf = The url pattern to compile. Will be messed up.
  612. *
  613. * Returns : JB_ERR_OK - Success
  614. * JB_ERR_MEMORY - Out of memory
  615. * JB_ERR_PARSE - Cannot parse regex
  616. *
  617. *********************************************************************/
  618. static jb_err compile_url_pattern(struct pattern_spec *url, char *buf)
  619. {
  620. char *p;
  621. #ifdef FEATURE_PCRE_HOST_PATTERNS
  622. const size_t prefix_length = 18;
  623. if (strncmpic(buf, "PCRE-HOST-PATTERN:", prefix_length) == 0)
  624. {
  625. url->pattern.url_spec.host_regex_type = PCRE_HOST_PATTERN;
  626. /* Overwrite the "PCRE-HOST-PATTERN:" prefix */
  627. memmove(buf, buf+prefix_length, strlen(buf+prefix_length)+1);
  628. }
  629. else
  630. {
  631. url->pattern.url_spec.host_regex_type = VANILLA_HOST_PATTERN;
  632. }
  633. #endif
  634. p = strchr(buf, '/');
  635. if (NULL != p)
  636. {
  637. /*
  638. * Only compile the regex if it consists of more than
  639. * a single slash, otherwise it wouldn't affect the result.
  640. */
  641. if (p[1] != '\0')
  642. {
  643. /*
  644. * XXX: does it make sense to compile the slash at the beginning?
  645. */
  646. jb_err err = compile_pattern(p, LEFT_ANCHORED, url, &url->pattern.url_spec.preg);
  647. if (JB_ERR_OK != err)
  648. {
  649. return err;
  650. }
  651. }
  652. *p = '\0';
  653. }
  654. /*
  655. * IPv6 numeric hostnames can contain colons, thus we need
  656. * to delimit the hostname before the real port separator.
  657. * As brackets are already used in the hostname pattern,
  658. * we use angle brackets ('<', '>') instead.
  659. */
  660. if ((buf[0] == '<') && (NULL != (p = strchr(buf + 1, '>'))))
  661. {
  662. *p++ = '\0';
  663. buf++;
  664. if (*p == '\0')
  665. {
  666. /* IPv6 address without port number */
  667. p = NULL;
  668. }
  669. else if (*p != ':')
  670. {
  671. /* Garbage after address delimiter */
  672. return JB_ERR_PARSE;
  673. }
  674. }
  675. else
  676. {
  677. p = strchr(buf, ':');
  678. }
  679. if (NULL != p)
  680. {
  681. *p++ = '\0';
  682. url->pattern.url_spec.port_list = strdup_or_die(p);
  683. }
  684. else
  685. {
  686. url->pattern.url_spec.port_list = NULL;
  687. }
  688. if (buf[0] != '\0')
  689. {
  690. #ifdef FEATURE_PCRE_HOST_PATTERNS
  691. if (url->pattern.url_spec.host_regex_type == PCRE_HOST_PATTERN)
  692. {
  693. return compile_pcre_host_pattern(url, buf);
  694. }
  695. else
  696. #endif
  697. {
  698. return compile_vanilla_host_pattern(url, buf);
  699. }
  700. }
  701. return JB_ERR_OK;
  702. }
  703. #ifdef FEATURE_PCRE_HOST_PATTERNS
  704. /*********************************************************************
  705. *
  706. * Function : compile_pcre_host_pattern
  707. *
  708. * Description : Parses and compiles a pcre host pattern.
  709. *
  710. * Parameters :
  711. * 1 : url = Target pattern_spec to be filled in.
  712. * 2 : host_pattern = Host pattern to compile.
  713. *
  714. * Returns : JB_ERR_OK - Success
  715. * JB_ERR_MEMORY - Out of memory
  716. * JB_ERR_PARSE - Cannot parse regex
  717. *
  718. *********************************************************************/
  719. static jb_err compile_pcre_host_pattern(struct pattern_spec *url, const char *host_pattern)
  720. {
  721. return compile_pattern(host_pattern, RIGHT_ANCHORED_HOST, url, &url->pattern.url_spec.host_regex);
  722. }
  723. #endif /* def FEATURE_PCRE_HOST_PATTERNS */
  724. /*********************************************************************
  725. *
  726. * Function : compile_vanilla_host_pattern
  727. *
  728. * Description : Parses and "compiles" an old-school host pattern.
  729. *
  730. * Parameters :
  731. * 1 : url = Target pattern_spec to be filled in.
  732. * 2 : host_pattern = Host pattern to parse.
  733. *
  734. * Returns : JB_ERR_OK - Success
  735. * JB_ERR_PARSE - Cannot parse regex
  736. *
  737. *********************************************************************/
  738. static jb_err compile_vanilla_host_pattern(struct pattern_spec *url, const char *host_pattern)
  739. {
  740. char *v[150];
  741. size_t size;
  742. char *p;
  743. /*
  744. * Parse domain part
  745. */
  746. if (host_pattern[strlen(host_pattern) - 1] == '.')
  747. {
  748. url->pattern.url_spec.unanchored |= ANCHOR_RIGHT;
  749. }
  750. if (host_pattern[0] == '.')
  751. {
  752. url->pattern.url_spec.unanchored |= ANCHOR_LEFT;
  753. }
  754. /*
  755. * Split domain into components
  756. */
  757. url->pattern.url_spec.dbuffer = strdup_or_die(host_pattern);
  758. /*
  759. * Map to lower case
  760. */
  761. for (p = url->pattern.url_spec.dbuffer; *p ; p++)
  762. {
  763. *p = (char)privoxy_tolower(*p);
  764. }
  765. /*
  766. * Split the domain name into components
  767. */
  768. url->pattern.url_spec.dcount = ssplit(url->pattern.url_spec.dbuffer, ".", v, SZ(v));
  769. if (url->pattern.url_spec.dcount < 0)
  770. {
  771. free_pattern_spec(url);
  772. return JB_ERR_PARSE;
  773. }
  774. else if (url->pattern.url_spec.dcount != 0)
  775. {
  776. /*
  777. * Save a copy of the pointers in dvec
  778. */
  779. size = (size_t)url->pattern.url_spec.dcount * sizeof(*url->pattern.url_spec.dvec);
  780. url->pattern.url_spec.dvec = malloc_or_die(size);
  781. memcpy(url->pattern.url_spec.dvec, v, size);
  782. }
  783. /*
  784. * else dcount == 0 in which case we needn't do anything,
  785. * since dvec will never be accessed and the pattern will
  786. * match all domains.
  787. */
  788. return JB_ERR_OK;
  789. }
  790. /*********************************************************************
  791. *
  792. * Function : simplematch
  793. *
  794. * Description : String matching, with a (greedy) '*' wildcard that
  795. * stands for zero or more arbitrary characters and
  796. * character classes in [], which take both enumerations
  797. * and ranges.
  798. *
  799. * Parameters :
  800. * 1 : pattern = pattern for matching
  801. * 2 : text = text to be matched
  802. *
  803. * Returns : 0 if match, else nonzero
  804. *
  805. *********************************************************************/
  806. static int simplematch(const char *pattern, const char *text)
  807. {
  808. const unsigned char *pat = (const unsigned char *)pattern;
  809. const unsigned char *txt = (const unsigned char *)text;
  810. const unsigned char *fallback = pat;
  811. int wildcard = 0;
  812. unsigned char lastchar = 'a';
  813. unsigned i;
  814. unsigned char charmap[32];
  815. while (*txt)
  816. {
  817. /* EOF pattern but !EOF text? */
  818. if (*pat == '\0')
  819. {
  820. if (wildcard)
  821. {
  822. pat = fallback;
  823. }
  824. else
  825. {
  826. return 1;
  827. }
  828. }
  829. /* '*' in the pattern? */
  830. if (*pat == '*')
  831. {
  832. /* The pattern ends afterwards? Speed up the return. */
  833. if (*++pat == '\0')
  834. {
  835. return 0;
  836. }
  837. /* Else, set wildcard mode and remember position after '*' */
  838. wildcard = 1;
  839. fallback = pat;
  840. }
  841. /* Character range specification? */
  842. if (*pat == '[')
  843. {
  844. memset(charmap, '\0', sizeof(charmap));
  845. while (*++pat != ']')
  846. {
  847. if (!*pat)
  848. {
  849. return 1;
  850. }
  851. else if (*pat == '-')
  852. {
  853. if ((*++pat == ']') || *pat == '\0')
  854. {
  855. return(1);
  856. }
  857. for (i = lastchar; i <= *pat; i++)
  858. {
  859. charmap[i / 8] |= (unsigned char)(1 << (i % 8));
  860. }
  861. }
  862. else
  863. {
  864. charmap[*pat / 8] |= (unsigned char)(1 << (*pat % 8));
  865. lastchar = *pat;
  866. }
  867. }
  868. } /* -END- if Character range specification */
  869. /*
  870. * Char match, or char range match?
  871. */
  872. if ((*pat == *txt)
  873. || (*pat == '?')
  874. || ((*pat == ']') && (charmap[*txt / 8] & (1 << (*txt % 8)))))
  875. {
  876. /*
  877. * Success: Go ahead
  878. */
  879. pat++;
  880. }
  881. else if (!wildcard)
  882. {
  883. /*
  884. * No match && no wildcard: No luck
  885. */
  886. return 1;
  887. }
  888. else if (pat != fallback)
  889. {
  890. /*
  891. * Increment text pointer if in char range matching
  892. */
  893. if (*pat == ']')
  894. {
  895. txt++;
  896. }
  897. /*
  898. * Wildcard mode && nonmatch beyond fallback: Rewind pattern
  899. */
  900. pat = fallback;
  901. /*
  902. * Restart matching from current text pointer
  903. */
  904. continue;
  905. }
  906. txt++;
  907. }
  908. /* Cut off extra '*'s */
  909. if (*pat == '*') pat++;
  910. /* If this is the pattern's end, fine! */
  911. return(*pat);
  912. }
  913. /*********************************************************************
  914. *
  915. * Function : simple_domaincmp
  916. *
  917. * Description : Domain-wise Compare fqdn's. The comparison is
  918. * both left- and right-anchored. The individual
  919. * domain names are compared with simplematch().
  920. * This is only used by domain_match.
  921. *
  922. * Parameters :
  923. * 1 : pv = array of patterns to compare
  924. * 2 : fv = array of domain components to compare
  925. * 3 : len = length of the arrays (both arrays are the
  926. * same length - if they weren't, it couldn't
  927. * possibly be a match).
  928. *
  929. * Returns : 0 => domains are equivalent, else no match.
  930. *
  931. *********************************************************************/
  932. static int simple_domaincmp(char **pv, char **fv, int len)
  933. {
  934. int n;
  935. for (n = 0; n < len; n++)
  936. {
  937. if (simplematch(pv[n], fv[n]))
  938. {
  939. return 1;
  940. }
  941. }
  942. return 0;
  943. }
  944. /*********************************************************************
  945. *
  946. * Function : domain_match
  947. *
  948. * Description : Domain-wise Compare fqdn's. Governed by the bimap in
  949. * p.pattern->unachored, the comparison is un-, left-,
  950. * right-anchored, or both.
  951. * The individual domain names are compared with
  952. * simplematch().
  953. *
  954. * Parameters :
  955. * 1 : p = a domain that may contain a '*' as a wildcard.
  956. * 2 : fqdn = domain name against which the patterns are compared.
  957. *
  958. * Returns : 0 => domains are equivalent, else no match.
  959. *
  960. *********************************************************************/
  961. static int domain_match(const struct pattern_spec *p, const struct http_request *fqdn)
  962. {
  963. char **pv, **fv; /* vectors */
  964. int plen, flen;
  965. int unanchored = p->pattern.url_spec.unanchored & (ANCHOR_RIGHT | ANCHOR_LEFT);
  966. plen = p->pattern.url_spec.dcount;
  967. flen = fqdn->dcount;
  968. if (flen < plen)
  969. {
  970. /* fqdn is too short to match this pattern */
  971. return 1;
  972. }
  973. pv = p->pattern.url_spec.dvec;
  974. fv = fqdn->dvec;
  975. if (unanchored == ANCHOR_LEFT)
  976. {
  977. /*
  978. * Right anchored.
  979. *
  980. * Convert this into a fully anchored pattern with
  981. * the fqdn and pattern the same length
  982. */
  983. fv += (flen - plen); /* flen - plen >= 0 due to check above */
  984. return simple_domaincmp(pv, fv, plen);
  985. }
  986. else if (unanchored == 0)
  987. {
  988. /* Fully anchored, check length */
  989. if (flen != plen)
  990. {
  991. return 1;
  992. }
  993. return simple_domaincmp(pv, fv, plen);
  994. }
  995. else if (unanchored == ANCHOR_RIGHT)
  996. {
  997. /* Left anchored, ignore all extra in fqdn */
  998. return simple_domaincmp(pv, fv, plen);
  999. }
  1000. else
  1001. {
  1002. /* Unanchored */
  1003. int n;
  1004. int maxn = flen - plen;
  1005. for (n = 0; n <= maxn; n++)
  1006. {
  1007. if (!simple_domaincmp(pv, fv, plen))
  1008. {
  1009. return 0;
  1010. }
  1011. /*
  1012. * Doesn't match from start of fqdn
  1013. * Try skipping first part of fqdn
  1014. */
  1015. fv++;
  1016. }
  1017. return 1;
  1018. }
  1019. }
  1020. /*********************************************************************
  1021. *
  1022. * Function : create_pattern_spec
  1023. *
  1024. * Description : Creates a "pattern_spec" structure from a string.
  1025. * When finished, free with free_pattern_spec().
  1026. *
  1027. * Parameters :
  1028. * 1 : pattern = Target pattern_spec to be filled in.
  1029. * Will be zeroed before use.
  1030. * 2 : buf = Source pattern, null terminated. NOTE: The
  1031. * contents of this buffer are destroyed by this
  1032. * function. If this function succeeds, the
  1033. * buffer is copied to pattern->spec. If this
  1034. * function fails, the contents of the buffer
  1035. * are lost forever.
  1036. *
  1037. * Returns : JB_ERR_OK - Success
  1038. * JB_ERR_PARSE - Cannot parse regex (Detailed message
  1039. * written to system log)
  1040. *
  1041. *********************************************************************/
  1042. jb_err create_pattern_spec(struct pattern_spec *pattern, char *buf)
  1043. {
  1044. static const struct
  1045. {
  1046. /** The tag pattern prefix to match */
  1047. const char *prefix;
  1048. /** The length of the prefix to match */
  1049. const size_t prefix_length;
  1050. /** The pattern flag */
  1051. const unsigned flag;
  1052. } tag_pattern[] = {
  1053. { "TAG:", 4, PATTERN_SPEC_TAG_PATTERN},
  1054. #ifdef FEATURE_CLIENT_TAGS
  1055. { "CLIENT-TAG:", 11, PATTERN_SPEC_CLIENT_TAG_PATTERN},
  1056. #endif
  1057. { "NO-REQUEST-TAG:", 15, PATTERN_SPEC_NO_REQUEST_TAG_PATTERN},
  1058. { "NO-RESPONSE-TAG:", 16, PATTERN_SPEC_NO_RESPONSE_TAG_PATTERN}
  1059. };
  1060. int i;
  1061. assert(pattern);
  1062. assert(buf);
  1063. memset(pattern, '\0', sizeof(*pattern));
  1064. /* Remember the original specification for the CGI pages. */
  1065. pattern->spec = strdup_or_die(buf);
  1066. /* Check if it's a tag pattern */
  1067. for (i = 0; i < SZ(tag_pattern); i++)
  1068. {
  1069. if (0 == strncmpic(pattern->spec, tag_pattern[i].prefix, tag_pattern[i].prefix_length))
  1070. {
  1071. /* The regex starts after the prefix */
  1072. const char *tag_regex = buf + tag_pattern[i].prefix_length;
  1073. pattern->flags |= tag_pattern[i].flag;
  1074. return compile_pattern(tag_regex, NO_ANCHORING, pattern,
  1075. &pattern->pattern.tag_regex);
  1076. }
  1077. }
  1078. /* If it isn't a tag pattern it must be an URL pattern. */
  1079. pattern->flags |= PATTERN_SPEC_URL_PATTERN;
  1080. return compile_url_pattern(pattern, buf);
  1081. }
  1082. /*********************************************************************
  1083. *
  1084. * Function : free_pattern_spec
  1085. *
  1086. * Description : Called from the "unloaders". Freez the pattern
  1087. * structure elements.
  1088. *
  1089. * Parameters :
  1090. * 1 : pattern = pointer to a pattern_spec structure.
  1091. *
  1092. * Returns : N/A
  1093. *
  1094. *********************************************************************/
  1095. void free_pattern_spec(struct pattern_spec *pattern)
  1096. {
  1097. if (pattern == NULL) return;
  1098. freez(pattern->spec);
  1099. #ifdef FEATURE_PCRE_HOST_PATTERNS
  1100. if (pattern->pattern.url_spec.host_regex)
  1101. {
  1102. regfree(pattern->pattern.url_spec.host_regex);
  1103. freez(pattern->pattern.url_spec.host_regex);
  1104. }
  1105. #endif /* def FEATURE_PCRE_HOST_PATTERNS */
  1106. freez(pattern->pattern.url_spec.dbuffer);
  1107. freez(pattern->pattern.url_spec.dvec);
  1108. pattern->pattern.url_spec.dcount = 0;
  1109. freez(pattern->pattern.url_spec.port_list);
  1110. if (pattern->pattern.url_spec.preg)
  1111. {
  1112. regfree(pattern->pattern.url_spec.preg);
  1113. freez(pattern->pattern.url_spec.preg);
  1114. }
  1115. if (pattern->pattern.tag_regex)
  1116. {
  1117. regfree(pattern->pattern.tag_regex);
  1118. freez(pattern->pattern.tag_regex);
  1119. }
  1120. }
  1121. /*********************************************************************
  1122. *
  1123. * Function : port_matches
  1124. *
  1125. * Description : Compares a port against a port list.
  1126. *
  1127. * Parameters :
  1128. * 1 : port = The port to check.
  1129. * 2 : port_list = The list of port to compare with.
  1130. *
  1131. * Returns : TRUE for yes, FALSE otherwise.
  1132. *
  1133. *********************************************************************/
  1134. static int port_matches(const int port, const char *port_list)
  1135. {
  1136. return ((NULL == port_list) || match_portlist(port_list, port));
  1137. }
  1138. /*********************************************************************
  1139. *
  1140. * Function : host_matches
  1141. *
  1142. * Description : Compares a host against a host pattern.
  1143. *
  1144. * Parameters :
  1145. * 1 : url = The URL to match
  1146. * 2 : pattern = The URL pattern
  1147. *
  1148. * Returns : TRUE for yes, FALSE otherwise.
  1149. *
  1150. *********************************************************************/
  1151. static int host_matches(const struct http_request *http,
  1152. const struct pattern_spec *pattern)
  1153. {
  1154. assert(http->host != NULL);
  1155. #ifdef FEATURE_PCRE_HOST_PATTERNS
  1156. if (pattern->pattern.url_spec.host_regex_type == PCRE_HOST_PATTERN)
  1157. {
  1158. return ((NULL == pattern->pattern.url_spec.host_regex)
  1159. || (0 == regexec(pattern->pattern.url_spec.host_regex,
  1160. http->host, 0, NULL, 0)));
  1161. }
  1162. #endif
  1163. return ((NULL == pattern->pattern.url_spec.dbuffer) || (0 == domain_match(pattern, http)));
  1164. }
  1165. /*********************************************************************
  1166. *
  1167. * Function : path_matches
  1168. *
  1169. * Description : Compares a path against a path pattern.
  1170. *
  1171. * Parameters :
  1172. * 1 : path = The path to match
  1173. * 2 : pattern = The URL pattern
  1174. *
  1175. * Returns : TRUE for yes, FALSE otherwise.
  1176. *
  1177. *********************************************************************/
  1178. static int path_matches(const char *path, const struct pattern_spec *pattern)
  1179. {
  1180. return ((NULL == pattern->pattern.url_spec.preg)
  1181. || (0 == regexec(pattern->pattern.url_spec.preg, path, 0, NULL, 0)));
  1182. }
  1183. /*********************************************************************
  1184. *
  1185. * Function : url_match
  1186. *
  1187. * Description : Compare a URL against a URL pattern.
  1188. *
  1189. * Parameters :
  1190. * 1 : pattern = a URL pattern
  1191. * 2 : url = URL to match
  1192. *
  1193. * Returns : Nonzero if the URL matches the pattern, else 0.
  1194. *
  1195. *********************************************************************/
  1196. int url_match(const struct pattern_spec *pattern,
  1197. const struct http_request *http)
  1198. {
  1199. if (!(pattern->flags & PATTERN_SPEC_URL_PATTERN))
  1200. {
  1201. /* It's not an URL pattern and thus shouldn't be matched against URLs */
  1202. return 0;
  1203. }
  1204. return (port_matches(http->port, pattern->pattern.url_spec.port_list)
  1205. && host_matches(http, pattern) && path_matches(http->path, pattern));
  1206. }
  1207. /*********************************************************************
  1208. *
  1209. * Function : match_portlist
  1210. *
  1211. * Description : Check if a given number is covered by a comma
  1212. * separated list of numbers and ranges (a,b-c,d,..)
  1213. *
  1214. * Parameters :
  1215. * 1 : portlist = String with list
  1216. * 2 : port = port to check
  1217. *
  1218. * Returns : 0 => no match
  1219. * 1 => match
  1220. *
  1221. *********************************************************************/
  1222. int match_portlist(const char *portlist, int port)
  1223. {
  1224. char *min, *max, *next, *portlist_copy;
  1225. min = portlist_copy = strdup_or_die(portlist);
  1226. /*
  1227. * Zero-terminate first item and remember offset for next
  1228. */
  1229. if (NULL != (next = strchr(portlist_copy, (int) ',')))
  1230. {
  1231. *next++ = '\0';
  1232. }
  1233. /*
  1234. * Loop through all items, checking for match
  1235. */
  1236. while (NULL != min)
  1237. {
  1238. if (NULL == (max = strchr(min, (int) '-')))
  1239. {
  1240. /*
  1241. * No dash, check for equality
  1242. */
  1243. if (port == atoi(min))
  1244. {
  1245. freez(portlist_copy);
  1246. return(1);
  1247. }
  1248. }
  1249. else
  1250. {
  1251. /*
  1252. * This is a range, so check if between min and max,
  1253. * or, if max was omitted, between min and 65K
  1254. */
  1255. *max++ = '\0';
  1256. if (port >= atoi(min) && port <= (atoi(max) ? atoi(max) : 65535))
  1257. {
  1258. freez(portlist_copy);
  1259. return(1);
  1260. }
  1261. }
  1262. /*
  1263. * Jump to next item
  1264. */
  1265. min = next;
  1266. /*
  1267. * Zero-terminate next item and remember offset for n+1
  1268. */
  1269. if ((NULL != next) && (NULL != (next = strchr(next, (int) ','))))
  1270. {
  1271. *next++ = '\0';
  1272. }
  1273. }
  1274. freez(portlist_copy);
  1275. return 0;
  1276. }
  1277. /*********************************************************************
  1278. *
  1279. * Function : parse_forwarder_address
  1280. *
  1281. * Description : Parse out the username, password, host and port from
  1282. * a forwarder address.
  1283. *
  1284. * Parameters :
  1285. * 1 : address = The forwarder address to parse.
  1286. * 2 : hostname = Used to return the hostname. NULL on error.
  1287. * 3 : port = Used to return the port. Untouched if no port
  1288. * is specified.
  1289. * 4 : username = Used to return the username if any.
  1290. * 5 : password = Used to return the password if any.
  1291. *
  1292. * Returns : JB_ERR_OK on success
  1293. * JB_ERR_MEMORY on out of memory
  1294. * JB_ERR_PARSE on malformed address.
  1295. *
  1296. *********************************************************************/
  1297. jb_err parse_forwarder_address(char *address, char **hostname, int *port,
  1298. char **username, char **password)
  1299. {
  1300. char *p;
  1301. char *tmp;
  1302. tmp = *hostname = strdup_or_die(address);
  1303. /* Parse username and password */
  1304. if (username && password && (NULL != (p = strchr(*hostname, '@'))))
  1305. {
  1306. *p++ = '\0';
  1307. *username = strdup_or_die(*hostname);
  1308. *hostname = strdup_or_die(p);
  1309. if (NULL != (p = strchr(*username, ':')))
  1310. {
  1311. *p++ = '\0';
  1312. *password = strdup_or_die(p);
  1313. }
  1314. freez(tmp);
  1315. }
  1316. /* Parse hostname and port */
  1317. p = *hostname;
  1318. if ((*p == '[') && (NULL == strchr(p, ']')))
  1319. {
  1320. /* XXX: Should do some more validity checks here. */
  1321. return JB_ERR_PARSE;
  1322. }
  1323. if ((**hostname == '[') && (NULL != (p = strchr(*hostname, ']'))))
  1324. {
  1325. *p++ = '\0';
  1326. memmove(*hostname, (*hostname + 1), (size_t)(p - *hostname));
  1327. if (*p == ':')
  1328. {
  1329. *port = (int)strtol(++p, NULL, 0);
  1330. }
  1331. }
  1332. else if (NULL != (p = strchr(*hostname, ':')))
  1333. {
  1334. *p++ = '\0';
  1335. *port = (int)strtol(p, NULL, 0);
  1336. }
  1337. return JB_ERR_OK;
  1338. }
  1339. /*
  1340. Local Variables:
  1341. tab-width: 3
  1342. end:
  1343. */