Exemplo n.º 1
0
int ssl_client_main(int argc UNUSED_PARAM, char **argv)
{
	tls_state_t *tls;
	const char *sni = NULL;
	int opt;

	// INIT_G();

	tls = new_tls_state();
	opt = getopt32(argv, "s:#r:#n:", &tls->ofd, &tls->ifd, &sni);
	if (!(opt & 2)) {
		/* -r N defaults to -s N */
		tls->ifd = tls->ofd;
	}

	if (!(opt & 3)) {
		if (!argv[1])
			bb_show_usage();
		/* Undocumented debug feature: without -s and -r, takes HOST arg and connects to it */
		//
		// Talk to kernel.org:
		// printf "GET / HTTP/1.1\r\nHost: kernel.org\r\n\r\n" | ./busybox ssl_client kernel.org
		if (!sni)
			sni = argv[1];
		tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[1], 443);
	}

	tls_handshake(tls, sni);
	tls_run_copy_loop(tls);

	return EXIT_SUCCESS;
}
Exemplo n.º 2
0
static time_t askremotedate(const char *host)
{
	uint32_t nett;
	int fd;

	/* Add a timeout for dead or inaccessible servers */
	alarm(10);
	signal(SIGALRM, socket_timeout);

	fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37));

	if (safe_read(fd, (void *)&nett, 4) != 4)    /* read time from server */
		bb_error_msg_and_die("%s did not send the complete time", host);
	close(fd);

	/* convert from network byte order to local byte order.
	 * RFC 868 time is the number of seconds
	 * since 00:00 (midnight) 1 January 1900 GMT
	 * the RFC 868 time 2,208,988,800 corresponds to 00:00  1 Jan 1970 GMT
	 * Subtract the RFC 868 time to get Linux epoch
	 */

	return ntohl(nett) - RFC_868_BIAS;
}
Exemplo n.º 3
0
int sendmail_main(int argc UNUSED_PARAM, char **argv)
{
	char *opt_connect = opt_connect;
	char *opt_from = NULL;
	char *s;
	llist_t *list = NULL;
	char *host = sane_address(safe_gethostname());
	unsigned nheaders = 0;
	int code;
	enum {
		HDR_OTHER = 0,
		HDR_TOCC,
		HDR_BCC,
	} last_hdr = 0;
	int check_hdr;
	int has_to = 0;

	enum {
	//--- standard options
		OPT_t = 1 << 0,         // read message for recipients, append them to those on cmdline
		OPT_f = 1 << 1,         // sender address
		OPT_o = 1 << 2,         // various options. -oi IMPLIED! others are IGNORED!
		OPT_i = 1 << 3,         // IMPLIED!
	//--- BB specific options
		OPT_w = 1 << 4,         // network timeout
		OPT_H = 1 << 5,         // use external connection helper
		OPT_S = 1 << 6,         // specify connection string
		OPT_a = 1 << 7,         // authentication tokens
		OPT_v = 1 << 8,         // verbosity
	};

	// init global variables
	INIT_G();

	// save initial stdin since body is piped!
	xdup2(STDIN_FILENO, 3);
	G.fp0 = xfdopen_for_read(3);

	// parse options
	// -v is a counter, -H and -S are mutually exclusive, -a is a list
	opt_complementary = "vv:w+:H--S:S--H:a::";
	// N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect
	// -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility,
	// it is still under development.
	opts = getopt32(argv, "tf:o:iw:H:S:a::v", &opt_from, NULL,
			&timeout, &opt_connect, &opt_connect, &list, &verbose);
	//argc -= optind;
	argv += optind;

	// process -a[upm]<token> options
	if ((opts & OPT_a) && !list)
		bb_show_usage();
	while (list) {
		char *a = (char *) llist_pop(&list);
		if ('u' == a[0])
			G.user = xstrdup(a+1);
		if ('p' == a[0])
			G.pass = xstrdup(a+1);
		// N.B. we support only AUTH LOGIN so far
		//if ('m' == a[0])
		//	G.method = xstrdup(a+1);
	}
	// N.B. list == NULL here
	//bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv);

	// connect to server

	// connection helper ordered? ->
	if (opts & OPT_H) {
		const char *args[] = { "sh", "-c", opt_connect, NULL };
		// plug it in
		launch_helper(args);
		// Now:
		// our stdout will go to helper's stdin,
		// helper's stdout will be available on our stdin.

		// Wait for initial server message.
		// If helper (such as openssl) invokes STARTTLS, the initial 220
		// is swallowed by helper (and not repeated after TLS is initiated).
		// We will send NOOP cmd to server and check the response.
		// We should get 220+250 on plain connection, 250 on STARTTLSed session.
		//
		// The problem here is some servers delay initial 220 message,
		// and consider client to be a spammer if it starts sending cmds
		// before 220 reached it. The code below is unsafe in this regard:
		// in non-STARTTLSed case, we potentially send NOOP before 220
		// is sent by server.
		// Ideas? (--delay SECS opt? --assume-starttls-helper opt?)
		code = smtp_check("NOOP", -1);
		if (code == 220)
			// we got 220 - this is not STARTTLSed connection,
			// eat 250 response to our NOOP
			smtp_check(NULL, 250);
		else
		if (code != 250)
			bb_error_msg_and_die("SMTP init failed");
	} else {
		// vanilla connection
		int fd;
		// host[:port] not explicitly specified? -> use $SMTPHOST
		// no $SMTPHOST? -> use localhost
		if (!(opts & OPT_S)) {
			opt_connect = getenv("SMTPHOST");
			if (!opt_connect)
				opt_connect = (char *)"127.0.0.1";
		}
		// do connect
		fd = create_and_connect_stream_or_die(opt_connect, 25);
		// and make ourselves a simple IO filter
		xmove_fd(fd, STDIN_FILENO);
		xdup2(STDIN_FILENO, STDOUT_FILENO);

		// Wait for initial server 220 message
		smtp_check(NULL, 220);
	}

	// we should start with modern EHLO
	if (250 != smtp_checkp("EHLO %s", host, -1))
		smtp_checkp("HELO %s", host, 250);

	// perform authentication
	if (opts & OPT_a) {
		smtp_check("AUTH LOGIN", 334);
		// we must read credentials unless they are given via -a[up] options
		if (!G.user || !G.pass)
			get_cred_or_die(4);
		encode_base64(NULL, G.user, NULL);
		smtp_check("", 334);
		encode_base64(NULL, G.pass, NULL);
		smtp_check("", 235);
	}

	// set sender
	// N.B. we have here a very loosely defined algorythm
	// since sendmail historically offers no means to specify secrets on cmdline.
	// 1) server can require no authentication ->
	//	we must just provide a (possibly fake) reply address.
	// 2) server can require AUTH ->
	//	we must provide valid username and password along with a (possibly fake) reply address.
	//	For the sake of security username and password are to be read either from console or from a secured file.
	//	Since reading from console may defeat usability, the solution is either to read from a predefined
	//	file descriptor (e.g. 4), or again from a secured file.

	// got no sender address? use auth name, then UID username as a last resort
	if (!opt_from) {
		opt_from = xasprintf("%s@%s",
		                     G.user ? G.user : xuid2uname(getuid()),
		                     xgethostbyname(host)->h_name);
	}
	free(host);

	smtp_checkp("MAIL FROM:<%s>", opt_from, 250);

	// process message

	// read recipients from message and add them to those given on cmdline.
	// this means we scan stdin for To:, Cc:, Bcc: lines until an empty line
	// and then use the rest of stdin as message body
	code = 0; // set "analyze headers" mode
	while ((s = xmalloc_fgetline(G.fp0)) != NULL) {
 dump:
		// put message lines doubling leading dots
		if (code) {
			// escape leading dots
			// N.B. this feature is implied even if no -i (-oi) switch given
			// N.B. we need to escape the leading dot regardless of
			// whether it is single or not character on the line
			if ('.' == s[0] /*&& '\0' == s[1] */)
				bb_putchar('.');
			// dump read line
			send_r_n(s);
			free(s);
			continue;
		}

		// analyze headers
		// To: or Cc: headers add recipients
		check_hdr = (0 == strncasecmp("To:", s, 3));
		has_to |= check_hdr;
		if (opts & OPT_t) {
			if (check_hdr || 0 == strncasecmp("Bcc:" + 1, s, 3)) {
				rcptto_list(s+3);
				last_hdr = HDR_TOCC;
				goto addheader;
			}
			// Bcc: header adds blind copy (hidden) recipient
			if (0 == strncasecmp("Bcc:", s, 4)) {
				rcptto_list(s+4);
				free(s);
				last_hdr = HDR_BCC;
				continue; // N.B. Bcc: vanishes from headers!
			}
		}
		check_hdr = (list && isspace(s[0]));
		if (strchr(s, ':') || check_hdr) {
			// other headers go verbatim
			// N.B. RFC2822 2.2.3 "Long Header Fields" allows for headers to occupy several lines.
			// Continuation is denoted by prefixing additional lines with whitespace(s).
			// Thanks (stefan.seyfried at googlemail.com) for pointing this out.
			if (check_hdr && last_hdr != HDR_OTHER) {
				rcptto_list(s+1);
				if (last_hdr == HDR_BCC)
					continue;
					// N.B. Bcc: vanishes from headers!
			} else {
				last_hdr = HDR_OTHER;
			}
 addheader:
			// N.B. we allow MAX_HEADERS generic headers at most to prevent attacks
			if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
				goto bail;
			llist_add_to_end(&list, s);
		} else {
			// a line without ":" (an empty line too, by definition) doesn't look like a valid header
			// so stop "analyze headers" mode
 reenter:
			// put recipients specified on cmdline
			check_hdr = 1;
			while (*argv) {
				char *t = sane_address(*argv);
				rcptto(t);
				//if (MAX_HEADERS && ++nheaders >= MAX_HEADERS)
				//	goto bail;
				if (!has_to) {
					const char *hdr;

					if (check_hdr && argv[1])
						hdr = "To: %s,";
					else if (check_hdr)
						hdr = "To: %s";
					else if (argv[1])
						hdr = "To: %s," + 3;
					else
						hdr = "To: %s" + 3;
					llist_add_to_end(&list,
							xasprintf(hdr, t));
					check_hdr = 0;
				}
				argv++;
			}
			// enter "put message" mode
			// N.B. DATA fails iff no recipients were accepted (or even provided)
			// in this case just bail out gracefully
			if (354 != smtp_check("DATA", -1))
				goto bail;
			// dump the headers
			while (list) {
				send_r_n((char *) llist_pop(&list));
			}
			// stop analyzing headers
			code++;
			// N.B. !s means: we read nothing, and nothing to be read in the future.
			// just dump empty line and break the loop
			if (!s) {
				send_r_n("");
				break;
			}
			// go dump message body
			// N.B. "s" already contains the first non-header line, so pretend we read it from input
			goto dump;
		}
	}
	// odd case: we didn't stop "analyze headers" mode -> message body is empty. Reenter the loop
	// N.B. after reenter code will be > 0
	if (!code)
		goto reenter;

	// finalize the message
	smtp_check(".", 250);
 bail:
	// ... and say goodbye
	smtp_check("QUIT", 221);
	// cleanup
	if (ENABLE_FEATURE_CLEAN_UP)
		fclose(G.fp0);

	return EXIT_SUCCESS;
}
Exemplo n.º 4
0
int telnet_main(int argc, char **argv)
{
	char *host;
	int port;
	int len;
#ifdef USE_POLL
	struct pollfd ufds[2];
#else
	fd_set readfds;
	int maxfd;
#endif

	INIT_G();

#if ENABLE_FEATURE_AUTOWIDTH
	get_terminal_width_height(0, &G.win_width, &G.win_height);
#endif

#if ENABLE_FEATURE_TELNET_TTYPE
	G.ttype = getenv("TERM");
#endif

	if (tcgetattr(0, &G.termios_def) >= 0) {
		G.do_termios = 1;
		G.termios_raw = G.termios_def;
		cfmakeraw(&G.termios_raw);
	}

	if (argc < 2)
		bb_show_usage();

#if ENABLE_FEATURE_TELNET_AUTOLOGIN
	if (1 & getopt32(argv, "al:", &G.autologin))
		G.autologin = getenv("USER");
	argv += optind;
#else
	argv++;
#endif
	if (!*argv)
		bb_show_usage();
	host = *argv++;
	port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
	if (*argv) /* extra params?? */
		bb_show_usage();

	G.netfd = create_and_connect_stream_or_die(host, port);

	setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));

	signal(SIGINT, fgotsig);

