1/* -*- c-basic-offset: 8; -*-
2 *
3 * Copyright (c) 1993 W. Richard Stevens.  All rights reserved.
4 * Permission to use or modify this software and its documentation only for
5 * educational purposes and without fee is hereby granted, provided that
6 * the above copyright notice appear in all copies.  The author makes no
7 * representations about the suitability of this software for any purpose.
8 * It is provided "as is" without express or implied warranty.
9 */
10
11#include <stdio.h>
12#include <netdb.h>
13#include <sys/types.h>
14#include <netinet/in.h>
15#include <arpa/inet.h>
16#include <unistd.h>
17#ifdef HAVE_GETOPT_H
18#include <getopt.h>
19#endif
20#include <string.h>
21#include	"sock.h"
22
23char	*host;		/* hostname or dotted-decimal string */
24char	*port;
25
26			/* DefinE global variables */
27int		bindport;			/* 0 or TCP or UDP port number to bind */
28							/* set by -b or -l options */
29int		broadcast;			/* SO_BROADCAST */
30int		cbreak;				/* set terminal to cbreak mode */
31int		chunkwrite;			/* write in small chunks; not all-at-once */
32int		client = 1;			/* acting as client is the default */
33int		connectudp = 1;			/* connect UDP client */
34int		crlf;				/* convert newline to CR/LF & vice versa */
35int		debug;				/* SO_DEBUG */
36int		dofork;				/* concurrent server, do a fork() */
37int		dontroute;			/* SO_DONTROUTE */
38char		foreignip[32];			/* foreign IP address, dotted-decimal string */
39int		foreignport;			/* foreign port number */
40int		halfclose;			/* TCP half close option */
41int		ignorewerr;			/* true if write() errors should be ignored */
42int		iptos = -1;			/* IP_TOS opton */
43int		ipttl = -1;			/* IP_TTL opton */
44char		joinip[32];			/* multicast IP address, dotted-decimal string */
45int		keepalive;			/* SO_KEEPALIVE */
46long		linger = -1;			/* 0 or positive turns on option */
47int		listenq = 5;			/* listen queue for TCP Server */
48char		localip[32];			/* local IP address, dotted-decimal string */
49int		maxseg;				/* TCP_MAXSEG */
50int		mcastttl;			/* multicast TTL */
51int		msgpeek;			/* MSG_PEEK */
52int		nodelay;			/* TCP_NODELAY (Nagle algorithm) */
53int		nbuf = 1024;			/* number of buffers to write (sink mode) */
54int		onesbcast;			/* set IP_ONESBCAST for 255.255.255.255 bcasts */
55int		pauseclose;			/* #ms to sleep after recv FIN, before close */
56int		pauseinit;			/* #ms to sleep before first read */
57int		pauselisten;			/* #ms to sleep after listen() */
58int		pauserw;			/* #ms to sleep before each read or write */
59int		reuseaddr;			/* SO_REUSEADDR */
60int		reuseport;			/* SO_REUSEPORT */
61int		readlen = 1024;			/* default read length for socket */
62int		writelen = 1024;		/* default write length for socket */
63int		recvdstaddr;			/* IP_RECVDSTADDR option */
64int		rcvbuflen;			/* size for SO_RCVBUF */
65int		sndbuflen;			/* size for SO_SNDBUF */
66long		rcvtimeo;			/* SO_RCVTIMEO */
67long		sndtimeo;			/* SO_SNDTIMEO */
68int		sroute_cnt;			/* count of #IP addresses in route */
69char   		*rbuf;				/* pointer that is malloc'ed */
70char   		*wbuf;				/* pointer that is malloc'ed */
71int		server;				/* to act as server requires -s option */
72int		sigio;				/* send SIGIO */
73int		sourcesink;			/* source/sink mode */
74int		udp;				/* use UDP instead of TCP */
75int		urgwrite;			/* write urgent byte after this write */
76int		verbose;			/* each -v increments this by 1 */
77int		usewritev;			/* use writev() instead of write() */
78
79struct sockaddr_in	cliaddr, servaddr;
80
81static void	usage(const char *);
82
83int
84main(int argc, char *argv[])
85{
86	int	c, fd;
87	char	*ptr;
88
89	if (argc < 2)
90		usage("");
91
92	opterr = 0;		/* don't want getopt() writing to stderr */
93	while ( (c = getopt(argc, argv, "2b:cf:g:hij:kl:n:op:q:r:st:uvw:x:y:ABCDEFG:H:IJ:KL:NO:P:Q:R:S:TU:VWX:YZ")) != -1) {
94		switch (c) {
95#ifdef	IP_ONESBCAST
96		case '2':			/* use 255.255.255.255 as broadcast address */
97			onesbcast = 1;
98			break;
99#endif
100
101		case 'b':
102			bindport = atoi(optarg);
103			break;
104
105		case 'c':			/* convert newline to CR/LF & vice versa */
106			crlf = 1;
107			break;
108
109		case 'f':			/* foreign IP address and port#: a.b.c.d.p */
110			if ( (ptr = strrchr(optarg, '.')) == NULL)
111				usage("invalid -f option");
112
113			*ptr++ = 0;					/* null replaces final period */
114			foreignport = atoi(ptr);	/* port number */
115			strcpy(foreignip, optarg);	/* save dotted-decimal IP */
116			break;
117
118		case 'g':			/* loose source route */
119			sroute_doopt(0, optarg);
120			break;
121
122		case 'h':			/* TCP half-close option */
123			halfclose = 1;
124			break;
125
126		case 'i':			/* source/sink option */
127			sourcesink = 1;
128			break;
129
130#ifdef	IP_ADD_MEMBERSHIP
131		case 'j':			/* join multicast group a.b.c.d */
132			strcpy(joinip, optarg);	/* save dotted-decimal IP */
133			break;
134#endif
135
136		case 'k':			/* chunk-write option */
137			chunkwrite = 1;
138			break;
139
140		case 'l':			/* local IP address and port#: a.b.c.d.p */
141			if ( (ptr = strrchr(optarg, '.')) == NULL)
142				usage("invalid -l option");
143
144			*ptr++ = 0;					/* null replaces final period */
145			bindport = atoi(ptr);		/* port number */
146			strcpy(localip, optarg);	/* save dotted-decimal IP */
147			break;
148
149		case 'n':			/* number of buffers to write */
150			nbuf = atol(optarg);
151			break;
152
153		case 'o':			/* do not connect UDP client */
154			connectudp = 0;
155			break;
156
157		case 'p':			/* pause before each read or write */
158			pauserw = atoi(optarg);
159			break;
160
161		case 'q':			/* listen queue for TCP server */
162			listenq = atoi(optarg);
163			break;
164
165		case 'r':			/* read() length */
166			readlen = atoi(optarg);
167			break;
168
169		case 's':			/* server */
170			server = 1;
171			client = 0;
172			break;
173
174#ifdef	IP_MULTICAST_TTL
175		case 't':			/* IP_MULTICAST_TTL */
176			mcastttl = atoi(optarg);
177			break;
178#endif
179
180		case 'u':			/* use UDP instead of TCP */
181			udp = 1;
182			break;
183
184		case 'v':			/* output what's going on */
185			verbose++;
186			break;
187
188		case 'w':			/* write() length */
189			writelen = atoi(optarg);
190			break;
191
192		case 'x':			/* SO_RCVTIMEO socket option */
193			rcvtimeo = atol(optarg);
194			break;
195
196		case 'y':			/* SO_SNDTIMEO socket option */
197			sndtimeo = atol(optarg);
198			break;
199
200		case 'A':			/* SO_REUSEADDR socket option */
201			reuseaddr = 1;
202			break;
203
204		case 'B':			/* SO_BROADCAST socket option */
205			broadcast = 1;
206			break;
207
208		case 'C':			/* set standard input to cbreak mode */
209			cbreak = 1;
210			break;
211
212		case 'D':			/* SO_DEBUG socket option */
213			debug = 1;
214			break;
215
216		case 'E':			/* IP_RECVDSTADDR socket option */
217			recvdstaddr = 1;
218			break;
219
220		case 'F':			/* concurrent server, do a fork() */
221			dofork = 1;
222			break;
223
224		case 'G':			/* strict source route */
225			sroute_doopt(1, optarg);
226			break;
227
228#ifdef	IP_TOS
229		case 'H':			/* IP_TOS socket option */
230			iptos = atoi(optarg);
231			break;
232#endif
233
234		case 'I':			/* SIGIO signal */
235			sigio = 1;
236			break;
237
238#ifdef	IP_TTL
239		case 'J':			/* IP_TTL socket option */
240			ipttl = atoi(optarg);
241			break;
242#endif
243
244		case 'K':			/* SO_KEEPALIVE socket option */
245			keepalive = 1;
246			break;
247
248		case 'L':			/* SO_LINGER socket option */
249			linger = atol(optarg);
250			break;
251
252		case 'N':			/* SO_NODELAY socket option */
253			nodelay = 1;
254			break;
255
256		case 'O':			/* pause before listen(), before first accept() */
257			pauselisten = atoi(optarg);
258			break;
259
260		case 'P':			/* pause before first read() */
261			pauseinit = atoi(optarg);
262			break;
263
264		case 'Q':			/* pause after receiving FIN, but before close() */
265			pauseclose = atoi(optarg);
266			break;
267
268		case 'R':			/* SO_RCVBUF socket option */
269			rcvbuflen = atoi(optarg);
270			break;
271
272		case 'S':			/* SO_SNDBUF socket option */
273			sndbuflen = atoi(optarg);
274			break;
275
276#ifdef	SO_REUSEPORT
277		case 'T':			/* SO_REUSEPORT socket option */
278			reuseport = 1;
279			break;
280#endif
281
282		case 'U':			/* when to write urgent byte */
283			urgwrite = atoi(optarg);
284			break;
285
286		case 'V':			/* use writev() instead of write() */
287			usewritev = 1;
288			chunkwrite = 1;	/* implies this option too */
289			break;
290
291		case 'W':			/* ignore write errors */
292			ignorewerr = 1;
293			break;
294
295		case 'X':			/* TCP maximum segment size option */
296			maxseg = atoi(optarg);
297			break;
298
299		case 'Y':			/* SO_DONTROUTE socket option */
300			dontroute = 1;
301			break;
302
303		case 'Z':			/* MSG_PEEK option */
304			msgpeek = MSG_PEEK;
305			break;
306
307		case '?':
308			usage("unrecognized option");
309		}
310	}
311
312		/* check for options that don't make sense */
313	if (udp && halfclose)
314		usage("can't specify -h and -u");
315	if (udp && debug)
316		usage("can't specify -D and -u");
317	if (udp && linger >= 0)
318		usage("can't specify -L and -u");
319	if (udp && nodelay)
320		usage("can't specify -N and -u");
321#ifdef	notdef
322	if (udp == 0 && broadcast)
323		usage("can't specify -B with TCP");
324#endif
325	if (udp == 0 && foreignip[0] != 0)
326		usage("can't specify -f with TCP");
327
328	if (client) {
329		if (optind != argc-2)
330			usage("missing <hostname> and/or <port>");
331		host = argv[optind];
332		port = argv[optind+1];
333
334	} else {
335			/* If server specifies host and port, then local address is
336			   bound to the "host" argument, instead of being wildcarded. */
337		if (optind == argc-2) {
338			host = argv[optind];
339			port = argv[optind+1];
340		} else if (optind == argc-1) {
341			host = NULL;
342			port = argv[optind];
343		} else
344			usage("missing <port>");
345	}
346
347	if (client)
348		fd = cliopen(host, port);
349	else
350		fd = servopen(host, port);
351
352	if (sourcesink) {		/* ignore stdin/stdout */
353		if (client) {
354			if (udp)
355				source_udp(fd);
356			else
357				source_tcp(fd);
358		} else {
359			if (udp)
360				sink_udp(fd);
361			else
362				sink_tcp(fd);
363		}
364
365	} else {				/* copy stdin/stdout to/from socket */
366		if (udp)
367			loop_udp(fd);
368		else
369			loop_tcp(fd);
370	}
371
372	exit(0);
373}
374
375static void
376usage(const char *msg)
377{
378	err_msg(
379"usage: sock [ options ] <host> <port>              (for client; default)\n"
380"       sock [ options ] -s [ <IPaddr> ] <port>     (for server)\n"
381"       sock [ options ] -i <host> <port>           (for \"source\" client)\n"
382"       sock [ options ] -i -s [ <IPaddr> ] <port>  (for \"sink\" server)\n"
383"options: -b n  bind n as client's local port number\n"
384"         -c    convert newline to CR/LF & vice versa\n"
385"         -f a.b.c.d.p  foreign IP address = a.b.c.d, foreign port# = p\n"
386"         -g a.b.c.d  loose source route\n"
387"         -h    issue TCP half close on standard input EOF\n"
388"         -i    \"source\" data to socket, \"sink\" data from socket (w/-s)\n"
389#ifdef	IP_ADD_MEMBERSHIP
390"         -j a.b.c.d  join multicast group\n"
391#endif
392"         -k    write or writev in chunks\n"
393"         -l a.b.c.d.p  client's local IP address = a.b.c.d, local port# = p\n"
394"         -n n  #buffers to write for \"source\" client (default 1024)\n"
395"         -o    do NOT connect UDP client\n"
396"         -p n  #ms to pause before each read or write (source/sink)\n"
397"         -q n  size of listen queue for TCP server (default 5)\n"
398"         -r n  #bytes per read() for \"sink\" server (default 1024)\n"
399"         -s    operate as server instead of client\n"
400#ifdef	IP_MULTICAST_TTL
401"         -t n  set multicast ttl\n"
402#endif
403"         -u    use UDP instead of TCP\n"
404"         -v    verbose\n"
405"         -w n  #bytes per write() for \"source\" client (default 1024)\n"
406"         -x n  #ms for SO_RCVTIMEO (receive timeout)\n"
407"         -y n  #ms for SO_SNDTIMEO (send timeout)\n"
408"         -A    SO_REUSEADDR option\n"
409"         -B    SO_BROADCAST option\n"
410"         -C    set terminal to cbreak mode\n"
411"         -D    SO_DEBUG option\n"
412"         -E    IP_RECVDSTADDR option\n"
413"         -F    fork after connection accepted (TCP concurrent server)\n"
414"         -G a.b.c.d  strict source route\n"
415#ifdef	IP_TOS
416"         -H n  IP_TOS option (16=min del, 8=max thru, 4=max rel, 2=min$)\n"
417#endif
418"         -I    SIGIO signal\n"
419#ifdef	IP_TTL
420"         -J n  IP_TTL option\n"
421#endif
422"         -K    SO_KEEPALIVE option\n"
423"         -L n  SO_LINGER option, n = linger time\n"
424"         -N    TCP_NODELAY option\n"
425"         -O n  #ms to pause after listen, but before first accept\n"
426"         -P n  #ms to pause before first read or write (source/sink)\n"
427"         -Q n  #ms to pause after receiving FIN, but before close\n"
428"         -R n  SO_RCVBUF option\n"
429"         -S n  SO_SNDBUF option\n"
430#ifdef	SO_REUSEPORT
431"         -T    SO_REUSEPORT option\n"
432#endif
433"         -U n  enter urgent mode before write number n (source only)\n"
434"         -V    use writev() instead of write(); enables -k too\n"
435"         -W    ignore write errors for sink client\n"
436"         -X n  TCP_MAXSEG option (set MSS)\n"
437"         -Y    SO_DONTROUTE option\n"
438"         -Z    MSG_PEEK\n"
439#ifdef	IP_ONESBCAST
440"         -2    IP_ONESBCAST option (255.255.255.255 for broadcast\n"
441#endif
442);
443
444	if (msg[0] != 0)
445		err_quit("%s", msg);
446	exit(1);
447}
448