PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/nmap-update/nmap-update.c

https://github.com/prakashgamit/nmap
C | 1327 lines | 1126 code | 185 blank | 16 comment | 259 complexity | d873d4565c888f8e2cfb7b1e23e7923f MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-2.0, LGPL-2.1
  1. #include <ctype.h>
  2. #include <errno.h>
  3. #include <stdarg.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <time.h>
  8. #include <sys/stat.h>
  9. #include "nbase.h"
  10. #ifndef WIN32
  11. #include <dirent.h>
  12. #include <getopt.h>
  13. #include <pwd.h>
  14. #include <unistd.h>
  15. #include "config.h"
  16. #else
  17. #include <shlobj.h>
  18. #include "win_config.h"
  19. #endif
  20. /* See the file tools/examples/minimal_client.c in the Subversion source
  21. directory for an example of using the svn_client API. */
  22. #if HAVE_SUBVERSION_1_SVN_CLIENT_H
  23. #include <subversion-1/svn_client.h>
  24. #include <subversion-1/svn_cmdline.h>
  25. #include <subversion-1/svn_opt.h>
  26. #include <subversion-1/svn_pools.h>
  27. #include <subversion-1/svn_types.h>
  28. #else
  29. #include <svn_client.h>
  30. #include <svn_cmdline.h>
  31. #include <svn_opt.h>
  32. #include <svn_pools.h>
  33. #include <svn_types.h>
  34. #endif
  35. /* From svn_auth.c. */
  36. svn_error_t *
  37. nmap_update_svn_cmdline_setup_auth_baton(svn_auth_baton_t **ab,
  38. svn_boolean_t non_interactive,
  39. const char *auth_username,
  40. const char *auth_password,
  41. const char *config_dir,
  42. svn_boolean_t no_auth_cache,
  43. svn_config_t *cfg,
  44. svn_cancel_func_t cancel_func,
  45. void *cancel_baton,
  46. apr_pool_t *pool);
  47. #include "default_channel.h"
  48. #ifdef WIN32
  49. #define PATHSEP "\\"
  50. #else
  51. #define PATHSEP "/"
  52. #endif
  53. static const char *DEFAULT_SVN_REPO = "https://svn.nmap.org/updates";
  54. static const char *DEFAULT_CHANNELS[] = { DEFAULT_CHANNEL };
  55. /* Internal error handling. */
  56. #define NELEMS(a) (sizeof(a) / sizeof(*a))
  57. #define internal_error(msg) \
  58. do {\
  59. fprintf(stderr, "%s:%d: internal error: %s.\n", __FILE__, __LINE__, msg); \
  60. abort(); \
  61. } while (0)
  62. #define internal_assert(expr) \
  63. do { \
  64. if (!(expr)) \
  65. internal_error("assertion failed: " #expr); \
  66. } while (0)
  67. static char *safe_strdup(const char *s)
  68. {
  69. char *t;
  70. size_t len;
  71. len = strlen(s);
  72. t = safe_malloc(len + 1);
  73. memcpy(t, s, len);
  74. t[len] = '\0';
  75. return t;
  76. }
  77. static int streq(const char *a, const char *b)
  78. {
  79. return strcmp(a, b) == 0;
  80. }
  81. static char *string_make(const char *begin, const char *end)
  82. {
  83. char *s;
  84. s = safe_malloc(end - begin + 1);
  85. memcpy(s, begin, end - begin);
  86. s[end - begin] = '\0';
  87. return s;
  88. }
  89. static char *strbuf_append(char **buf, size_t *size, size_t *offset, const char *s, size_t n)
  90. {
  91. internal_assert(*offset <= *size);
  92. /* Double the buffer size if necessary. */
  93. if (n >= *size - *offset) {
  94. *size = (*size + n) * 2;
  95. *buf = safe_realloc(*buf, *size + 1);
  96. }
  97. memcpy(*buf + *offset, s, n);
  98. *offset += n;
  99. (*buf)[*offset] = '\0';
  100. return *buf;
  101. }
  102. /* Append a '\0'-terminated string as with strbuf_append. */
  103. static char *strbuf_append_str(char **buf, size_t *size, size_t *offset, const char *s)
  104. {
  105. return strbuf_append(buf, size, offset, s, strlen(s));
  106. }
  107. static char *strbuf_append_char(char **buf, size_t *size, size_t *offset, char c)
  108. {
  109. return strbuf_append(buf, size, offset, &c, 1);
  110. }
  111. static char *strbuf_trim(char **buf, size_t *size, size_t *offset)
  112. {
  113. if (*offset < *size) {
  114. *size = *offset;
  115. *buf = safe_realloc(*buf, *size + 1);
  116. }
  117. internal_assert((*buf)[*size] == '\0');
  118. return *buf;
  119. }
  120. static char *string_unescape(const char *escaped)
  121. {
  122. char *buf;
  123. size_t size, offset;
  124. const char *p;
  125. buf = NULL;
  126. size = 0;
  127. offset = 0;
  128. p = escaped;
  129. while (*p != '\0') {
  130. char hex[3], *tail;
  131. unsigned long byte;
  132. /* We support backslash escapes for '\\' and '"', and \xXX
  133. hexadecimal only. */
  134. if (*p == '\\') {
  135. p++;
  136. switch (*p) {
  137. case '\\':
  138. case '"':
  139. strbuf_append_char(&buf, &size, &offset, *p);
  140. p++;
  141. break;
  142. case 'x':
  143. p++;
  144. if (!(isxdigit(*p) && isxdigit(*(p + 1))))
  145. goto bail;
  146. memcpy(hex, p, 2);
  147. hex[2] = '\0';
  148. errno = 0;
  149. byte = strtoul(hex, &tail, 16);
  150. if (errno != 0 || byte > 255 || *tail != '\0')
  151. goto bail;
  152. strbuf_append_char(&buf, &size, &offset, (char) byte);
  153. p += 2;
  154. break;
  155. default:
  156. goto bail;
  157. break;
  158. }
  159. } else {
  160. strbuf_append_char(&buf, &size, &offset, *p);
  161. p++;
  162. }
  163. }
  164. return strbuf_trim(&buf, &size, &offset);
  165. bail:
  166. if (buf != NULL)
  167. free(buf);
  168. return NULL;
  169. }
  170. /* Return a newly allocated string that is the concatenation of all the va_list
  171. args, separated by join:
  172. str1 JOIN str2 JOIN str3 ...
  173. The final argument must be NULL. */
  174. static char *strs_vjoin(const char *join, const char *first, va_list ap)
  175. {
  176. char *buf;
  177. size_t size, offset;
  178. const char *p;
  179. internal_assert(first != NULL);
  180. buf = NULL;
  181. size = 0;
  182. offset = 0;
  183. strbuf_append_str(&buf, &size, &offset, first);
  184. while ((p = va_arg(ap, const char *)) != NULL) {
  185. strbuf_append_str(&buf, &size, &offset, join);
  186. strbuf_append_str(&buf, &size, &offset, p);
  187. }
  188. strbuf_trim(&buf, &size, &offset);
  189. return buf;
  190. }
  191. static char *strs_cat(const char *first, ...)
  192. {
  193. va_list ap;
  194. char *result;
  195. va_start(ap, first);
  196. result = strs_vjoin("", first, ap);
  197. va_end(ap);
  198. return result;
  199. }
  200. static char *path_join(const char *first, ...)
  201. {
  202. va_list ap;
  203. char *result;
  204. va_start(ap, first);
  205. result = strs_vjoin(PATHSEP, first, ap);
  206. va_end(ap);
  207. return result;
  208. }
  209. #ifdef WIN32
  210. static char *get_user_dir(const char *subdir) {
  211. char appdata[MAX_PATH];
  212. if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata) != S_OK)
  213. return NULL;
  214. return path_join(appdata, "nmap", subdir, NULL);
  215. }
  216. #else
  217. static char *get_user_dir(const char *subdir) {
  218. static struct passwd *pw;
  219. errno = 0;
  220. pw = getpwuid(getuid());
  221. if (pw == NULL)
  222. return NULL;
  223. return path_join(pw->pw_dir, ".nmap", subdir, NULL);
  224. }
  225. #endif
  226. static char *get_install_dir(void) {
  227. return get_user_dir("updates");
  228. }
  229. static char *get_staging_dir(void) {
  230. return get_user_dir("updates-staging");
  231. }
  232. static char *get_conf_filename(void) {
  233. return get_user_dir("nmap-update.conf");
  234. }
  235. /* Configuration file parsing. */
  236. enum token_type {
  237. TOKEN_ERROR,
  238. TOKEN_EOL,
  239. TOKEN_EOF,
  240. TOKEN_WORD,
  241. TOKEN_EQUALS,
  242. TOKEN_STRING,
  243. };
  244. struct config_parser {
  245. FILE *fp;
  246. unsigned long lineno;
  247. };
  248. struct config_entry {
  249. char *key;
  250. char *value;
  251. };
  252. static void config_entry_free(struct config_entry *entry)
  253. {
  254. free(entry->key);
  255. free(entry->value);
  256. }
  257. static int config_parser_open(const char *filename, struct config_parser *cp)
  258. {
  259. cp->fp = fopen(filename, "r");
  260. if (cp->fp == NULL)
  261. return -1;
  262. cp->lineno = 1;
  263. return 0;
  264. }
  265. static int config_parser_close(struct config_parser *cp)
  266. {
  267. int ret;
  268. ret = fclose(cp->fp);
  269. if (ret == EOF)
  270. return -1;
  271. return 0;
  272. }
  273. static int is_word_char(int c)
  274. {
  275. return c != EOF && !isspace(c) && c != '"' && c != '#';
  276. }
  277. static char *read_quoted_string(struct config_parser *cp)
  278. {
  279. char *buf, *unescaped;
  280. size_t size, offset;
  281. int c;
  282. buf = NULL;
  283. size = 0;
  284. offset = 0;
  285. for (;;) {
  286. errno = 0;
  287. c = fgetc(cp->fp);
  288. if (c == EOF)
  289. /* EOF in the middle of a string is always an error. */
  290. return NULL;
  291. if (c == '\n')
  292. return NULL;
  293. if (c == '"')
  294. break;
  295. if (c == '\\') {
  296. strbuf_append_char(&buf, &size, &offset, c);
  297. errno = 0;
  298. c = fgetc(cp->fp);
  299. if (c == EOF)
  300. return NULL;
  301. }
  302. strbuf_append_char(&buf, &size, &offset, c);
  303. }
  304. unescaped = string_unescape(buf);
  305. free(buf);
  306. return unescaped;
  307. }
  308. static enum token_type config_parser_read_token(struct config_parser *cp,
  309. char **token)
  310. {
  311. size_t size, offset;
  312. unsigned long prev_lineno;
  313. int c;
  314. *token = NULL;
  315. size = 0;
  316. offset = 0;
  317. /* Skip comments and blank space. */
  318. prev_lineno = cp->lineno;
  319. do {
  320. errno = 0;
  321. while (isspace(c = fgetc(cp->fp))) {
  322. if (c == '\n')
  323. cp->lineno++;
  324. }
  325. if (c == EOF) {
  326. if (errno != 0)
  327. goto bail;
  328. *token = NULL;
  329. return TOKEN_EOF;
  330. }
  331. if (c == '#') {
  332. while ((c = fgetc(cp->fp)) != EOF && c != '\n')
  333. ;
  334. if (c == EOF) {
  335. if (errno != 0)
  336. goto bail;
  337. *token = NULL;
  338. return TOKEN_EOF;
  339. } else if (c == '\n') {
  340. cp->lineno++;
  341. }
  342. }
  343. } while (isspace(c) || c == '#');
  344. /* Collapse multiple consecutive line endings. */
  345. if (cp->lineno != prev_lineno) {
  346. ungetc(c, cp->fp);
  347. *token = NULL;
  348. return TOKEN_EOL;
  349. }
  350. if (c == '=') {
  351. strbuf_append_char(token, &size, &offset, c);
  352. return TOKEN_EQUALS;
  353. } else if (is_word_char(c)) {
  354. while (is_word_char(c)) {
  355. strbuf_append_char(token, &size, &offset, c);
  356. errno = 0;
  357. c = fgetc(cp->fp);
  358. if (c == EOF && errno != 0)
  359. goto bail;
  360. }
  361. return TOKEN_WORD;
  362. } else if (c == '"') {
  363. char *qs;
  364. qs = read_quoted_string(cp);
  365. if (qs == NULL)
  366. goto bail;
  367. *token = safe_strdup(qs);
  368. return TOKEN_STRING;
  369. } else {
  370. goto bail;
  371. }
  372. bail:
  373. if (*token != NULL)
  374. free(*token);
  375. *token = NULL;
  376. return TOKEN_ERROR;
  377. }
  378. static int config_parser_next(struct config_parser *cp, struct config_entry *entry)
  379. {
  380. char *token;
  381. enum token_type type;
  382. while ((type = config_parser_read_token(cp, &token)) == TOKEN_EOL)
  383. ;
  384. if (type == TOKEN_EOF) {
  385. free(token);
  386. return 0;
  387. }
  388. if (type != TOKEN_WORD) {
  389. free(token);
  390. return -1;
  391. }
  392. entry->key = token;
  393. type = config_parser_read_token(cp, &token);
  394. if (type != TOKEN_EQUALS) {
  395. free(token);
  396. return -1;
  397. }
  398. free(token);
  399. type = config_parser_read_token(cp, &token);
  400. if (!(type == TOKEN_WORD || type == TOKEN_STRING)) {
  401. free(token);
  402. return -1;
  403. }
  404. entry->value = token;
  405. return 1;
  406. }
  407. /* Global state. */
  408. static char *program_name;
  409. static struct {
  410. int verbose;
  411. const char *install_dir;
  412. const char *staging_dir;
  413. const char *conf_filename;
  414. const char **channels;
  415. unsigned int num_channels;
  416. char *svn_repo;
  417. char *username;
  418. char *password;
  419. } options;
  420. struct metadata {
  421. int is_expired;
  422. time_t expiry_date;
  423. };
  424. static void metadata_init(struct metadata *metadata)
  425. {
  426. metadata->is_expired = 0;
  427. metadata->expiry_date = 0;
  428. }
  429. static void init_options(void)
  430. {
  431. options.verbose = 0;
  432. options.install_dir = get_install_dir();
  433. if (options.install_dir == NULL) {
  434. fprintf(stderr, "Could not find an install directory: %s.\n",
  435. strerror(errno));
  436. exit(1);
  437. }
  438. options.staging_dir = get_staging_dir();
  439. if (options.staging_dir == NULL) {
  440. fprintf(stderr, "Could not find a staging directory: %s.\n",
  441. strerror(errno));
  442. exit(1);
  443. }
  444. options.conf_filename = get_conf_filename();
  445. if (options.conf_filename == NULL) {
  446. fprintf(stderr, "Could not find the configuration file: %s.\n",
  447. strerror(errno));
  448. exit(1);
  449. }
  450. options.channels = DEFAULT_CHANNELS;
  451. options.num_channels = NELEMS(DEFAULT_CHANNELS);
  452. options.svn_repo = NULL;
  453. options.username = NULL;
  454. options.password = NULL;
  455. }
  456. static int read_config_file(const char *conf_filename)
  457. {
  458. struct config_parser cp;
  459. struct config_entry entry;
  460. int ret;
  461. if (options.verbose)
  462. printf("Trying to open configuration file %s.\n", conf_filename);
  463. errno = 0;
  464. if (config_parser_open(conf_filename, &cp) == -1) {
  465. if (options.verbose)
  466. printf("Failed to open %s: %s.\n", conf_filename, strerror(errno));
  467. return -1;
  468. }
  469. while ((ret = config_parser_next(&cp, &entry)) > 0) {
  470. if (streq(entry.key, "username")) {
  471. if (options.username != NULL) {
  472. fprintf(stderr, "Warning: %s:%lu: duplicate \"%s\".\n",
  473. conf_filename, cp.lineno, entry.key);
  474. free(options.username);
  475. }
  476. options.username = safe_strdup(entry.value);
  477. } else if (streq(entry.key, "password")) {
  478. if (options.password != NULL) {
  479. fprintf(stderr, "Warning: %s:%lu: duplicate \"%s\".\n",
  480. conf_filename, cp.lineno, entry.key);
  481. free(options.password);
  482. }
  483. options.password = safe_strdup(entry.value);
  484. } else if (streq(entry.key, "repo")) {
  485. if (options.svn_repo != NULL) {
  486. fprintf(stderr, "Warning: %s:%lu: duplicate \"%s\".\n",
  487. conf_filename, cp.lineno, entry.key);
  488. free(options.svn_repo);
  489. }
  490. options.svn_repo = safe_strdup(entry.value);
  491. } else {
  492. fprintf(stderr, "Warning: %s:%lu: unknown key \"%s\".\n",
  493. conf_filename, cp.lineno, entry.key);
  494. }
  495. config_entry_free(&entry);
  496. }
  497. if (ret == -1) {
  498. fprintf(stderr, "Parse error on line %lu of %s.\n",
  499. cp.lineno, conf_filename);
  500. exit(1);
  501. }
  502. errno = 0;
  503. if (config_parser_close(&cp) == -1) {
  504. if (options.verbose)
  505. printf("Failed to close %s: %s.\n", conf_filename, strerror(errno));
  506. return -1;
  507. }
  508. return 0;
  509. }
  510. static int parse_date(const char *s, time_t *t)
  511. {
  512. struct tm tm = {0};
  513. if (sscanf(s, "%d-%d-%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday) != 3)
  514. return -1;
  515. tm.tm_year -= 1900;
  516. tm.tm_mon -= 1;
  517. *t = mktime(&tm);
  518. if (*t == -1)
  519. return -1;
  520. return 0;
  521. }
  522. static int date_is_after(time_t t, time_t now)
  523. {
  524. return difftime(t, now) > 0;
  525. }
  526. static int read_metadata_file(const char *metadata_filename, struct metadata *metadata)
  527. {
  528. struct config_parser cp;
  529. struct config_entry entry;
  530. int ret;
  531. errno = 0;
  532. if (config_parser_open(metadata_filename, &cp) == -1) {
  533. /* A missing file is not an error for metadata. */
  534. return 0;
  535. }
  536. while ((ret = config_parser_next(&cp, &entry)) > 0) {
  537. if (streq(entry.key, "expired")) {
  538. if (parse_date(entry.value, &metadata->expiry_date) == -1) {
  539. fprintf(stderr, "Warning: %s:%lu: can't parse date \"%s\".\n",
  540. metadata_filename, cp.lineno, entry.value);
  541. } else {
  542. if (date_is_after(time(NULL), metadata->expiry_date))
  543. metadata->is_expired = 1;
  544. }
  545. } else {
  546. fprintf(stderr, "Warning: %s:%lu: unknown key \"%s\".\n",
  547. metadata_filename, cp.lineno, entry.key);
  548. }
  549. config_entry_free(&entry);
  550. }
  551. if (ret == -1) {
  552. fprintf(stderr, "Parse error on line %lu of %s.\n",
  553. cp.lineno, metadata_filename);
  554. config_parser_close(&cp);
  555. return -1;
  556. }
  557. errno = 0;
  558. if (config_parser_close(&cp) == -1) {
  559. if (options.verbose)
  560. printf("Failed to close %s: %s.\n", metadata_filename, strerror(errno));
  561. return -1;
  562. }
  563. return 0;
  564. }
  565. static void usage(FILE *fp)
  566. {
  567. char *install_dir;
  568. internal_assert(program_name != NULL);
  569. install_dir = get_install_dir();
  570. fprintf(fp, "\
  571. Usage: %s [-d INSTALL_DIR] [CHANNEL...]\n\
  572. Updates system-independent Nmap files. By default the new files are installed to\n\
  573. %s. Each CHANNEL is a version number like \"" DEFAULT_CHANNEL "\".\n\
  574. \n\
  575. -d DIR install files to DIR (default %s).\n\
  576. -h, --help show this help.\n\
  577. -r, --repo REPO use REPO as SVN repository and path (default %s).\n\
  578. -v, --verbose be more verbose.\n\
  579. --username USERNAME use this username.\n\
  580. --password PASSWORE use this password.\n\
  581. ", program_name, install_dir, install_dir, DEFAULT_SVN_REPO);
  582. free(install_dir);
  583. }
  584. static void usage_error(void)
  585. {
  586. usage(stderr);
  587. exit(1);
  588. }
  589. static const char *try_channels(const char *channels[], unsigned int num_channels);
  590. static int stage_and_install(const char *channel);
  591. static int stage_channel(const char *channel, const char *staging_dir);
  592. static int install(const char *staging_dir, const char *install_dir);
  593. static int channel_is_expired(const char *channel, time_t *expiry_date);
  594. static void summarize_options(void)
  595. {
  596. unsigned int i;
  597. printf("Installing to directory: %s.\n", options.install_dir);
  598. printf("Using staging directory: %s.\n", options.staging_dir);
  599. printf("Using channels:");
  600. for (i = 0; i < options.num_channels; i++)
  601. printf(" %s", options.channels[i]);
  602. printf(".\n");
  603. }
  604. const struct option LONG_OPTIONS[] = {
  605. { "help", no_argument, NULL, 'h' },
  606. { "repo", required_argument, NULL, 'r' },
  607. { "verbose", required_argument, NULL, 'v' },
  608. { "username", required_argument, NULL, '?' },
  609. { "password", required_argument, NULL, '?' },
  610. };
  611. int main(int argc, char *argv[])
  612. {
  613. int opt, longoptidx;
  614. const char *successful_channel;
  615. const char *username, *password, *svn_repo;
  616. time_t expiry_date;
  617. internal_assert(argc > 0);
  618. program_name = argv[0];
  619. init_options();
  620. if (svn_cmdline_init(program_name, stderr) != 0)
  621. internal_error("svn_cmdline_init");
  622. username = NULL;
  623. password = NULL;
  624. svn_repo = NULL;
  625. while ((opt = getopt_long(argc, argv, "d:hr:v", LONG_OPTIONS, &longoptidx)) != -1) {
  626. if (opt == 'd') {
  627. options.install_dir = optarg;
  628. } else if (opt == 'h') {
  629. usage(stdout);
  630. exit(0);
  631. } else if (opt == 'r') {
  632. svn_repo = optarg;
  633. } else if (opt == 'v') {
  634. options.verbose = 1;
  635. } else if (opt == '?' && streq(LONG_OPTIONS[longoptidx].name, "username")) {
  636. username = optarg;
  637. } else if (opt == '?' && streq(LONG_OPTIONS[longoptidx].name, "password")) {
  638. password = optarg;
  639. } else {
  640. usage_error();
  641. }
  642. }
  643. /* User-specified channels. */
  644. if (optind < argc) {
  645. options.channels = (const char **) argv + optind;
  646. options.num_channels = argc - optind;
  647. }
  648. internal_assert(options.channels != NULL);
  649. internal_assert(options.num_channels > 0);
  650. if (options.verbose)
  651. summarize_options();
  652. read_config_file(options.conf_filename);
  653. /* Default options. */
  654. if (options.svn_repo == NULL)
  655. options.svn_repo = safe_strdup(DEFAULT_SVN_REPO);
  656. /* Possibly override configuration file. */
  657. if (username != NULL) {
  658. free(options.username);
  659. options.username = safe_strdup(username);
  660. }
  661. if (password != NULL) {
  662. free(options.password);
  663. options.password = safe_strdup(password);
  664. }
  665. if (svn_repo != NULL) {
  666. free(options.svn_repo);
  667. options.svn_repo = safe_strdup(svn_repo);
  668. }
  669. successful_channel = try_channels(options.channels, options.num_channels);
  670. if (successful_channel != NULL && channel_is_expired(successful_channel, &expiry_date)) {
  671. fprintf(stderr, "\
  672. \n\
  673. UPDATE CHANNEL %s HAS EXPIRED:\n\
  674. \n\
  675. The channel %s has expired and won't receive any more\n\
  676. updates. Visit http://nmap.org for a newer Nmap release with \n\
  677. supported updates.\n\
  678. ", successful_channel, successful_channel);
  679. }
  680. if (successful_channel == NULL && options.username == NULL) {
  681. fprintf(stderr, "\
  682. \n\
  683. Could not stage any channels and don't have authentication credentials.\n\
  684. \n\
  685. Edit the file %s and enter your username and password. For example:\n\
  686. username = user\n\
  687. password = secret\n\
  688. ", options.conf_filename);
  689. }
  690. if (successful_channel != NULL)
  691. return 0;
  692. else
  693. return 1;
  694. }
  695. static const char *try_channels(const char *channels[], unsigned int num_channels)
  696. {
  697. unsigned int i;
  698. for (i = 0; i < num_channels; i++) {
  699. if (stage_and_install(channels[i]) == 0)
  700. return channels[i];
  701. }
  702. return NULL;
  703. }
  704. static void fatal_err_svn(svn_error_t *err)
  705. {
  706. svn_handle_error2(err, stderr, TRUE, "nmap-update: ");
  707. }
  708. static svn_error_t *checkout_svn(const char *url, const char *path)
  709. {
  710. svn_error_t *err;
  711. apr_pool_t *pool;
  712. svn_opt_revision_t peg_revision, revision;
  713. svn_client_ctx_t *ctx;
  714. svn_revnum_t revnum;
  715. svn_config_t *cfg;
  716. peg_revision.kind = svn_opt_revision_unspecified;
  717. revision.kind = svn_opt_revision_head;
  718. pool = svn_pool_create(NULL);
  719. err = svn_client_create_context(&ctx, pool);
  720. if (err != NULL)
  721. fatal_err_svn(err);
  722. /* The creation of this directory is needed to cache credentials. */
  723. err = svn_config_ensure(NULL, pool);
  724. if (err != NULL)
  725. fatal_err_svn(err);
  726. err = svn_config_get_config(&ctx->config, NULL, pool);
  727. if (err != NULL)
  728. fatal_err_svn(err);
  729. cfg = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
  730. APR_HASH_KEY_STRING);
  731. svn_config_set_bool(cfg, SVN_CONFIG_SECTION_GLOBAL,
  732. SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, TRUE);
  733. nmap_update_svn_cmdline_setup_auth_baton(&ctx->auth_baton,
  734. FALSE, /* non_interactive */
  735. options.username, /* username */
  736. options.password, /* password */
  737. NULL, /* config_dir */
  738. FALSE, /* no_auth_cache */
  739. cfg, /* cfg */
  740. NULL, /* cancel_func */
  741. NULL, /* cancel_baton */
  742. pool);
  743. err = svn_client_checkout2(&revnum, url, path,
  744. &peg_revision, &revision,
  745. TRUE, /* recurse */
  746. TRUE, /* ignore_externals */
  747. ctx, pool);
  748. svn_pool_destroy(pool);
  749. if (err != NULL)
  750. return err;
  751. printf("Checked out r%lu\n", (unsigned long) revnum);
  752. return SVN_NO_ERROR;
  753. }
  754. static int stage_and_install(const char *channel)
  755. {
  756. char *staging_dir, *install_dir;
  757. int rc;
  758. internal_assert(options.staging_dir != NULL);
  759. staging_dir = path_join(options.staging_dir, channel, NULL);
  760. rc = stage_channel(channel, staging_dir);
  761. if (rc == -1) {
  762. free(staging_dir);
  763. return -1;
  764. }
  765. install_dir = path_join(options.install_dir, channel, NULL);
  766. rc = install(staging_dir, install_dir);
  767. free(staging_dir);
  768. free(install_dir);
  769. return rc;
  770. }
  771. static int stage_channel(const char *channel, const char *staging_dir)
  772. {
  773. char *svn_url;
  774. svn_error_t *err;
  775. int rc;
  776. rc = 0;
  777. svn_url = strs_cat(options.svn_repo, "/", channel, NULL);
  778. if (options.verbose)
  779. printf("Checking out %s to %s.\n", svn_url, staging_dir);
  780. printf("\
  781. \n\
  782. The Nmap Updater is currently only available to a small set of users\n\
  783. for testing purposes. We hope to expand it in the future.\n\
  784. \n\
  785. ");
  786. err = checkout_svn(svn_url, staging_dir);
  787. if (err != NULL) {
  788. svn_handle_error2(err, stderr, FALSE, "nmap-update: ");
  789. fprintf(stderr, "Error checking out %s.\n", svn_url);
  790. rc = -1;
  791. }
  792. free(svn_url);
  793. return rc;
  794. }
  795. static int channel_is_expired(const char *channel, time_t *expiry_date)
  796. {
  797. char *metadata_filename;
  798. struct metadata metadata;
  799. int rc;
  800. metadata_init(&metadata);
  801. metadata_filename = path_join(options.staging_dir, channel, "metadata.conf", NULL);
  802. rc = read_metadata_file(metadata_filename, &metadata);
  803. if (rc == -1) {
  804. fprintf(stderr, "Can't read metadata file %s.\n", metadata_filename);
  805. free(metadata_filename);
  806. exit(1);
  807. }
  808. free(metadata_filename);
  809. *expiry_date = metadata.expiry_date;
  810. return metadata.is_expired;
  811. }
  812. static int copy_tree(const char *from_dirname, const char *to_dirname);
  813. static int rename_file(const char *from_filename, const char *to_filename);
  814. static int install(const char *staging_dir, const char *install_dir)
  815. {
  816. if (options.verbose)
  817. printf("Installing from %s to %s.\n", staging_dir, install_dir);
  818. return copy_tree(staging_dir, install_dir);
  819. }
  820. static int copy_file(const char *from_filename, const char *to_filename)
  821. {
  822. char buf[BUFSIZ];
  823. char *tmp_filename;
  824. FILE *from_fd, *tmp_fd;
  825. int rc, from_rc, tmp_rc;
  826. size_t nr, nw;
  827. tmp_filename = NULL;
  828. from_fd = NULL;
  829. tmp_fd = NULL;
  830. errno = 0;
  831. from_fd = fopen(from_filename, "rb");
  832. if (from_fd == NULL) {
  833. fprintf(stderr, "Can't open %s: %s.\n", from_filename, strerror(errno));
  834. goto bail;
  835. }
  836. tmp_filename = strs_cat(to_filename, "-tmp", NULL);
  837. errno = 0;
  838. tmp_fd = fopen(tmp_filename, "wb");
  839. if (tmp_fd == NULL) {
  840. fprintf(stderr, "Can't open %s: %s.\n", tmp_filename, strerror(errno));
  841. goto bail;
  842. }
  843. errno = 0;
  844. while ((nr = fread(buf, 1, sizeof(buf), from_fd)) != 0) {
  845. errno = 0;
  846. nw = fwrite(buf, 1, nr, tmp_fd);
  847. if (nw != nr || errno != 0) {
  848. printf("%lu %lu\n", nw, nr);
  849. fprintf(stderr, "Error writing to %s: %s.\n", tmp_filename, strerror(errno));
  850. goto bail;
  851. }
  852. }
  853. if (errno != 0) {
  854. fprintf(stderr, "Error reading from %s: %s.\n", from_filename, strerror(errno));
  855. goto bail;
  856. }
  857. from_rc = fclose(from_fd);
  858. from_fd = NULL;
  859. if (from_rc == -1) {
  860. fprintf(stderr, "Can't close %s: %s.\n", from_filename, strerror(errno));
  861. goto bail;
  862. }
  863. tmp_rc = fclose(tmp_fd);
  864. tmp_fd = NULL;
  865. if (tmp_rc == -1) {
  866. fprintf(stderr, "Can't close %s: %s.\n", to_filename, strerror(errno));
  867. goto bail;
  868. }
  869. rc = rename_file(tmp_filename, to_filename);
  870. if (rc == -1) {
  871. fprintf(stderr, "Can't rename %s to %s: %s.\n",
  872. tmp_filename, to_filename, strerror(errno));
  873. goto bail;
  874. }
  875. free(tmp_filename);
  876. tmp_filename = NULL;
  877. return 0;
  878. bail:
  879. if (from_fd != NULL)
  880. fclose(from_fd);
  881. if (tmp_fd != NULL)
  882. fclose(tmp_fd);
  883. if (tmp_filename != NULL)
  884. free(tmp_filename);
  885. return -1;
  886. }
  887. static int is_pathsep(int c)
  888. {
  889. #ifdef WIN32
  890. return c == '/' || c == '\\';
  891. #else
  892. return c == '/';
  893. #endif
  894. }
  895. static char *parent_dir(const char *path)
  896. {
  897. const char *p;
  898. p = path + strlen(path) - 1;
  899. while (p > path && is_pathsep(*p))
  900. p--;
  901. while (p > path && !is_pathsep(*p))
  902. p--;
  903. while (p > path && is_pathsep(*p))
  904. p--;
  905. if (p == path)
  906. return safe_strdup("/");
  907. return string_make(path, p + 1);
  908. }
  909. #ifdef WIN32
  910. static int rename_file(const char *from_filename, const char *to_filename)
  911. {
  912. int rc;
  913. /* Windows rename doesn't remove the destination if it exists. */
  914. errno = 0;
  915. rc = unlink(to_filename);
  916. if (rc == -1 && errno != ENOENT)
  917. return -1;
  918. return rename(from_filename, to_filename);
  919. }
  920. static int makedir(const char *dirname)
  921. {
  922. return CreateDirectory(dirname, NULL) != 0 ? 0 : -1;
  923. }
  924. static int makedirs(const char *dirname)
  925. {
  926. char *parent;
  927. int rc;
  928. rc = makedir(dirname);
  929. if (rc == 0 || GetLastError() == ERROR_ALREADY_EXISTS)
  930. return 0;
  931. if (GetLastError() != ERROR_PATH_NOT_FOUND)
  932. return -1;
  933. parent = parent_dir(dirname);
  934. rc = makedirs(parent);
  935. free(parent);
  936. if (rc == -1)
  937. return -1;
  938. rc = makedir(dirname);
  939. if (rc == -1)
  940. return -1;
  941. return rc;
  942. }
  943. static int copy_tree(const char *from_dirname, const char *to_dirname)
  944. {
  945. WIN32_FIND_DATA ffd;
  946. HANDLE find_handle;
  947. DWORD dwError;
  948. char *from_pattern;
  949. int rc;
  950. rc = makedirs(to_dirname);
  951. if (rc == -1) {
  952. fprintf(stderr, "Can't create the directory %s: %s.\n",
  953. to_dirname, strerror(errno));
  954. return -1;
  955. }
  956. from_pattern = path_join(from_dirname, "*", NULL);
  957. find_handle = FindFirstFile(from_pattern, &ffd);
  958. free(from_pattern);
  959. if (find_handle == INVALID_HANDLE_VALUE) {
  960. fprintf(stderr, "Can't open the directory %s.\n", from_dirname);
  961. return -1;
  962. }
  963. do {
  964. char *from_path, *to_path;
  965. int error;
  966. from_path = path_join(from_dirname, ffd.cFileName, NULL);
  967. to_path = path_join(to_dirname, ffd.cFileName, NULL);
  968. error = 0;
  969. if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  970. if (streq(ffd.cFileName, ".") || streq(ffd.cFileName, ".."))
  971. continue;
  972. if (streq(ffd.cFileName, ".svn"))
  973. continue;
  974. rc = makedirs(to_path);
  975. if (rc == 0) {
  976. rc = copy_tree(from_path, to_path);
  977. if (rc == -1)
  978. error = 1;
  979. } else {
  980. error = 1;
  981. }
  982. } else {
  983. rc = copy_file(from_path, to_path);
  984. if (rc == -1)
  985. error = 1;
  986. }
  987. free(from_path);
  988. free(to_path);
  989. if (error)
  990. goto bail;
  991. } while (FindNextFile(find_handle, &ffd) != 0);
  992. dwError = GetLastError();
  993. if (dwError != ERROR_NO_MORE_FILES) {
  994. fprintf(stderr, "Error in FindFirstFile/FindNextFile.\n");
  995. goto bail;
  996. }
  997. FindClose(find_handle);
  998. return 0;
  999. bail:
  1000. FindClose(find_handle);
  1001. return -1;
  1002. }
  1003. #else
  1004. static int rename_file(const char *from_filename, const char *to_filename)
  1005. {
  1006. return rename(from_filename, to_filename);
  1007. }
  1008. static int makedir(const char *dirname)
  1009. {
  1010. return mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
  1011. }
  1012. static int makedirs(const char *dirname)
  1013. {
  1014. char *parent;
  1015. int rc;
  1016. rc = makedir(dirname);
  1017. if (rc == 0 || errno == EEXIST)
  1018. return 0;
  1019. if (errno != ENOENT)
  1020. return -1;
  1021. parent = parent_dir(dirname);
  1022. rc = makedirs(parent);
  1023. free(parent);
  1024. if (rc == -1)
  1025. return -1;
  1026. rc = makedir(dirname);
  1027. if (rc == -1)
  1028. return -1;
  1029. return rc;
  1030. }
  1031. static int copy_tree(const char *from_dirname, const char *to_dirname)
  1032. {
  1033. DIR *dir;
  1034. const struct dirent *ent;
  1035. int rc;
  1036. rc = makedirs(to_dirname);
  1037. if (rc == -1) {
  1038. fprintf(stderr, "Can't create the directory %s: %s.\n",
  1039. to_dirname, strerror(errno));
  1040. return -1;
  1041. }
  1042. dir = opendir(from_dirname);
  1043. if (dir == NULL) {
  1044. fprintf(stderr, "Can't open the directory %s: %s.\n",
  1045. from_dirname, strerror(errno));
  1046. return -1;
  1047. }
  1048. errno = 0;
  1049. while ((ent = readdir(dir)) != NULL) {
  1050. char *from_path, *to_path;
  1051. int error;
  1052. from_path = path_join(from_dirname, ent->d_name, NULL);
  1053. to_path = path_join(to_dirname, ent->d_name, NULL);
  1054. error = 0;
  1055. if (ent->d_type == DT_DIR) {
  1056. if (streq(ent->d_name, ".") || streq(ent->d_name, ".."))
  1057. continue;
  1058. if (streq(ent->d_name, ".svn"))
  1059. continue;
  1060. rc = makedirs(to_path);
  1061. if (rc == 0) {
  1062. rc = copy_tree(from_path, to_path);
  1063. if (rc == -1)
  1064. error = 1;
  1065. } else {
  1066. error = 1;
  1067. }
  1068. } else if (ent->d_type == DT_REG) {
  1069. rc = copy_file(from_path, to_path);
  1070. if (rc == -1)
  1071. error = 1;
  1072. } else {
  1073. fprintf(stderr, "Warning: unknown file type %u of %s.\n",
  1074. ent->d_type, ent->d_name);
  1075. }
  1076. free(from_path);
  1077. free(to_path);
  1078. if (error)
  1079. goto bail;
  1080. }
  1081. if (errno != 0) {
  1082. fprintf(stderr, "Error in readdir: %s.\n", strerror(errno));
  1083. goto bail;
  1084. }
  1085. rc = closedir(dir);
  1086. if (rc == -1) {
  1087. fprintf(stderr, "Can't close the directory %s: %s.\n",
  1088. from_dirname, strerror(errno));
  1089. return -1;
  1090. }
  1091. return 0;
  1092. bail:
  1093. closedir(dir);
  1094. return -1;
  1095. }
  1096. #endif