/*
* Command-Line Socket Interface
*
- * (c) 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * (c) 1998--2001 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
*
* This program can be freely distributed and used according
* to the terms of the GNU General Public Licence.
*/
-/*
- * FIXME: --version
- */
-
#include "config.h"
+#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
-#include <getopt.h>
+#include <sys/socket.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <sys/un.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
-#include <sys/signal.h>
+#include <signal.h>
#include <sys/wait.h>
#include <sys/time.h>
-#include <sys/types.h>
-static char *opts = "vtuxb:lde";
+#define SOCK_VERSION "1.1"
+
+static char *opts = "vtuxb:lden";
static enum { ip, ux } addr = ip;
static enum { deflt, tcp, udp, uxp } proto = deflt;
-static int listen_p, verbose, daemon_p, both_eof_p;
+static int listen_p, verbose, daemon_p, single_eof_p, avoid_dns_p;
static char *cmd, *my_name;
-static struct sockaddr sa_bind, sa_conn;
+static struct sockaddr *sa_bind, *sa_conn;
static char *na_bind, *na_conn;
-static int sa_bind_p;
+static int sock_addr_length;
#ifdef __GNUC__
#define NORET __attribute__((noreturn))
-b\tBind local end to specified address\n\
-l\tListening mode\n\
-d\tDaemon mode (listen only) -- process multiple connections\n\
--e\tTerminate only if EOF seen in both directions\n\
+-e\tTerminate as soon as EOF is seen in any direction\n\
+-n\tAvoid reverse DNS lookups\n\
", my_name);
exit(1);
}
exit(1);
}
+static void *
+xmalloc(int size)
+{
+ void *x = malloc(size);
+ if (!x)
+ die("Out of memory");
+ return x;
+}
+
+static struct sockaddr *
+alloc_sockaddr(void)
+{
+ struct sockaddr *sa;
+ sock_addr_length = (addr == ux) ? sizeof(struct sockaddr_un) : sizeof(struct sockaddr_in);
+ sa = xmalloc(sock_addr_length);
+ memset(sa, 0, sizeof(*sa));
+ sa->sa_family = (addr == ux) ? AF_UNIX : AF_INET;
+ return sa;
+}
+
static char *
-parse_addr(struct sockaddr *sa, char *a, int need_port, int need_addr)
+parse_addr(struct sockaddr **sap, char *a, int need_port, int need_addr)
{
char *o = a;
+ struct sockaddr *sa = alloc_sockaddr();
switch (addr)
{
else if (need_port)
die("%s: port number required", a);
s->sin_family = AF_INET;
+ if (!*a && need_addr)
+ a = "localhost";
if (*a)
{
struct hostent *e = gethostbyname(a);
memcpy(&s->sin_addr, e->h_addr, sizeof(struct in_addr));
o = e->h_name;
}
- else if (need_addr)
- die("Host name required");
if (p)
{
struct servent *e = NULL;
break;
}
}
+ *sap = sa;
return o;
}
case ip:
{
struct sockaddr_in *a = (struct sockaddr_in *) sa;
- struct hostent *h = gethostbyaddr((char *) &a->sin_addr.s_addr, sizeof(struct in_addr), AF_INET);
- if (!h)
- mdie("xxx");
- return h->h_name;
+ struct hostent *h = avoid_dns_p ? NULL : gethostbyaddr((char *) &a->sin_addr.s_addr, sizeof(struct in_addr), AF_INET);
+ if (h)
+ return h->h_name;
+ else
+ return inet_ntoa(a->sin_addr);
}
case ux:
{
static RETSIGTYPE
sigchld_handler(int sig)
{
- while (wait3(NULL, WNOHANG, NULL) > 0)
+ while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
{
struct sigaction sa;
- bzero(&sa, sizeof(sa));
+ memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigchld_handler;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) < 0)
int n;
fd_set in, out;
- if (fcntl(0, F_SETFL, O_NDELAY) < 0 ||
- fcntl(1, F_SETFL, O_NDELAY) < 0 ||
+ if (proto == udp)
+ {
+ if (listen_p)
+ ieof = 1;
+ else
+ oeof = 1;
+ }
+ if (!ieof && fcntl(0, F_SETFL, O_NDELAY) < 0 ||
+ !oeof && fcntl(1, F_SETFL, O_NDELAY) < 0 ||
fcntl(sk, F_SETFL, O_NDELAY) < 0)
mdie("fcntl");
FD_ZERO(&in);
ieof = 2;
}
if (ibr == ib && obr == ob &&
- (both_eof_p ? (ieof && oeof) : (ieof || oeof)))
+ (single_eof_p ? (ieof || oeof) : (ieof && oeof)))
break;
if (select(sk+1, &in, &out, NULL, NULL) < 0)
mdie("select");
{
int c, sk;
+ if (argc == 2 && !strcmp(argv[1], "--version"))
+ {
+ puts("This is sock " SOCK_VERSION ". Be happy.");
+ return 0;
+ }
+
my_name = argv[0];
while ((c = getopt(argc, argv, opts)) > 0)
switch (c)
daemon_p++;
break;
case 't':
+ if (proto != deflt)
+ usage();
proto = tcp;
break;
case 'u':
+ if (proto != deflt)
+ usage();
proto = udp;
break;
case 'x':
- addr = ux;
+ if (proto != deflt)
+ usage();
proto = uxp;
break;
case 'l':
listen_p++;
break;
case 'b':
- if (sa_bind_p++)
+ if (na_bind)
usage();
- na_bind = parse_addr(&sa_bind, optarg, 0, 0);
+ na_bind = optarg;
break;
case 'e':
- both_eof_p++;
+ single_eof_p++;
+ break;
+ case 'n':
+ avoid_dns_p++;
break;
default:
usage();
else if (argc != optind + 1)
usage();
+ if (proto == uxp)
+ addr = ux;
+
if (listen_p)
{
- if (sa_bind_p)
+ if (na_bind)
usage();
na_bind = parse_addr(&sa_bind, argv[optind], 1, 0);
- sa_bind_p = 1;
}
else
- na_conn = parse_addr(&sa_conn, argv[optind], 1, 1);
+ {
+ if (na_bind)
+ na_bind = parse_addr(&sa_bind, na_bind, 0, 0);
+ na_conn = parse_addr(&sa_conn, argv[optind], 1, 1);
+ }
switch (proto)
{
}
if (sk < 0)
mdie("socket");
- if (sa_bind_p)
+ if (sa_bind)
{
int one = 1;
- if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+ if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one)) < 0)
mdie("setsockopt(SO_REUSEADDR)");
- if (bind(sk, &sa_bind, sizeof(sa_bind)) < 0)
+ if (bind(sk, sa_bind, sock_addr_length) < 0)
mdie("bind");
}
if (listen_p)
{
- int l = sizeof(sa_conn);
+ struct sockaddr *sa_incoming;
if (verbose)
fprintf(stderr, "Listening on %s\n", na_bind);
+ if (proto == udp)
+ {
+ gw(sk);
+ return 0;
+ }
if (listen(sk, (daemon_p ? 10 : 1)) < 0)
mdie("listen");
if (cmd && daemon_p)
setup_sigchld();
+ sa_incoming = alloc_sockaddr();
for(;;)
{
- int ns = accept(sk, &sa_conn, &l);
+ int l = sock_addr_length;
+ int ns = accept(sk, sa_incoming, &l);
if (ns < 0)
mdie("accept");
if (verbose)
- fprintf(stderr, "Got connection from %s\n", name_addr(&sa_conn));
+ fprintf(stderr, "Got connection from %s\n", name_addr(sa_incoming));
if (!daemon_p)
{
close(sk);
{
if (verbose)
fprintf(stderr, "Connecting to %s\n", na_conn);
- if (connect(sk, &sa_conn, sizeof(sa_conn)) < 0)
+ if (connect(sk, sa_conn, sock_addr_length) < 0)
mdie("connect");
gw(sk);
}