view toys/netcat.c @ 303:b8f6dd7cf7f7

Basic tests for sort. Needs more...
author Rob Landley <rob@landley.net>
date Sun, 22 Jun 2008 04:18:39 -0500
parents 7cb15eae1664
children dfbfbaeb69c9
line wrap: on
line source

/* vi: set sw=4 ts=4:
 *
 * nc: mini-netcat - Forward stdin/stdout to a file or network connection.
 *
 * Copyright 2007 Rob Landley <rob@landley.net>
 *
 * Not in SUSv3.

USE_NETCAT(OLDTOY(nc, netcat, "i#w#l@p#s:q#f:e", TOYFLAG_BIN))
USE_NETCAT(NEWTOY(netcat, "i#w#l@p#s:q#f:e", TOYFLAG_BIN))

config NETCAT
	bool "netcat"
	default n
	help
	  usage: netcat [-iwlp] {IPADDR PORTNUM|-f FILENAME} [-e COMMAND]

	  -e	exec the rest of the command line
	  -i	SECONDS delay after each line sent
	  -w	SECONDS timeout for connection
	  -f	filename use file (ala /dev/ttyS0) instead of network
	  -l	listen for incoming connection (twice for persistent connection)
	  -p	local port number
	  -s	local source address
	  -q	SECONDS quit this many seconds after EOF on stdin.

	  Use -l twice with -e for a quick-and-dirty server.

	  Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
	  netcat -f to connect to a serial port.
*/

#include "toys.h"
#include "toynet.h"

DEFINE_GLOBALS(
	char *filename;        // -f read from filename instead of network
	long quit_delay;       // -q Exit after EOF from stdin after # seconds.
	char *source_address;  // -s Bind to a specific source address.
	long port;             // -p Bind to a specific source port.
	long listen;           // -l Listen for connection instead of dialing out.
	long wait;             // -w Wait # seconds for a connection.
	long delay;            // -i delay between lines sent
)

#define TT this.netcat

static void timeout(int signum)
{
	error_exit("Timeout");
}

// Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name.
void lookup_name(char *name, uint32_t *result)
{
	struct hostent *hostbyname;

	hostbyname = gethostbyname(*toys.optargs);
	if (!hostbyname) error_exit("name lookup failed");
	*result = *(uint32_t *)*hostbyname->h_addr_list;
}

// Worry about a fancy lookup later.
void lookup_port(char *str, uint16_t *port)
{
  *port = SWAP_BE16(atoi(str));
}

void netcat_main(void)
{
	int sockfd, pollcount;
	struct pollfd pollfds[2];

	if (TT.wait) {
		signal(SIGALRM, timeout);
		alarm(TT.wait);
	}

	if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR);
	else {
		int temp;
		struct sockaddr_in address;

		// The argument parsing logic can't make "<2" conditional on "-f", so...
		if (!*toys.optargs || !toys.optargs[1]) {
			toys.exithelp++;
			error_exit("Need address and port");
		}

		// Setup socket
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		if (-1 == sockfd) perror_exit("socket");
		fcntl(sockfd, F_SETFD, FD_CLOEXEC);
		temp = 1;
		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp));
		memset(&address, 0, sizeof(address));
		address.sin_family = AF_INET;
		if (TT.port) {
			address.sin_port = TT.port;
			if (-1 == bind(sockfd, &address, sizeof(address)))
				perror_exit("bind");
		}

		// Figure out where to dial out to.
		lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr);
		lookup_port(toys.optargs[1], &address.sin_port);
		temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
		if (temp<0) perror_exit("connect");
		pollfds[0].fd = sockfd;
	}

	// We have a connection.  Disarm timeout.
	if (TT.wait) {
		alarm(0);
		signal(SIGALRM, SIG_DFL);
	}

	pollcount = 2;
	pollfds[1].fd = 0;
	pollfds[0].events = pollfds[1].events = POLLIN;

	// Poll loop copying stdin->socket and socket->stdout.
	for (;;) {
		int i;

		if (0>poll(pollfds, pollcount, -1)) perror_exit("poll");

		for (i=0; i<pollcount; i++) {
			if (pollfds[i].revents & POLLIN) {
				int len = read(pollfds[i].fd, toybuf, sizeof(toybuf));
				if (len<1) goto dohupnow;
				xwrite(i ? pollfds[0].fd : 1, toybuf, len);
			}
			if (pollfds[i].revents & POLLHUP) {
dohupnow:
				// Close half-connect.  This is needed for things like
				// "echo GET / | netcat landley.net 80" to work.
				if (i) {
					shutdown(pollfds[0].fd, SHUT_WR);
					pollcount--;
				} else goto cleanup;
			}
		}
	}
cleanup:
	close(pollfds[0].fd);
//	close(sockfd);
}