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