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
12#include <stdio.h>
13#include <netdb.h>
14#include <sys/types.h>
15#include <netinet/in.h>
16#include <arpa/inet.h>
17#include <sys/time.h>
18#include <unistd.h>
19#include <signal.h>
20#include "sock.h"
21#include <fcntl.h>
22#include <sys/ioctl.h>
23
24#ifdef	FIOASYNC
25static void sigio_func(int);
26
27static void
28sigio_func(int signo)
29{
30	fprintf(stderr, "SIGIO\n");
31	/* shouldn't printf from a signal handler ... */
32}
33#endif
34
35void
36sockopts(int sockfd, int doall)
37{
38	int 		option;
39	unsigned	optlen;
40	struct linger	ling;
41	struct timeval	timer;
42
43	/* "doall" is 0 for a server's listening socket (i.e., before
44	   accept() has returned.)  Some socket options such as SO_KEEPALIVE
45	   don't make sense at this point, while others like SO_DEBUG do. */
46
47	if (debug) {
48		option = 1;
49		if (setsockopt(sockfd, SOL_SOCKET, SO_DEBUG,
50			       &option, sizeof(option)) < 0)
51			err_sys("SO_DEBUG setsockopt error");
52
53		option = 0;
54		optlen = sizeof(option);
55		if (getsockopt(sockfd, SOL_SOCKET, SO_DEBUG,
56			       &option, &optlen) < 0)
57			err_sys("SO_DEBUG getsockopt error");
58		if (option == 0)
59			err_quit("SO_DEBUG not set (%d)", option);
60
61		if (verbose)
62			fprintf(stderr, "SO_DEBUG set\n");
63	}
64
65	if (dontroute) {
66		option = 1;
67		if (setsockopt(sockfd, SOL_SOCKET, SO_DONTROUTE,
68			       &option, sizeof(option)) < 0)
69			err_sys("SO_DONTROUTE setsockopt error");
70
71		option = 0;
72		optlen = sizeof(option);
73		if (getsockopt(sockfd, SOL_SOCKET, SO_DONTROUTE,
74			       &option, &optlen) < 0)
75			err_sys("SO_DONTROUTE getsockopt error");
76		if (option == 0)
77			err_quit("SO_DONTROUTE not set (%d)", option);
78
79		if (verbose)
80			fprintf(stderr, "SO_DONTROUTE set\n");
81	}
82
83#ifdef	IP_TOS
84	if (iptos != -1 && doall == 0) {
85		if (setsockopt(sockfd, IPPROTO_IP, IP_TOS,
86			       &iptos, sizeof(iptos)) < 0)
87			err_sys("IP_TOS setsockopt error");
88
89		option = 0;
90		optlen = sizeof(option);
91		if (getsockopt(sockfd, IPPROTO_IP, IP_TOS,
92			       &option, &optlen) < 0)
93			err_sys("IP_TOS getsockopt error");
94		if (option != iptos)
95			err_quit("IP_TOS not set (%d)", option);
96
97		if (verbose)
98			fprintf(stderr, "IP_TOS set to %d\n", iptos);
99	}
100#endif
101
102#ifdef	IP_TTL
103	if (ipttl != -1 && doall == 0) {
104		if (setsockopt(sockfd, IPPROTO_IP, IP_TTL,
105			       &ipttl, sizeof(ipttl)) < 0)
106			err_sys("IP_TTL setsockopt error");
107
108		option = 0;
109		optlen = sizeof(option);
110		if (getsockopt(sockfd, IPPROTO_IP, IP_TTL,
111			       &option, &optlen) < 0)
112			err_sys("IP_TTL getsockopt error");
113		if (option != ipttl)
114			err_quit("IP_TTL not set (%d)", option);
115
116		if (verbose)
117			fprintf(stderr, "IP_TTL set to %d\n", ipttl);
118	}
119#endif
120
121	if (maxseg && udp == 0) {
122		/* Need to set MSS for server before connection established */
123		/* Beware: some kernels do not let the process set this socket
124		   option; others only let it be decreased. */
125		if (setsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG,
126			       &maxseg, sizeof(maxseg)) < 0)
127			err_sys("TCP_MAXSEG setsockopt error");
128
129		option = 0;
130		optlen = sizeof(option);
131		if (getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG,
132			       &option, &optlen) < 0)
133		    err_sys("TCP_MAXSEG getsockopt error");
134
135		if (verbose)
136			fprintf(stderr, "TCP_MAXSEG = %d\n", option);
137	}
138
139	if (sroute_cnt > 0)
140		sroute_set(sockfd);
141
142	if (broadcast) {
143		option = 1;
144		if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,
145			       &option, sizeof(option)) < 0)
146			err_sys("SO_BROADCAST setsockopt error");
147
148		option = 0;
149		optlen = sizeof(option);
150		if (getsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,
151			       &option, &optlen) < 0)
152			err_sys("SO_BROADCAST getsockopt error");
153		if (option == 0)
154			err_quit("SO_BROADCAST not set (%d)", option);
155
156		if (verbose)
157			fprintf(stderr, "SO_BROADCAST set\n");
158
159#ifdef	IP_ONESBCAST
160		if (onesbcast) {
161			option = 1;
162			if (setsockopt(sockfd, IPPROTO_IP, IP_ONESBCAST,
163				       &option, sizeof(option)) < 0)
164				err_sys("IP_ONESBCAST setsockopt error");
165
166			option = 0;
167			optlen = sizeof(option);
168			if (getsockopt(sockfd, IPPROTO_IP, IP_ONESBCAST,
169				       &option, &optlen) < 0)
170				err_sys("IP_ONESBCAST getsockopt error");
171			if (option == 0)
172				err_quit("IP_ONESBCAST not set (%d)", option);
173
174			if (verbose)
175				fprintf(stderr, "IP_ONESBCAST set\n");
176		}
177#endif
178	}
179
180#ifdef	IP_ADD_MEMBERSHIP
181	if (joinip[0]) {
182		struct ip_mreq	join;
183
184		if (inet_aton(joinip, &join.imr_multiaddr) == 0)
185			err_quit("invalid multicast address: %s", joinip);
186		join.imr_interface.s_addr = htonl(INADDR_ANY);
187		if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
188			       &join, sizeof(join)) < 0)
189			err_sys("IP_ADD_MEMBERSHIP setsockopt error");
190
191		if (verbose)
192			fprintf(stderr, "IP_ADD_MEMBERSHIP set\n");
193	}
194#endif
195
196#ifdef	IP_MULTICAST_TTL
197	if (mcastttl) {
198		u_char	ttl = mcastttl;
199
200		if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
201			       &ttl, sizeof(ttl)) < 0)
202			err_sys("IP_MULTICAST_TTL setsockopt error");
203
204		optlen = sizeof(ttl);
205		if (getsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
206			       &ttl, &optlen) < 0)
207			err_sys("IP_MULTICAST_TTL getsockopt error");
208		if (ttl != mcastttl)
209			err_quit("IP_MULTICAST_TTL not set (%d)", ttl);
210
211		if (verbose)
212			fprintf(stderr, "IP_MULTICAST_TTL set to %d\n", ttl);
213	}
214#endif
215
216	if (keepalive && doall && udp == 0) {
217		option = 1;
218		if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
219			       &option, sizeof(option)) < 0)
220			err_sys("SO_KEEPALIVE setsockopt error");
221
222		option = 0;
223		optlen = sizeof(option);
224		if (getsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
225			       &option, &optlen) < 0)
226			err_sys("SO_KEEPALIVE getsockopt error");
227		if (option == 0)
228			err_quit("SO_KEEPALIVE not set (%d)", option);
229
230		if (verbose)
231			fprintf(stderr, "SO_KEEPALIVE set\n");
232	}
233
234	if (nodelay && doall && udp == 0) {
235		option = 1;
236		if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
237			       &option, sizeof(option)) < 0)
238			err_sys("TCP_NODELAY setsockopt error");
239
240		option = 0;
241		optlen = sizeof(option);
242		if (getsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
243			       &option, &optlen) < 0)
244			err_sys("TCP_NODELAY getsockopt error");
245		if (option == 0)
246			err_quit("TCP_NODELAY not set (%d)", option);
247
248		if (verbose)
249			fprintf(stderr, "TCP_NODELAY set\n");
250	}
251
252	if (doall && verbose && udp == 0) {	/* just print MSS if verbose */
253		option = 0;
254		optlen = sizeof(option);
255		if (getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG,
256			       &option, &optlen) < 0)
257			err_sys("TCP_MAXSEG getsockopt error");
258
259		fprintf(stderr, "TCP_MAXSEG = %d\n", option);
260	}
261
262	if (linger >= 0 && doall && udp == 0) {
263		ling.l_onoff = 1;
264		ling.l_linger = linger;		/* 0 for abortive disconnect */
265		if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER,
266			       &ling, sizeof(ling)) < 0)
267			err_sys("SO_LINGER setsockopt error");
268
269		ling.l_onoff = 0;
270		ling.l_linger = -1;
271		optlen = sizeof(struct linger);
272		if (getsockopt(sockfd, SOL_SOCKET, SO_LINGER,
273			       &ling, &optlen) < 0)
274			err_sys("SO_LINGER getsockopt error");
275		if (ling.l_onoff == 0 || ling.l_linger != linger)
276			err_quit("SO_LINGER not set (%d, %d)", ling.l_onoff, ling.l_linger);
277
278		if (verbose)
279			fprintf(stderr, "linger %s, time = %d\n",
280				ling.l_onoff ? "on" : "off", ling.l_linger);
281	}
282
283	if (doall && rcvtimeo) {
284#ifdef	SO_RCVTIMEO
285	    /* User specifies millisec, must convert to sec/usec */
286		timer.tv_sec  =  rcvtimeo / 1000;
287		timer.tv_usec = (rcvtimeo % 1000) * 1000;
288		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,
289			       &timer, sizeof(timer)) < 0)
290			err_sys("SO_RCVTIMEO setsockopt error");
291
292		timer.tv_sec = timer.tv_usec = 0;
293		optlen = sizeof(timer);
294		if (getsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,
295			       &timer, &optlen) < 0)
296			err_sys("SO_RCVTIMEO getsockopt error");
297
298		if (verbose)
299			fprintf(stderr, "SO_RCVTIMEO: %ld.%06ld\n",
300				timer.tv_sec, timer.tv_usec);
301#else
302		fprintf(stderr, "warning: SO_RCVTIMEO not supported by host\n");
303#endif
304	}
305
306	if (doall && sndtimeo) {
307#ifdef	SO_SNDTIMEO
308		/* User specifies millisec, must convert to sec/usec */
309		timer.tv_sec  =  sndtimeo / 1000;
310		timer.tv_usec = (sndtimeo % 1000) * 1000;
311		if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO,
312			       &timer, sizeof(timer)) < 0)
313			err_sys("SO_SNDTIMEO setsockopt error");
314
315		timer.tv_sec = timer.tv_usec = 0;
316		optlen = sizeof(timer);
317		if (getsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO,
318			       &timer, &optlen) < 0)
319			err_sys("SO_SNDTIMEO getsockopt error");
320
321		if (verbose)
322			fprintf(stderr, "SO_SNDTIMEO: %ld.%06ld\n",
323				timer.tv_sec, timer.tv_usec);
324#else
325		fprintf(stderr, "warning: SO_SNDTIMEO not supported by host\n");
326#endif
327	}
328
329	if (recvdstaddr && udp) {
330#ifdef	IP_RECVDSTADDR
331		option = 1;
332		if (setsockopt(sockfd, IPPROTO_IP, IP_RECVDSTADDR,
333			       &option, sizeof(option)) < 0)
334			err_sys("IP_RECVDSTADDR setsockopt error");
335
336		option = 0;
337		optlen = sizeof(option);
338		if (getsockopt(sockfd, IPPROTO_IP, IP_RECVDSTADDR,
339			       &option, &optlen) < 0)
340			err_sys("IP_RECVDSTADDR getsockopt error");
341		if (option == 0)
342			err_quit("IP_RECVDSTADDR not set (%d)", option);
343
344		if (verbose)
345			fprintf(stderr, "IP_RECVDSTADDR set\n");
346#else
347		fprintf(stderr, "warning: IP_RECVDSTADDR not supported by host\n");
348#endif
349	}
350
351	if (sigio) {
352#ifdef	FIOASYNC
353		/*
354		 * Should be able to set this with fcntl(O_ASYNC) or fcntl(FASYNC),
355		 * but some systems (AIX?) only do it with ioctl().
356		 *
357		 * Need to set this for listening socket and for connected socket.
358		 */
359		signal(SIGIO, sigio_func);
360
361		if (fcntl(sockfd, F_SETOWN, getpid()) < 0)
362			err_sys("fcntl F_SETOWN error");
363
364		option = 1;
365		if (ioctl(sockfd, FIOASYNC, (char *) &option) < 0)
366			err_sys("ioctl FIOASYNC error");
367
368		if (verbose)
369			fprintf(stderr, "FIOASYNC set\n");
370#else
371		fprintf(stderr, "warning: FIOASYNC not supported by host\n");
372#endif
373	}
374}
375