#ifdef USE_POLL
	ufds[0].fd = 0; ufds[1].fd = G.netfd;
	ufds[0].events = ufds[1].events = POLLIN;
#else
	FD_ZERO(&readfds);
	FD_SET(STDIN_FILENO, &readfds);
	FD_SET(G.netfd, &readfds);
	maxfd = G.netfd + 1;
#endif

	while (1) {
#ifndef USE_POLL
		fd_set rfds = readfds;

		switch (select(maxfd, &rfds, NULL, NULL, NULL))
#else
		switch (poll(ufds, 2, -1))
#endif
		{
		case 0:
			/* timeout */
		case -1:
			/* error, ignore and/or log something, bay go to loop */
			if (G.gotsig)
				conescape();
			else
				sleep(1);
			break;
		default:

#ifdef USE_POLL
			if (ufds[0].revents) /* well, should check POLLIN, but ... */
#else
			if (FD_ISSET(STDIN_FILENO, &rfds))
#endif
			{
				len = read(STDIN_FILENO, G.buf, DATABUFSIZE);
				if (len <= 0)
					doexit(EXIT_SUCCESS);
				TRACE(0, ("Read con: %d\n", len));
				handlenetoutput(len);
			}

#ifdef USE_POLL
			if (ufds[1].revents) /* well, should check POLLIN, but ... */
#else
			if (FD_ISSET(G.netfd, &rfds))
#endif
			{
				len = read(G.netfd, G.buf, DATABUFSIZE);
				if (len <= 0) {
					write_str(1, "Connection closed by foreign host\r\n");
					doexit(EXIT_FAILURE);
				}
				TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
				handlenetinput(len);
			}
		}
	}
}
Exemplo n.º 5
0
int lpqr_main(int argc UNUSED_PARAM, char *argv[])
{
	enum {
		OPT_P           = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515"
		OPT_U           = 1 << 1, // -U username

		LPR_V           = 1 << 2, // -V: be verbose
		LPR_h           = 1 << 3, // -h: want banner printed
		LPR_C           = 1 << 4, // -C class: job "class" (? supposedly printed on banner)
		LPR_J           = 1 << 5, // -J title: the job title for the banner page
		LPR_m           = 1 << 6, // -m: send mail back to user

		LPQ_SHORT_FMT   = 1 << 2, // -s: short listing format
		LPQ_DELETE      = 1 << 3, // -d: delete job(s)
		LPQ_FORCE       = 1 << 4, // -f: force waiting job(s) to be printed
	};
	char tempfile[sizeof("/tmp/lprXXXXXX")];
	const char *job_title;
	const char *printer_class = "";   // printer class, max 32 char
	const char *queue;                // name of printer queue
	const char *server = "localhost"; // server[:port] of printer queue
	char *hostname;
	// N.B. IMHO getenv("USER") can be way easily spoofed!
	const char *user = xuid2uname(getuid());
	unsigned job;
	unsigned opts;
	int fd;

	queue = getenv("PRINTER");
	if (!queue)
		queue = "lp";

	// parse options
	// TODO: set opt_complementary: s,d,f are mutually exclusive
	opts = getopt32(argv,
		(/*lp*/'r' == applet_name[2]) ? "P:U:VhC:J:m" : "P:U:sdf"
		, &queue, &user
		, &printer_class, &job_title
	);
	argv += optind;

	{
		// queue name is to the left of '@'
		char *s = strchr(queue, '@');
		if (s) {
			// server name is to the right of '@'
			*s = '\0';
			server = s + 1;
		}
	}

	// do connect
	fd = create_and_connect_stream_or_die(server, 515);

	//
	// LPQ ------------------------
	//
	if (/*lp*/'q' == applet_name[2]) {
		char cmd;
		// force printing of every job still in queue
		if (opts & LPQ_FORCE) {
			cmd = 1;
			goto command;
		// delete job(s)
		} else if (opts & LPQ_DELETE) {
			fdprintf(fd, "\x5" "%s %s", queue, user);
			while (*argv) {
				fdprintf(fd, " %s", *argv++);
			}
			bb_putchar('\n');
		// dump current jobs status
		// N.B. periodical polling should be achieved
		// via "watch -n delay lpq"
		// They say it's the UNIX-way :)
		} else {
			cmd = (opts & LPQ_SHORT_FMT) ? 3 : 4;
 command:
			fdprintf(fd, "%c" "%s\n", cmd, queue);
			bb_copyfd_eof(fd, STDOUT_FILENO);
		}

		return EXIT_SUCCESS;
	}

	//
	// LPR ------------------------
	//
	if (opts & LPR_V)
		bb_error_msg("connected to server");

	job = getpid() % 1000;
	hostname = safe_gethostname();

	// no files given on command line? -> use stdin
	if (!*argv)
		*--argv = (char *)"-";

	fdprintf(fd, "\x2" "%s\n", queue);
	get_response_or_say_and_die(fd, "setting queue");

	// process files
	do {
		unsigned cflen;
		int dfd;
		struct stat st;
		char *c;
		char *remote_filename;
		char *controlfile;

		// if data file is stdin, we need to dump it first
		if (LONE_DASH(*argv)) {
			strcpy(tempfile, "/tmp/lprXXXXXX");
			dfd = xmkstemp(tempfile);
			bb_copyfd_eof(STDIN_FILENO, dfd);
			xlseek(dfd, 0, SEEK_SET);
			*argv = (char*)bb_msg_standard_input;
		} else {
			dfd = xopen(*argv, O_RDONLY);
		}

		st.st_size = 0; /* paranoia: fstat may theoretically fail */
		fstat(dfd, &st);

		/* Apparently, some servers are buggy and won't accept 0-sized jobs.
		 * Standard lpr works around it by refusing to send such jobs:
		 */
		if (st.st_size == 0) {
			bb_error_msg("nothing to print");
			continue;
		}

		/* "The name ... should start with ASCII "cfA",
		 * followed by a three digit job number, followed
		 * by the host name which has constructed the file."
		 * We supply 'c' or 'd' as needed for control/data file. */
		remote_filename = xasprintf("fA%03u%s", job, hostname);

		// create control file
		// TODO: all lines but 2 last are constants! How we can use this fact?
		controlfile = xasprintf(
			"H" "%.32s\n" "P" "%.32s\n" /* H HOST, P USER */
			"C" "%.32s\n" /* C CLASS - printed on banner page (if L cmd is also given) */
			"J" "%.99s\n" /* J JOBNAME */
			/* "class name for banner page and job name
			 * for banner page commands must precede L command" */
			"L" "%.32s\n" /* L USER - print banner page, with given user's name */
			"M" "%.32s\n" /* M WHOM_TO_MAIL */
			"l" "d%.31s\n" /* l DATA_FILE_NAME ("dfAxxx") */
			, hostname, user
			, printer_class /* can be "" */
			, ((opts & LPR_J) ? job_title : *argv)
			, (opts & LPR_h) ? user : ""
			, (opts & LPR_m) ? user : ""
			, remote_filename
		);
		// delete possible "\nX\n" (that is, one-char) patterns
		c = controlfile;
		while ((c = strchr(c, '\n')) != NULL) {
			if (c[1] && c[2] == '\n') {
				overlapping_strcpy(c, c+2);
			} else {
				c++;
			}
		}

		// send control file
		if (opts & LPR_V)
			bb_error_msg("sending control file");
		/* "Acknowledgement processing must occur as usual
		 * after the command is sent." */
		cflen = (unsigned)strlen(controlfile);
		fdprintf(fd, "\x2" "%u c%s\n", cflen, remote_filename);
		get_response_or_say_and_die(fd, "sending control file");
		/* "Once all of the contents have
		 * been delivered, an octet of zero bits is sent as
		 * an indication that the file being sent is complete.
		 * A second level of acknowledgement processing
		 * must occur at this point." */
		full_write(fd, controlfile, cflen + 1); /* writes NUL byte too */
		get_response_or_say_and_die(fd, "sending control file");

		// send data file, with name "dfaXXX"
		if (opts & LPR_V)
			bb_error_msg("sending data file");
		fdprintf(fd, "\x3" "%"FILESIZE_FMT"u d%s\n", st.st_size, remote_filename);
		get_response_or_say_and_die(fd, "sending data file");
		if (bb_copyfd_size(dfd, fd, st.st_size) != st.st_size) {
			// We're screwed. We sent less bytes than we advertised.
			bb_error_msg_and_die("local file changed size?!");
		}
		write(fd, "", 1); // send ACK
		get_response_or_say_and_die(fd, "sending data file");

		// delete temporary file if we dumped stdin
		if (*argv == (char*)bb_msg_standard_input)
			unlink(tempfile);

		// cleanup
		close(fd);
		free(remote_filename);
		free(controlfile);

		// say job accepted
		if (opts & LPR_V)
			bb_error_msg("job accepted");

		// next, please!
		job = (job + 1) % 1000;
	} while (*++argv);

	return EXIT_SUCCESS;
}
Exemplo n.º 6
0
int sendmail_main(int argc UNUSED_PARAM, char **argv)
{
	char *opt_connect = opt_connect;
	char *opt_from;
	char *s;
	llist_t *list = NULL;
	char *domain = sane_address(safe_getdomainname());
	int code;

	enum {
	//--- standard options
		OPT_t = 1 << 0,         // read message for recipients, append them to those on cmdline
		OPT_f = 1 << 1,         // sender address
		OPT_o = 1 << 2,         // various options. -oi IMPLIED! others are IGNORED!
	//--- BB specific options
		OPT_w = 1 << 3,         // network timeout
		OPT_H = 1 << 4,         // use external connection helper
		OPT_S = 1 << 5,         // specify connection string
		OPT_a = 1 << 6,         // authentication tokens
	};

	// init global variables
	INIT_G();

	// save initial stdin since body is piped!
	xdup2(STDIN_FILENO, 3);
	G.fp0 = fdopen(3, "r");

	// parse options
	// -f is required. -H and -S are mutually exclusive
	opt_complementary = "f:w+:H--S:S--H:a::";
	// N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect
	// -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility,
	// it is still under development.
	opts = getopt32(argv, "tf:o:w:H:S:a::", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list);
	//argc -= optind;
	argv += optind;

	// process -a[upm]<token> options
	if ((opts & OPT_a) && !list)
		bb_show_usage();
	while (list) {
		char *a = (char *) llist_pop(&list);
		if ('u' == a[0])
			G.user = xstrdup(a+1);
		if ('p' == a[0])
			G.pass = xstrdup(a+1);
		// N.B. we support only AUTH LOGIN so far
		//if ('m' == a[0])
		//	G.method = xstrdup(a+1);
	}
	// N.B. list == NULL here
	//bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv);

	// connect to server

	// connection helper ordered? ->
	if (opts & OPT_H) {
		const char *args[] = { "sh", "-c", opt_connect, NULL };
		// plug it in
		launch_helper(args);
	// vanilla connection
	} else {
		int fd;
		// host[:port] not explicitly specified? -> use $SMTPHOST
		// no $SMTPHOST ? -> use localhost
		if (!(opts & OPT_S)) {
			opt_connect = getenv("SMTPHOST");
			if (!opt_connect)
				opt_connect = (char *)"127.0.0.1";
		}
		// do connect
		fd = create_and_connect_stream_or_die(opt_connect, 25);
		// and make ourselves a simple IO filter
		xmove_fd(fd, STDIN_FILENO);
		xdup2(STDIN_FILENO, STDOUT_FILENO);
	}
	// N.B. from now we know nothing about network :)

	// wait for initial server OK
	// N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure
	// so we need to kick the server to see whether we are ok
	code = smtp_check("NOOP", -1);
	// 220 on plain connection, 250 on openssl-helped TLS session
	if (220 == code)
		smtp_check(NULL, 250); // reread the code to stay in sync
	else if (250 != code)
		bb_error_msg_and_die("INIT failed");

	// we should start with modern EHLO
	if (250 != smtp_checkp("EHLO %s", domain, -1)) {
		smtp_checkp("HELO %s", domain, 250);
	}
	if (ENABLE_FEATURE_CLEAN_UP)
		free(domain);

	// perform authentication
	if (opts & OPT_a) {
		smtp_check("AUTH LOGIN", 334);
		// we must read credentials unless they are given via -a[up] options
		if (!G.user || !G.pass)
			get_cred_or_die(4);
		encode_base64(NULL, G.user, NULL);
		smtp_check("", 334);
		encode_base64(NULL, G.pass, NULL);
		smtp_check("", 235);
	}

	// set sender
	// N.B. we have here a very loosely defined algotythm
	// since sendmail historically offers no means to specify secrets on cmdline.
	// 1) server can require no authentication ->
	//	we must just provide a (possibly fake) reply address.
	// 2) server can require AUTH ->
	//	we must provide valid username and password along with a (possibly fake) reply address.
	//	For the sake of security username and password are to be read either from console or from a secured file.
	//	Since reading from console may defeat usability, the solution is either to read from a predefined
	//	file descriptor (e.g. 4), or again from a secured file.

	// got no sender address? -> use system username as a resort
	// N.B. we marked -f as required option!
	//if (!G.user) {
	//	// N.B. IMHO getenv("USER") can be way easily spoofed!
	//	G.user = xuid2uname(getuid());
	//	opt_from = xasprintf("%s@%s", G.user, domain);
	//}
	//if (ENABLE_FEATURE_CLEAN_UP)
	//	free(domain);
	smtp_checkp("MAIL FROM:<%s>", opt_from, 250);

	// process message

	// read recipients from message and add them to those given on cmdline.
	// this means we scan stdin for To:, Cc:, Bcc: lines until an empty line
	// and then use the rest of stdin as message body
	code = 0; // set "analyze headers" mode
	while ((s = xmalloc_fgetline(G.fp0)) != NULL) {
		// put message lines doubling leading dots
		if (code) {
			// escape leading dots
			// N.B. this feature is implied even if no -i (-oi) switch given
			// N.B. we need to escape the leading dot regardless of
			// whether it is single or not character on the line
			if ('.' == s[0] /*&& '\0' == s[1] */)
				printf(".");
			// dump read line
			printf("%s\r\n", s);
			free(s);
			continue;
		}

		// analyze headers
		// To: or Cc: headers add recipients
		if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Bcc: " + 1, s, 4)) {
			rcptto(sane_address(s+4));
//			goto addh;
			llist_add_to_end(&list, s);
		// Bcc: header adds blind copy (hidden) recipient
		} else if (0 == strncasecmp("Bcc: ", s, 5)) {
			rcptto(sane_address(s+5));
			free(s);
			// N.B. Bcc: vanishes from headers!
		// other headers go verbatim
		} else if (s[0]) {
// addh:
			llist_add_to_end(&list, s);
		// the empty line stops analyzing headers
		} else {
			free(s);
			// put recipients specified on cmdline
			while (*argv) {
				s = sane_address(*argv);
				rcptto(s);
				llist_add_to_end(&list, xasprintf("To: %s", s));
				argv++;
			}
			// enter "put message" mode
			smtp_check("DATA", 354);
			// dump the headers
			while (list) {
				printf("%s\r\n", (char *) llist_pop(&list));
			}
			printf("%s\r\n" + 2); // quirk for format string to be reused
			// stop analyzing headers
			code++;
		}
	}

	// finalize the message
	smtp_check(".", 250);
	// ... and say goodbye
	smtp_check("QUIT", 221);
	// cleanup
	if (ENABLE_FEATURE_CLEAN_UP)
		fclose(G.fp0);

	return EXIT_SUCCESS;
}
Exemplo n.º 7
0
int telnet_main(int argc UNUSED_PARAM, char **argv)
{
	char *host;
	int port;
	int len;
	struct pollfd ufds[2];

	INIT_G();

#if ENABLE_FEATURE_AUTOWIDTH
	get_terminal_width_height(0, &G.win_width, &G.win_height);
#endif

#if ENABLE_FEATURE_TELNET_TTYPE
	G.ttype = getenv("TERM");
#endif

	if (tcgetattr(0, &G.termios_def) >= 0) {
		G.do_termios = 1;
		G.termios_raw = G.termios_def;
		cfmakeraw(&G.termios_raw);
	}

#if ENABLE_FEATURE_TELNET_AUTOLOGIN
	if (1 & getopt32(argv, "al:", &G.autologin))
		G.autologin = getenv("USER");
	argv += optind;
#else
	argv++;
#endif
	if (!*argv)
		bb_show_usage();
	host = *argv++;
	port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
	if (*argv) /* extra params?? */
		bb_show_usage();

	xmove_fd(create_and_connect_stream_or_die(host, port), netfd);

	setsockopt_keepalive(netfd);

	signal(SIGINT, record_signo);

	ufds[0].fd = STDIN_FILENO;
	ufds[0].events = POLLIN;
	ufds[1].fd = netfd;
	ufds[1].events = POLLIN;

	while (1) {
		if (poll(ufds, 2, -1) < 0) {
			/* error, ignore and/or log something, bay go to loop */
			if (bb_got_signal)
				con_escape();
			else
				sleep(1);
			continue;
		}

// FIXME: reads can block. Need full bidirectional buffering.

		if (ufds[0].revents) {
			len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
			if (len <= 0)
				doexit(EXIT_SUCCESS);
			TRACE(0, ("Read con: %d\n", len));
			handle_net_output(len);
		}

		if (ufds[1].revents) {
			len = safe_read(netfd, G.buf, DATABUFSIZE);
			if (len <= 0) {
				full_write1_str("Connection closed by foreign host\r\n");
				doexit(EXIT_FAILURE);
			}
			TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
			handle_net_input(len);
		}
	} /* while (1) */
}