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