HardenedBSD src tree https://hardenedbsd.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1595 lines
36 KiB

  1. /* $NetBSD: util.c,v 1.21 2009/11/15 10:12:37 lukem Exp $ */
  2. /* from NetBSD: util.c,v 1.152 2009/07/13 19:05:41 roy Exp */
  3. /*-
  4. * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
  5. * All rights reserved.
  6. *
  7. * This code is derived from software contributed to The NetBSD Foundation
  8. * by Luke Mewburn.
  9. *
  10. * This code is derived from software contributed to The NetBSD Foundation
  11. * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
  12. * NASA Ames Research Center.
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions
  16. * are met:
  17. * 1. Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * 2. Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in the
  21. * documentation and/or other materials provided with the distribution.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  24. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  25. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  26. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  27. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. /*
  36. * Copyright (c) 1985, 1989, 1993, 1994
  37. * The Regents of the University of California. All rights reserved.
  38. *
  39. * Redistribution and use in source and binary forms, with or without
  40. * modification, are permitted provided that the following conditions
  41. * are met:
  42. * 1. Redistributions of source code must retain the above copyright
  43. * notice, this list of conditions and the following disclaimer.
  44. * 2. Redistributions in binary form must reproduce the above copyright
  45. * notice, this list of conditions and the following disclaimer in the
  46. * documentation and/or other materials provided with the distribution.
  47. * 3. Neither the name of the University nor the names of its contributors
  48. * may be used to endorse or promote products derived from this software
  49. * without specific prior written permission.
  50. *
  51. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  52. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  53. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  54. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  55. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  56. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  57. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  58. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  59. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  60. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  61. * SUCH DAMAGE.
  62. */
  63. #include "tnftp.h"
  64. #if 0 /* tnftp */
  65. #include <sys/cdefs.h>
  66. #ifndef lint
  67. __RCSID(" NetBSD: util.c,v 1.152 2009/07/13 19:05:41 roy Exp ");
  68. #endif /* not lint */
  69. /*
  70. * FTP User Program -- Misc support routines
  71. */
  72. #include <sys/param.h>
  73. #include <sys/socket.h>
  74. #include <sys/ioctl.h>
  75. #include <sys/time.h>
  76. #include <netinet/in.h>
  77. #include <arpa/ftp.h>
  78. #include <ctype.h>
  79. #include <err.h>
  80. #include <errno.h>
  81. #include <fcntl.h>
  82. #include <glob.h>
  83. #include <signal.h>
  84. #include <libgen.h>
  85. #include <limits.h>
  86. #include <netdb.h>
  87. #include <stdio.h>
  88. #include <stdlib.h>
  89. #include <string.h>
  90. #include <termios.h>
  91. #include <time.h>
  92. #include <tzfile.h>
  93. #include <unistd.h>
  94. #endif /* tnftp */
  95. #include "ftp_var.h"
  96. /*
  97. * Connect to peer server and auto-login, if possible.
  98. */
  99. void
  100. setpeer(int argc, char *argv[])
  101. {
  102. char *host;
  103. const char *port;
  104. if (argc == 0)
  105. goto usage;
  106. if (connected) {
  107. fprintf(ttyout, "Already connected to %s, use close first.\n",
  108. hostname);
  109. code = -1;
  110. return;
  111. }
  112. if (argc < 2)
  113. (void)another(&argc, &argv, "to");
  114. if (argc < 2 || argc > 3) {
  115. usage:
  116. UPRINTF("usage: %s host-name [port]\n", argv[0]);
  117. code = -1;
  118. return;
  119. }
  120. if (gatemode)
  121. port = gateport;
  122. else
  123. port = ftpport;
  124. if (argc > 2)
  125. port = argv[2];
  126. if (gatemode) {
  127. if (gateserver == NULL || *gateserver == '\0')
  128. errx(1, "main: gateserver not defined");
  129. host = hookup(gateserver, port);
  130. } else
  131. host = hookup(argv[1], port);
  132. if (host) {
  133. if (gatemode && verbose) {
  134. fprintf(ttyout,
  135. "Connecting via pass-through server %s\n",
  136. gateserver);
  137. }
  138. connected = 1;
  139. /*
  140. * Set up defaults for FTP.
  141. */
  142. (void)strlcpy(typename, "ascii", sizeof(typename));
  143. type = TYPE_A;
  144. curtype = TYPE_A;
  145. (void)strlcpy(formname, "non-print", sizeof(formname));
  146. form = FORM_N;
  147. (void)strlcpy(modename, "stream", sizeof(modename));
  148. mode = MODE_S;
  149. (void)strlcpy(structname, "file", sizeof(structname));
  150. stru = STRU_F;
  151. (void)strlcpy(bytename, "8", sizeof(bytename));
  152. bytesize = 8;
  153. if (autologin)
  154. (void)ftp_login(argv[1], NULL, NULL);
  155. }
  156. }
  157. static void
  158. parse_feat(const char *fline)
  159. {
  160. /*
  161. * work-around broken ProFTPd servers that can't
  162. * even obey RFC2389.
  163. */
  164. while (*fline && isspace((int)*fline))
  165. fline++;
  166. if (strcasecmp(fline, "MDTM") == 0)
  167. features[FEAT_MDTM] = 1;
  168. else if (strncasecmp(fline, "MLST", sizeof("MLST") - 1) == 0) {
  169. features[FEAT_MLST] = 1;
  170. } else if (strcasecmp(fline, "REST STREAM") == 0)
  171. features[FEAT_REST_STREAM] = 1;
  172. else if (strcasecmp(fline, "SIZE") == 0)
  173. features[FEAT_SIZE] = 1;
  174. else if (strcasecmp(fline, "TVFS") == 0)
  175. features[FEAT_TVFS] = 1;
  176. }
  177. /*
  178. * Determine the remote system type (SYST) and features (FEAT).
  179. * Call after a successful login (i.e, connected = -1)
  180. */
  181. void
  182. getremoteinfo(void)
  183. {
  184. int overbose, i;
  185. overbose = verbose;
  186. if (ftp_debug == 0)
  187. verbose = -1;
  188. /* determine remote system type */
  189. if (command("SYST") == COMPLETE) {
  190. if (overbose) {
  191. char *cp, c;
  192. c = 0;
  193. cp = strchr(reply_string + 4, ' ');
  194. if (cp == NULL)
  195. cp = strchr(reply_string + 4, '\r');
  196. if (cp) {
  197. if (cp[-1] == '.')
  198. cp--;
  199. c = *cp;
  200. *cp = '\0';
  201. }
  202. fprintf(ttyout, "Remote system type is %s.\n",
  203. reply_string + 4);
  204. if (cp)
  205. *cp = c;
  206. }
  207. if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
  208. if (proxy)
  209. unix_proxy = 1;
  210. else
  211. unix_server = 1;
  212. /*
  213. * Set type to 0 (not specified by user),
  214. * meaning binary by default, but don't bother
  215. * telling server. We can use binary
  216. * for text files unless changed by the user.
  217. */
  218. type = 0;
  219. (void)strlcpy(typename, "binary", sizeof(typename));
  220. if (overbose)
  221. fprintf(ttyout,
  222. "Using %s mode to transfer files.\n",
  223. typename);
  224. } else {
  225. if (proxy)
  226. unix_proxy = 0;
  227. else
  228. unix_server = 0;
  229. if (overbose &&
  230. !strncmp(reply_string, "215 TOPS20", 10))
  231. fputs(
  232. "Remember to set tenex mode when transferring binary files from this machine.\n",
  233. ttyout);
  234. }
  235. }
  236. /* determine features (if any) */
  237. for (i = 0; i < FEAT_max; i++)
  238. features[i] = -1;
  239. reply_callback = parse_feat;
  240. if (command("FEAT") == COMPLETE) {
  241. for (i = 0; i < FEAT_max; i++) {
  242. if (features[i] == -1)
  243. features[i] = 0;
  244. }
  245. features[FEAT_FEAT] = 1;
  246. } else
  247. features[FEAT_FEAT] = 0;
  248. #ifndef NO_DEBUG
  249. if (ftp_debug) {
  250. #define DEBUG_FEAT(x) fprintf(ttyout, "features[" #x "] = %d\n", features[(x)])
  251. DEBUG_FEAT(FEAT_FEAT);
  252. DEBUG_FEAT(FEAT_MDTM);
  253. DEBUG_FEAT(FEAT_MLST);
  254. DEBUG_FEAT(FEAT_REST_STREAM);
  255. DEBUG_FEAT(FEAT_SIZE);
  256. DEBUG_FEAT(FEAT_TVFS);
  257. #undef DEBUG_FEAT
  258. }
  259. #endif
  260. reply_callback = NULL;
  261. verbose = overbose;
  262. }
  263. /*
  264. * Reset the various variables that indicate connection state back to
  265. * disconnected settings.
  266. * The caller is responsible for issuing any commands to the remote server
  267. * to perform a clean shutdown before this is invoked.
  268. */
  269. void
  270. cleanuppeer(void)
  271. {
  272. if (cout)
  273. (void)fclose(cout);
  274. cout = NULL;
  275. connected = 0;
  276. unix_server = 0;
  277. unix_proxy = 0;
  278. /*
  279. * determine if anonftp was specifically set with -a
  280. * (1), or implicitly set by auto_fetch() (2). in the
  281. * latter case, disable after the current xfer
  282. */
  283. if (anonftp == 2)
  284. anonftp = 0;
  285. data = -1;
  286. epsv4bad = 0;
  287. epsv6bad = 0;
  288. if (username)
  289. free(username);
  290. username = NULL;
  291. if (!proxy)
  292. macnum = 0;
  293. }
  294. /*
  295. * Top-level signal handler for interrupted commands.
  296. */
  297. void
  298. intr(int signo)
  299. {
  300. sigint_raised = 1;
  301. alarmtimer(0);
  302. if (fromatty)
  303. write(fileno(ttyout), "\n", 1);
  304. siglongjmp(toplevel, 1);
  305. }
  306. /*
  307. * Signal handler for lost connections; cleanup various elements of
  308. * the connection state, and call cleanuppeer() to finish it off.
  309. */
  310. void
  311. lostpeer(int dummy)
  312. {
  313. int oerrno = errno;
  314. alarmtimer(0);
  315. if (connected) {
  316. if (cout != NULL) {
  317. (void)shutdown(fileno(cout), 1+1);
  318. (void)fclose(cout);
  319. cout = NULL;
  320. }
  321. if (data >= 0) {
  322. (void)shutdown(data, 1+1);
  323. (void)close(data);
  324. data = -1;
  325. }
  326. connected = 0;
  327. }
  328. pswitch(1);
  329. if (connected) {
  330. if (cout != NULL) {
  331. (void)shutdown(fileno(cout), 1+1);
  332. (void)fclose(cout);
  333. cout = NULL;
  334. }
  335. connected = 0;
  336. }
  337. proxflag = 0;
  338. pswitch(0);
  339. cleanuppeer();
  340. errno = oerrno;
  341. }
  342. /*
  343. * Login to remote host, using given username & password if supplied.
  344. * Return non-zero if successful.
  345. */
  346. int
  347. ftp_login(const char *host, const char *luser, const char *lpass)
  348. {
  349. char tmp[80];
  350. char *fuser, *pass, *facct, *p;
  351. char emptypass[] = "";
  352. const char *errormsg;
  353. int n, aflag, rval, nlen;
  354. aflag = rval = 0;
  355. fuser = pass = facct = NULL;
  356. if (luser)
  357. fuser = ftp_strdup(luser);
  358. if (lpass)
  359. pass = ftp_strdup(lpass);
  360. DPRINTF("ftp_login: user `%s' pass `%s' host `%s'\n",
  361. STRorNULL(fuser), STRorNULL(pass), STRorNULL(host));
  362. /*
  363. * Set up arguments for an anonymous FTP session, if necessary.
  364. */
  365. if (anonftp) {
  366. FREEPTR(fuser);
  367. fuser = ftp_strdup("anonymous"); /* as per RFC1635 */
  368. FREEPTR(pass);
  369. pass = ftp_strdup(getoptionvalue("anonpass"));
  370. }
  371. if (ruserpass(host, &fuser, &pass, &facct) < 0) {
  372. code = -1;
  373. goto cleanup_ftp_login;
  374. }
  375. while (fuser == NULL) {
  376. if (localname)
  377. fprintf(ttyout, "Name (%s:%s): ", host, localname);
  378. else
  379. fprintf(ttyout, "Name (%s): ", host);
  380. errormsg = NULL;
  381. nlen = get_line(stdin, tmp, sizeof(tmp), &errormsg);
  382. if (nlen < 0) {
  383. fprintf(ttyout, "%s; %s aborted.\n", errormsg, "login");
  384. code = -1;
  385. goto cleanup_ftp_login;
  386. } else if (nlen == 0) {
  387. fuser = ftp_strdup(localname);
  388. } else {
  389. fuser = ftp_strdup(tmp);
  390. }
  391. }
  392. if (gatemode) {
  393. char *nuser;
  394. size_t len;
  395. len = strlen(fuser) + 1 + strlen(host) + 1;
  396. nuser = ftp_malloc(len);
  397. (void)strlcpy(nuser, fuser, len);
  398. (void)strlcat(nuser, "@", len);
  399. (void)strlcat(nuser, host, len);
  400. FREEPTR(fuser);
  401. fuser = nuser;
  402. }
  403. n = command("USER %s", fuser);
  404. if (n == CONTINUE) {
  405. if (pass == NULL) {
  406. p = getpass("Password: ");
  407. if (p == NULL)
  408. p = emptypass;
  409. pass = ftp_strdup(p);
  410. memset(p, 0, strlen(p));
  411. }
  412. n = command("PASS %s", pass);
  413. memset(pass, 0, strlen(pass));
  414. }
  415. if (n == CONTINUE) {
  416. aflag++;
  417. if (facct == NULL) {
  418. p = getpass("Account: ");
  419. if (p == NULL)
  420. p = emptypass;
  421. facct = ftp_strdup(p);
  422. memset(p, 0, strlen(p));
  423. }
  424. if (facct[0] == '\0') {
  425. warnx("Login failed");
  426. goto cleanup_ftp_login;
  427. }
  428. n = command("ACCT %s", facct);
  429. memset(facct, 0, strlen(facct));
  430. }
  431. if ((n != COMPLETE) ||
  432. (!aflag && facct != NULL && command("ACCT %s", facct) != COMPLETE)) {
  433. warnx("Login failed");
  434. goto cleanup_ftp_login;
  435. }
  436. rval = 1;
  437. username = ftp_strdup(fuser);
  438. if (proxy)
  439. goto cleanup_ftp_login;
  440. connected = -1;
  441. getremoteinfo();
  442. for (n = 0; n < macnum; ++n) {
  443. if (!strcmp("init", macros[n].mac_name)) {
  444. (void)strlcpy(line, "$init", sizeof(line));
  445. makeargv();
  446. domacro(margc, margv);
  447. break;
  448. }
  449. }
  450. updatelocalcwd();
  451. updateremotecwd();
  452. cleanup_ftp_login:
  453. FREEPTR(fuser);
  454. if (pass != NULL)
  455. memset(pass, 0, strlen(pass));
  456. FREEPTR(pass);
  457. if (facct != NULL)
  458. memset(facct, 0, strlen(facct));
  459. FREEPTR(facct);
  460. return (rval);
  461. }
  462. /*
  463. * `another' gets another argument, and stores the new argc and argv.
  464. * It reverts to the top level (via intr()) on EOF/error.
  465. *
  466. * Returns false if no new arguments have been added.
  467. */
  468. int
  469. another(int *pargc, char ***pargv, const char *aprompt)
  470. {
  471. const char *errormsg;
  472. int ret, nlen;
  473. size_t len;
  474. len = strlen(line);
  475. if (len >= sizeof(line) - 3) {
  476. fputs("Sorry, arguments too long.\n", ttyout);
  477. intr(0);
  478. }
  479. fprintf(ttyout, "(%s) ", aprompt);
  480. line[len++] = ' ';
  481. errormsg = NULL;
  482. nlen = get_line(stdin, line + len, sizeof(line)-len, &errormsg);
  483. if (nlen < 0) {
  484. fprintf(ttyout, "%s; %s aborted.\n", errormsg, "operation");
  485. intr(0);
  486. }
  487. len += nlen;
  488. makeargv();
  489. ret = margc > *pargc;
  490. *pargc = margc;
  491. *pargv = margv;
  492. return (ret);
  493. }
  494. /*
  495. * glob files given in argv[] from the remote server.
  496. * if errbuf isn't NULL, store error messages there instead
  497. * of writing to the screen.
  498. */
  499. char *
  500. remglob(char *argv[], int doswitch, const char **errbuf)
  501. {
  502. static char buf[MAXPATHLEN];
  503. static FILE *ftemp = NULL;
  504. static char **args;
  505. char temp[MAXPATHLEN];
  506. int oldverbose, oldhash, oldprogress, fd;
  507. char *cp;
  508. const char *rmode;
  509. size_t len;
  510. if (!mflag || !connected) {
  511. if (!doglob)
  512. args = NULL;
  513. else {
  514. if (ftemp) {
  515. (void)fclose(ftemp);
  516. ftemp = NULL;
  517. }
  518. }
  519. return (NULL);
  520. }
  521. if (!doglob) {
  522. if (args == NULL)
  523. args = argv;
  524. if ((cp = *++args) == NULL)
  525. args = NULL;
  526. return (cp);
  527. }
  528. if (ftemp == NULL) {
  529. len = strlcpy(temp, tmpdir, sizeof(temp));
  530. if (temp[len - 1] != '/')
  531. (void)strlcat(temp, "/", sizeof(temp));
  532. (void)strlcat(temp, TMPFILE, sizeof(temp));
  533. if ((fd = mkstemp(temp)) < 0) {
  534. warn("Unable to create temporary file `%s'", temp);
  535. return (NULL);
  536. }
  537. close(fd);
  538. oldverbose = verbose;
  539. verbose = (errbuf != NULL) ? -1 : 0;
  540. oldhash = hash;
  541. oldprogress = progress;
  542. hash = 0;
  543. progress = 0;
  544. if (doswitch)
  545. pswitch(!proxy);
  546. for (rmode = "w"; *++argv != NULL; rmode = "a")
  547. recvrequest("NLST", temp, *argv, rmode, 0, 0);
  548. if ((code / 100) != COMPLETE) {
  549. if (errbuf != NULL)
  550. *errbuf = reply_string;
  551. }
  552. if (doswitch)
  553. pswitch(!proxy);
  554. verbose = oldverbose;
  555. hash = oldhash;
  556. progress = oldprogress;
  557. ftemp = fopen(temp, "r");
  558. (void)unlink(temp);
  559. if (ftemp == NULL) {
  560. if (errbuf == NULL)
  561. warnx("Can't find list of remote files");
  562. else
  563. *errbuf =
  564. "Can't find list of remote files";
  565. return (NULL);
  566. }
  567. }
  568. if (fgets(buf, sizeof(buf), ftemp) == NULL) {
  569. (void)fclose(ftemp);
  570. ftemp = NULL;
  571. return (NULL);
  572. }
  573. if ((cp = strchr(buf, '\n')) != NULL)
  574. *cp = '\0';
  575. return (buf);
  576. }
  577. /*
  578. * Glob a local file name specification with the expectation of a single
  579. * return value. Can't control multiple values being expanded from the
  580. * expression, we return only the first.
  581. * Returns NULL on error, or a pointer to a buffer containing the filename
  582. * that's the caller's responsiblity to free(3) when finished with.
  583. */
  584. char *
  585. globulize(const char *pattern)
  586. {
  587. glob_t gl;
  588. int flags;
  589. char *p;
  590. if (!doglob)
  591. return (ftp_strdup(pattern));
  592. flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
  593. memset(&gl, 0, sizeof(gl));
  594. if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) {
  595. warnx("Glob pattern `%s' not found", pattern);
  596. globfree(&gl);
  597. return (NULL);
  598. }
  599. p = ftp_strdup(gl.gl_pathv[0]);
  600. globfree(&gl);
  601. return (p);
  602. }
  603. /*
  604. * determine size of remote file
  605. */
  606. off_t
  607. remotesize(const char *file, int noisy)
  608. {
  609. int overbose, r;
  610. off_t size;
  611. overbose = verbose;
  612. size = -1;
  613. if (ftp_debug == 0)
  614. verbose = -1;
  615. if (! features[FEAT_SIZE]) {
  616. if (noisy)
  617. fprintf(ttyout,
  618. "SIZE is not supported by remote server.\n");
  619. goto cleanup_remotesize;
  620. }
  621. r = command("SIZE %s", file);
  622. if (r == COMPLETE) {
  623. char *cp, *ep;
  624. cp = strchr(reply_string, ' ');
  625. if (cp != NULL) {
  626. cp++;
  627. size = STRTOLL(cp, &ep, 10);
  628. if (*ep != '\0' && !isspace((unsigned char)*ep))
  629. size = -1;
  630. }
  631. } else {
  632. if (r == ERROR && code == 500 && features[FEAT_SIZE] == -1)
  633. features[FEAT_SIZE] = 0;
  634. if (noisy && ftp_debug == 0) {
  635. fputs(reply_string, ttyout);
  636. putc('\n', ttyout);
  637. }
  638. }
  639. cleanup_remotesize:
  640. verbose = overbose;
  641. return (size);
  642. }
  643. /*
  644. * determine last modification time (in GMT) of remote file
  645. */
  646. time_t
  647. remotemodtime(const char *file, int noisy)
  648. {
  649. int overbose, ocode, r;
  650. time_t rtime;
  651. overbose = verbose;
  652. ocode = code;
  653. rtime = -1;
  654. if (ftp_debug == 0)
  655. verbose = -1;
  656. if (! features[FEAT_MDTM]) {
  657. if (noisy)
  658. fprintf(ttyout,
  659. "MDTM is not supported by remote server.\n");
  660. goto cleanup_parse_time;
  661. }
  662. r = command("MDTM %s", file);
  663. if (r == COMPLETE) {
  664. struct tm timebuf;
  665. char *timestr, *frac;
  666. /*
  667. * time-val = 14DIGIT [ "." 1*DIGIT ]
  668. * YYYYMMDDHHMMSS[.sss]
  669. * mdtm-response = "213" SP time-val CRLF / error-response
  670. */
  671. timestr = reply_string + 4;
  672. /*
  673. * parse fraction.
  674. * XXX: ignored for now
  675. */
  676. frac = strchr(timestr, '\r');
  677. if (frac != NULL)
  678. *frac = '\0';
  679. frac = strchr(timestr, '.');
  680. if (frac != NULL)
  681. *frac++ = '\0';
  682. if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) {
  683. /*
  684. * XXX: Workaround for lame ftpd's that return
  685. * `19100' instead of `2000'
  686. */
  687. fprintf(ttyout,
  688. "Y2K warning! Incorrect time-val `%s' received from server.\n",
  689. timestr);
  690. timestr++;
  691. timestr[0] = '2';
  692. timestr[1] = '0';
  693. fprintf(ttyout, "Converted to `%s'\n", timestr);
  694. }
  695. memset(&timebuf, 0, sizeof(timebuf));
  696. if (strlen(timestr) != 14 ||
  697. (strptime(timestr, "%Y%m%d%H%M%S", &timebuf) == NULL)) {
  698. bad_parse_time:
  699. fprintf(ttyout, "Can't parse time `%s'.\n", timestr);
  700. goto cleanup_parse_time;
  701. }
  702. timebuf.tm_isdst = -1;
  703. rtime = timegm(&timebuf);
  704. if (rtime == -1) {
  705. if (noisy || ftp_debug != 0)
  706. goto bad_parse_time;
  707. else
  708. goto cleanup_parse_time;
  709. } else {
  710. DPRINTF("remotemodtime: parsed date `%s' as " LLF
  711. ", %s",
  712. timestr, (LLT)rtime,
  713. rfc2822time(localtime(&rtime)));
  714. }
  715. } else {
  716. if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1)
  717. features[FEAT_MDTM] = 0;
  718. if (noisy && ftp_debug == 0) {
  719. fputs(reply_string, ttyout);
  720. putc('\n', ttyout);
  721. }
  722. }
  723. cleanup_parse_time:
  724. verbose = overbose;
  725. if (rtime == -1)
  726. code = ocode;
  727. return (rtime);
  728. }
  729. /*
  730. * Format tm in an RFC2822 compatible manner, with a trailing \n.
  731. * Returns a pointer to a static string containing the result.
  732. */
  733. const char *
  734. rfc2822time(const struct tm *tm)
  735. {
  736. static char result[50];
  737. if (strftime(result, sizeof(result),
  738. "%a, %d %b %Y %H:%M:%S %z\n", tm) == 0)
  739. errx(1, "Can't convert RFC2822 time: buffer too small");
  740. return result;
  741. }
  742. /*
  743. * Update global `localcwd', which contains the state of the local cwd
  744. */
  745. void
  746. updatelocalcwd(void)
  747. {
  748. if (getcwd(localcwd, sizeof(localcwd)) == NULL)
  749. localcwd[0] = '\0';
  750. DPRINTF("updatelocalcwd: got `%s'\n", localcwd);
  751. }
  752. /*
  753. * Update global `remotecwd', which contains the state of the remote cwd
  754. */
  755. void
  756. updateremotecwd(void)
  757. {
  758. int overbose, ocode;
  759. size_t i;
  760. char *cp;
  761. overbose = verbose;
  762. ocode = code;
  763. if (ftp_debug == 0)
  764. verbose = -1;
  765. if (command("PWD") != COMPLETE)
  766. goto badremotecwd;
  767. cp = strchr(reply_string, ' ');
  768. if (cp == NULL || cp[0] == '\0' || cp[1] != '"')
  769. goto badremotecwd;
  770. cp += 2;
  771. for (i = 0; *cp && i < sizeof(remotecwd) - 1; i++, cp++) {
  772. if (cp[0] == '"') {
  773. if (cp[1] == '"')
  774. cp++;
  775. else
  776. break;
  777. }
  778. remotecwd[i] = *cp;
  779. }
  780. remotecwd[i] = '\0';
  781. DPRINTF("updateremotecwd: got `%s'\n", remotecwd);
  782. goto cleanupremotecwd;
  783. badremotecwd:
  784. remotecwd[0]='\0';
  785. cleanupremotecwd:
  786. verbose = overbose;
  787. code = ocode;
  788. }
  789. /*
  790. * Ensure file is in or under dir.
  791. * Returns 1 if so, 0 if not (or an error occurred).
  792. */
  793. int
  794. fileindir(const char *file, const char *dir)
  795. {
  796. char parentdirbuf[PATH_MAX+1], *parentdir;
  797. char realdir[PATH_MAX+1];
  798. size_t dirlen;
  799. /* determine parent directory of file */
  800. (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
  801. parentdir = dirname(parentdirbuf);
  802. if (strcmp(parentdir, ".") == 0)
  803. return 1; /* current directory is ok */
  804. /* find the directory */
  805. if (realpath(parentdir, realdir) == NULL) {
  806. warn("Unable to determine real path of `%s'", parentdir);
  807. return 0;
  808. }
  809. if (realdir[0] != '/') /* relative result is ok */
  810. return 1;
  811. dirlen = strlen(dir);
  812. if (strncmp(realdir, dir, dirlen) == 0 &&
  813. (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
  814. return 1;
  815. return 0;
  816. }
  817. /*
  818. * List words in stringlist, vertically arranged
  819. */
  820. void
  821. list_vertical(StringList *sl)
  822. {
  823. size_t i, j;
  824. size_t columns, lines;
  825. char *p;
  826. size_t w, width;
  827. width = 0;
  828. for (i = 0 ; i < sl->sl_cur ; i++) {
  829. w = strlen(sl->sl_str[i]);
  830. if (w > width)
  831. width = w;
  832. }
  833. width = (width + 8) &~ 7;
  834. columns = ttywidth / width;
  835. if (columns == 0)
  836. columns = 1;
  837. lines = (sl->sl_cur + columns - 1) / columns;
  838. for (i = 0; i < lines; i++) {
  839. for (j = 0; j < columns; j++) {
  840. p = sl->sl_str[j * lines + i];
  841. if (p)
  842. fputs(p, ttyout);
  843. if (j * lines + i + lines >= sl->sl_cur) {
  844. putc('\n', ttyout);
  845. break;
  846. }
  847. if (p) {
  848. w = strlen(p);
  849. while (w < width) {
  850. w = (w + 8) &~ 7;
  851. (void)putc('\t', ttyout);
  852. }
  853. }
  854. }
  855. }
  856. }
  857. /*
  858. * Update the global ttywidth value, using TIOCGWINSZ.
  859. */
  860. void
  861. setttywidth(int a)
  862. {
  863. struct winsize winsize;
  864. int oerrno = errno;
  865. if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 &&
  866. winsize.ws_col != 0)
  867. ttywidth = winsize.ws_col;
  868. else
  869. ttywidth = 80;
  870. errno = oerrno;
  871. }
  872. /*
  873. * Change the rate limit up (SIGUSR1) or down (SIGUSR2)
  874. */
  875. void
  876. crankrate(int sig)
  877. {
  878. switch (sig) {
  879. case SIGUSR1:
  880. if (rate_get)
  881. rate_get += rate_get_incr;
  882. if (rate_put)
  883. rate_put += rate_put_incr;
  884. break;
  885. case SIGUSR2:
  886. if (rate_get && rate_get > rate_get_incr)
  887. rate_get -= rate_get_incr;
  888. if (rate_put && rate_put > rate_put_incr)
  889. rate_put -= rate_put_incr;
  890. break;
  891. default:
  892. err(1, "crankrate invoked with unknown signal: %d", sig);
  893. }
  894. }
  895. /*
  896. * Setup or cleanup EditLine structures
  897. */
  898. #ifndef NO_EDITCOMPLETE
  899. void
  900. controlediting(void)
  901. {
  902. if (editing && el == NULL && hist == NULL) {
  903. HistEvent ev;
  904. int editmode;
  905. el = el_init(getprogname(), stdin, ttyout, stderr);
  906. /* init editline */
  907. hist = history_init(); /* init the builtin history */
  908. history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */
  909. el_set(el, EL_HIST, history, hist); /* use history */
  910. el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
  911. el_set(el, EL_PROMPT, prompt); /* set the prompt functions */
  912. el_set(el, EL_RPROMPT, rprompt);
  913. /* add local file completion, bind to TAB */
  914. el_set(el, EL_ADDFN, "ftp-complete",
  915. "Context sensitive argument completion",
  916. complete);
  917. el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
  918. el_source(el, NULL); /* read ~/.editrc */
  919. if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0)
  920. editing = 0; /* the user doesn't want editing,
  921. * so disable, and let statement
  922. * below cleanup */
  923. else
  924. el_set(el, EL_SIGNAL, 1);
  925. }
  926. if (!editing) {
  927. if (hist) {
  928. history_end(hist);
  929. hist = NULL;
  930. }
  931. if (el) {
  932. el_end(el);
  933. el = NULL;
  934. }
  935. }
  936. }
  937. #endif /* !NO_EDITCOMPLETE */
  938. /*
  939. * Convert the string `arg' to an int, which may have an optional SI suffix
  940. * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise.
  941. */
  942. int
  943. strsuftoi(const char *arg)
  944. {
  945. char *cp;
  946. long val;
  947. if (!isdigit((unsigned char)arg[0]))
  948. return (-1);
  949. val = strtol(arg, &cp, 10);
  950. if (cp != NULL) {
  951. if (cp[0] != '\0' && cp[1] != '\0')
  952. return (-1);
  953. switch (tolower((unsigned char)cp[0])) {
  954. case '\0':
  955. case 'b':
  956. break;
  957. case 'k':
  958. val <<= 10;
  959. break;
  960. case 'm':
  961. val <<= 20;
  962. break;
  963. case 'g':
  964. val <<= 30;
  965. break;
  966. default:
  967. return (-1);
  968. }
  969. }
  970. if (val < 0 || val > INT_MAX)
  971. return (-1);
  972. return (val);
  973. }
  974. /*
  975. * Set up socket buffer sizes before a connection is made.
  976. */
  977. void
  978. setupsockbufsize(int sock)
  979. {
  980. socklen_t slen;
  981. if (0 == rcvbuf_size) {
  982. slen = sizeof(rcvbuf_size);
  983. if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
  984. (void *)&rcvbuf_size, &slen) == -1)
  985. err(1, "Unable to determine rcvbuf size");
  986. if (rcvbuf_size <= 0)
  987. rcvbuf_size = 8 * 1024;
  988. if (rcvbuf_size > 8 * 1024 * 1024)
  989. rcvbuf_size = 8 * 1024 * 1024;
  990. DPRINTF("setupsockbufsize: rcvbuf_size determined as %d\n",
  991. rcvbuf_size);
  992. }
  993. if (0 == sndbuf_size) {
  994. slen = sizeof(sndbuf_size);
  995. if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF,
  996. (void *)&sndbuf_size, &slen) == -1)
  997. err(1, "Unable to determine sndbuf size");
  998. if (sndbuf_size <= 0)
  999. sndbuf_size = 8 * 1024;
  1000. if (sndbuf_size > 8 * 1024 * 1024)
  1001. sndbuf_size = 8 * 1024 * 1024;
  1002. DPRINTF("setupsockbufsize: sndbuf_size determined as %d\n",
  1003. sndbuf_size);
  1004. }
  1005. #ifdef __FreeBSD__
  1006. DPRINTF("auto_rcvbuf = %d\n", auto_rcvbuf);
  1007. if (auto_sndbuf == 0) {
  1008. #endif
  1009. if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
  1010. (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1)
  1011. warn("Unable to set sndbuf size %d", sndbuf_size);
  1012. #ifdef __FreeBSD__
  1013. }
  1014. #endif
  1015. #ifdef __FreeBSD__
  1016. DPRINTF("auto_sndbuf = %d\n", auto_sndbuf);
  1017. if (auto_rcvbuf == 0) {
  1018. #endif
  1019. if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
  1020. (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1)
  1021. warn("Unable to set rcvbuf size %d", rcvbuf_size);
  1022. #ifdef __FreeBSD__
  1023. }
  1024. #endif
  1025. }
  1026. /*
  1027. * Copy characters from src into dst, \ quoting characters that require it
  1028. */
  1029. void
  1030. ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
  1031. {
  1032. size_t di, si;
  1033. for (di = si = 0;
  1034. src[si] != '\0' && di < dstlen && si < srclen;
  1035. di++, si++) {
  1036. switch (src[si]) {
  1037. case '\\':
  1038. case ' ':
  1039. case '\t':
  1040. case '\r':
  1041. case '\n':
  1042. case '"':
  1043. dst[di++] = '\\';
  1044. if (di >= dstlen)
  1045. break;
  1046. /* FALLTHROUGH */
  1047. default:
  1048. dst[di] = src[si];
  1049. }
  1050. }
  1051. dst[di] = '\0';
  1052. }
  1053. /*
  1054. * Copy src into buf (which is len bytes long), expanding % sequences.
  1055. */
  1056. void
  1057. formatbuf(char *buf, size_t len, const char *src)
  1058. {
  1059. const char *p, *p2, *q;
  1060. size_t i;
  1061. int op, updirs, pdirs;
  1062. #define ADDBUF(x) do { \
  1063. if (i >= len - 1) \
  1064. goto endbuf; \
  1065. buf[i++] = (x); \
  1066. } while (0)
  1067. p = src;
  1068. for (i = 0; *p; p++) {
  1069. if (*p != '%') {
  1070. ADDBUF(*p);
  1071. continue;
  1072. }
  1073. p++;
  1074. switch (op = *p) {
  1075. case '/':
  1076. case '.':
  1077. case 'c':
  1078. p2 = connected ? remotecwd : "";
  1079. updirs = pdirs = 0;
  1080. /* option to determine fixed # of dirs from path */
  1081. if (op == '.' || op == 'c') {
  1082. int skip;
  1083. q = p2;
  1084. while (*p2) /* calc # of /'s */
  1085. if (*p2++ == '/')
  1086. updirs++;
  1087. if (p[1] == '0') { /* print <x> or ... */
  1088. pdirs = 1;
  1089. p++;
  1090. }
  1091. if (p[1] >= '1' && p[1] <= '9') {
  1092. /* calc # to skip */
  1093. skip = p[1] - '0';
  1094. p++;
  1095. } else
  1096. skip = 1;
  1097. updirs -= skip;
  1098. while (skip-- > 0) {
  1099. while ((p2 > q) && (*p2 != '/'))
  1100. p2--; /* back up */
  1101. if (skip && p2 > q)
  1102. p2--;
  1103. }
  1104. if (*p2 == '/' && p2 != q)
  1105. p2++;
  1106. }
  1107. if (updirs > 0 && pdirs) {
  1108. if (i >= len - 5)
  1109. break;
  1110. if (op == '.') {
  1111. ADDBUF('.');
  1112. ADDBUF('.');
  1113. ADDBUF('.');
  1114. } else {
  1115. ADDBUF('/');
  1116. ADDBUF('<');
  1117. if (updirs > 9) {
  1118. ADDBUF('9');
  1119. ADDBUF('+');
  1120. } else
  1121. ADDBUF('0' + updirs);
  1122. ADDBUF('>');
  1123. }
  1124. }
  1125. for (; *p2; p2++)
  1126. ADDBUF(*p2);
  1127. break;
  1128. case 'M':
  1129. case 'm':
  1130. for (p2 = connected && hostname ? hostname : "-";
  1131. *p2 ; p2++) {
  1132. if (op == 'm' && *p2 == '.')
  1133. break;
  1134. ADDBUF(*p2);
  1135. }
  1136. break;
  1137. case 'n':
  1138. for (p2 = connected ? username : "-"; *p2 ; p2++)
  1139. ADDBUF(*p2);
  1140. break;
  1141. case '%':
  1142. ADDBUF('%');
  1143. break;
  1144. default: /* display unknown codes literally */
  1145. ADDBUF('%');
  1146. ADDBUF(op);
  1147. break;
  1148. }
  1149. }
  1150. endbuf:
  1151. buf[i] = '\0';
  1152. }
  1153. /*
  1154. * Determine if given string is an IPv6 address or not.
  1155. * Return 1 for yes, 0 for no
  1156. */
  1157. int
  1158. isipv6addr(const char *addr)
  1159. {
  1160. int rv = 0;
  1161. #ifdef INET6
  1162. struct addrinfo hints, *res;
  1163. memset(&hints, 0, sizeof(hints));
  1164. hints.ai_family = AF_INET6;
  1165. hints.ai_socktype = SOCK_DGRAM; /*dummy*/
  1166. hints.ai_flags = AI_NUMERICHOST;
  1167. if (getaddrinfo(addr, "0", &hints, &res) != 0)
  1168. rv = 0;
  1169. else {
  1170. rv = 1;
  1171. freeaddrinfo(res);
  1172. }
  1173. DPRINTF("isipv6addr: got %d for %s\n", rv, addr);
  1174. #endif
  1175. return (rv == 1) ? 1 : 0;
  1176. }
  1177. /*
  1178. * Read a line from the FILE stream into buf/buflen using fgets(), so up
  1179. * to buflen-1 chars will be read and the result will be NUL terminated.
  1180. * If the line has a trailing newline it will be removed.
  1181. * If the line is too long, excess characters will be read until
  1182. * newline/EOF/error.
  1183. * If EOF/error occurs or a too-long line is encountered and errormsg
  1184. * isn't NULL, it will be changed to a description of the problem.
  1185. * (The EOF message has a leading \n for cosmetic purposes).
  1186. * Returns:
  1187. * >=0 length of line (excluding trailing newline) if all ok
  1188. * -1 error occurred
  1189. * -2 EOF encountered
  1190. * -3 line was too long
  1191. */
  1192. int
  1193. get_line(FILE *stream, char *buf, size_t buflen, const char **errormsg)
  1194. {
  1195. int rv, ch;
  1196. size_t len;
  1197. if (fgets(buf, buflen, stream) == NULL) {
  1198. if (feof(stream)) { /* EOF */
  1199. rv = -2;
  1200. if (errormsg)
  1201. *errormsg = "\nEOF received";
  1202. } else { /* error */
  1203. rv = -1;
  1204. if (errormsg)
  1205. *errormsg = "Error encountered";
  1206. }
  1207. clearerr(stream);
  1208. return rv;
  1209. }
  1210. len = strlen(buf);
  1211. if (buf[len-1] == '\n') { /* clear any trailing newline */
  1212. buf[--len] = '\0';
  1213. } else if (len == buflen-1) { /* line too long */
  1214. while ((ch = getchar()) != '\n' && ch != EOF)
  1215. continue;
  1216. if (errormsg)
  1217. *errormsg = "Input line is too long";
  1218. clearerr(stream);
  1219. return -3;
  1220. }
  1221. if (errormsg)
  1222. *errormsg = NULL;
  1223. return len;
  1224. }
  1225. /*
  1226. * Internal version of connect(2); sets socket buffer sizes,
  1227. * binds to a specific local address (if set), and
  1228. * supports a connection timeout using a non-blocking connect(2) with
  1229. * a poll(2).
  1230. * Socket fcntl flags are temporarily updated to include O_NONBLOCK;
  1231. * these will not be reverted on connection failure.
  1232. * Returns 0 on success, or -1 upon failure (with an appropriate
  1233. * error message displayed.)
  1234. */
  1235. int
  1236. ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen)
  1237. {
  1238. int flags, rv, timeout, error;
  1239. socklen_t slen;
  1240. struct timeval endtime, now, td;
  1241. struct pollfd pfd[1];
  1242. char hname[NI_MAXHOST];
  1243. char sname[NI_MAXSERV];
  1244. setupsockbufsize(sock);
  1245. if (getnameinfo(name, namelen,
  1246. hname, sizeof(hname), sname, sizeof(sname),
  1247. NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
  1248. strlcpy(hname, "?", sizeof(hname));
  1249. strlcpy(sname, "?", sizeof(sname));
  1250. }
  1251. if (bindai != NULL) { /* bind to specific addr */
  1252. struct addrinfo *ai;
  1253. for (ai = bindai; ai != NULL; ai = ai->ai_next) {
  1254. if (ai->ai_family == name->sa_family)
  1255. break;
  1256. }
  1257. if (ai == NULL)
  1258. ai = bindai;
  1259. if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
  1260. char bname[NI_MAXHOST];
  1261. int saveerr;
  1262. saveerr = errno;
  1263. if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
  1264. bname, sizeof(bname), NULL, 0, NI_NUMERICHOST) != 0)
  1265. strlcpy(bname, "?", sizeof(bname));
  1266. errno = saveerr;
  1267. warn("Can't bind to `%s'", bname);
  1268. return -1;
  1269. }
  1270. }
  1271. /* save current socket flags */
  1272. if ((flags = fcntl(sock, F_GETFL, 0)) == -1) {
  1273. warn("Can't %s socket flags for connect to `%s:%s'",
  1274. "save", hname, sname);
  1275. return -1;
  1276. }
  1277. /* set non-blocking connect */
  1278. if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
  1279. warn("Can't set socket non-blocking for connect to `%s:%s'",
  1280. hname, sname);
  1281. return -1;
  1282. }
  1283. /* NOTE: we now must restore socket flags on successful exit */
  1284. pfd[0].fd = sock;
  1285. pfd[0].events = POLLIN|POLLOUT;
  1286. if (quit_time > 0) { /* want a non default timeout */
  1287. (void)gettimeofday(&endtime, NULL);
  1288. endtime.tv_sec += quit_time; /* determine end time */
  1289. }
  1290. rv = connect(sock, name, namelen); /* inititate the connection */
  1291. if (rv == -1) { /* connection error */
  1292. if (errno != EINPROGRESS) { /* error isn't "please wait" */
  1293. connecterror:
  1294. warn("Can't connect to `%s:%s'", hname, sname);
  1295. return -1;
  1296. }
  1297. /* connect EINPROGRESS; wait */
  1298. do {
  1299. if (quit_time > 0) { /* determine timeout */
  1300. (void)gettimeofday(&now, NULL);
  1301. timersub(&endtime, &now, &td);
  1302. timeout = td.tv_sec * 1000 + td.tv_usec/1000;
  1303. if (timeout < 0)
  1304. timeout = 0;
  1305. } else {
  1306. timeout = INFTIM;
  1307. }
  1308. pfd[0].revents = 0;
  1309. rv = ftp_poll(pfd, 1, timeout);
  1310. /* loop until poll ! EINTR */
  1311. } while (rv == -1 && errno == EINTR);
  1312. if (rv == 0) { /* poll (connect) timed out */
  1313. errno = ETIMEDOUT;
  1314. goto connecterror;
  1315. }
  1316. if (rv == -1) { /* poll error */
  1317. goto connecterror;
  1318. } else if (pfd[0].revents & (POLLIN|POLLOUT)) {
  1319. slen = sizeof(error); /* OK, or pending error */
  1320. if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
  1321. &error, &slen) == -1) {
  1322. /* Solaris pending error */
  1323. goto connecterror;
  1324. } else if (error != 0) {
  1325. errno = error; /* BSD pending error */
  1326. goto connecterror;
  1327. }
  1328. } else {
  1329. errno = EBADF; /* this shouldn't happen ... */
  1330. goto connecterror;
  1331. }
  1332. }
  1333. if (fcntl(sock, F_SETFL, flags) == -1) {
  1334. /* restore socket flags */
  1335. warn("Can't %s socket flags for connect to `%s:%s'",
  1336. "restore", hname, sname);
  1337. return -1;
  1338. }
  1339. return 0;
  1340. }
  1341. /*
  1342. * Internal version of listen(2); sets socket buffer sizes first.
  1343. */
  1344. int
  1345. ftp_listen(int sock, int backlog)
  1346. {
  1347. setupsockbufsize(sock);
  1348. return (listen(sock, backlog));
  1349. }
  1350. /*
  1351. * Internal version of poll(2), to allow reimplementation by select(2)
  1352. * on platforms without the former.
  1353. */
  1354. int
  1355. ftp_poll(struct pollfd *fds, int nfds, int timeout)
  1356. {
  1357. #if defined(HAVE_POLL)
  1358. return poll(fds, nfds, timeout);
  1359. #elif defined(HAVE_SELECT)
  1360. /* implement poll(2) using select(2) */
  1361. fd_set rset, wset, xset;
  1362. const int rsetflags = POLLIN | POLLRDNORM;
  1363. const int wsetflags = POLLOUT | POLLWRNORM;
  1364. const int xsetflags = POLLRDBAND;
  1365. struct timeval tv, *ptv;
  1366. int i, max, rv;
  1367. FD_ZERO(&rset); /* build list of read & write events */
  1368. FD_ZERO(&wset);
  1369. FD_ZERO(&xset);
  1370. max = 0;
  1371. for (i = 0; i < nfds; i++) {
  1372. if (fds[i].fd > FD_SETSIZE) {
  1373. warnx("can't select fd %d", fds[i].fd);
  1374. errno = EINVAL;
  1375. return -1;
  1376. } else if (fds[i].fd > max)
  1377. max = fds[i].fd;
  1378. if (fds[i].events & rsetflags)
  1379. FD_SET(fds[i].fd, &rset);
  1380. if (fds[i].events & wsetflags)
  1381. FD_SET(fds[i].fd, &wset);
  1382. if (fds[i].events & xsetflags)
  1383. FD_SET(fds[i].fd, &xset);
  1384. }
  1385. ptv = &tv; /* determine timeout */
  1386. if (timeout == -1) { /* wait forever */
  1387. ptv = NULL;
  1388. } else if (timeout == 0) { /* poll once */
  1389. ptv->tv_sec = 0;
  1390. ptv->tv_usec = 0;
  1391. }
  1392. else if (timeout != 0) { /* wait timeout milliseconds */
  1393. ptv->tv_sec = timeout / 1000;
  1394. ptv->tv_usec = (timeout % 1000) * 1000;
  1395. }
  1396. rv = select(max + 1, &rset, &wset, &xset, ptv);
  1397. if (rv <= 0) /* -1 == error, 0 == timeout */
  1398. return rv;
  1399. for (i = 0; i < nfds; i++) { /* determine results */
  1400. if (FD_ISSET(fds[i].fd, &rset))
  1401. fds[i].revents |= (fds[i].events & rsetflags);
  1402. if (FD_ISSET(fds[i].fd, &wset))
  1403. fds[i].revents |= (fds[i].events & wsetflags);
  1404. if (FD_ISSET(fds[i].fd, &xset))
  1405. fds[i].revents |= (fds[i].events & xsetflags);
  1406. }
  1407. return rv;
  1408. #else
  1409. # error no way to implement xpoll
  1410. #endif
  1411. }
  1412. /*
  1413. * malloc() with inbuilt error checking
  1414. */
  1415. void *
  1416. ftp_malloc(size_t size)
  1417. {
  1418. void *p;
  1419. p = malloc(size);
  1420. if (p == NULL)
  1421. err(1, "Unable to allocate %ld bytes of memory", (long)size);
  1422. return (p);
  1423. }
  1424. /*
  1425. * sl_init() with inbuilt error checking
  1426. */
  1427. StringList *
  1428. ftp_sl_init(void)
  1429. {
  1430. StringList *p;
  1431. p = sl_init();
  1432. if (p == NULL)
  1433. err(1, "Unable to allocate memory for stringlist");
  1434. return (p);
  1435. }
  1436. /*
  1437. * sl_add() with inbuilt error checking
  1438. */
  1439. void
  1440. ftp_sl_add(StringList *sl, char *i)
  1441. {
  1442. if (sl_add(sl, i) == -1)
  1443. err(1, "Unable to add `%s' to stringlist", i);
  1444. }
  1445. /*
  1446. * strdup() with inbuilt error checking
  1447. */
  1448. char *
  1449. ftp_strdup(const char *str)
  1450. {
  1451. char *s;
  1452. if (str == NULL)
  1453. errx(1, "ftp_strdup: called with NULL argument");
  1454. s = strdup(str);
  1455. if (s == NULL)
  1456. err(1, "Unable to allocate memory for string copy");
  1457. return (s);
  1458. }