]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/listen.c
New IPv6-capable address list stuff for STR #1313. Basically, this
[thirdparty/cups.git] / scheduler / listen.c
1 /*
2 * "$Id$"
3 *
4 * Server listening routines for the Common UNIX Printing System (CUPS)
5 * scheduler.
6 *
7 * Copyright 1997-2005 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Easy Software Products and are protected by Federal
11 * copyright law. Distribution and use rights are outlined in the file
12 * "LICENSE.txt" which should have been included with this file. If this
13 * file is missing or damaged please contact Easy Software Products
14 * at:
15 *
16 * Attn: CUPS Licensing Information
17 * Easy Software Products
18 * 44141 Airport View Drive, Suite 204
19 * Hollywood, Maryland 20636 USA
20 *
21 * Voice: (301) 373-9600
22 * EMail: cups-info@cups.org
23 * WWW: http://www.cups.org
24 *
25 * Contents:
26 *
27 * cupsdPauseListening() - Clear input polling on all listening sockets...
28 * cupsdResumeListening() - Set input polling on all listening sockets...
29 * cupsdStartListening() - Create all listening sockets...
30 * cupsdStopListening() - Close all listening sockets...
31 */
32
33 /*
34 * Include necessary headers...
35 */
36
37 #include "cupsd.h"
38
39
40 /*
41 * 'cupsdPauseListening()' - Clear input polling on all listening sockets...
42 */
43
44 void
45 cupsdPauseListening(void)
46 {
47 int i; /* Looping var */
48 cupsd_listener_t *lis; /* Current listening socket */
49
50
51 if (NumListeners < 1 || !FD_ISSET(Listeners[0].fd, InputSet))
52 return;
53
54 if (NumClients == MaxClients)
55 cupsdLogMessage(CUPSD_LOG_WARN,
56 "Max clients reached, holding new connections...");
57
58 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdPauseListening: Clearing input bits...");
59
60 for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
61 {
62 cupsdLogMessage(CUPSD_LOG_DEBUG2,
63 "cupsdPauseListening: Removing fd %d from InputSet...",
64 lis->fd);
65
66 FD_CLR(lis->fd, InputSet);
67 }
68 }
69
70
71 /*
72 * 'cupsdResumeListening()' - Set input polling on all listening sockets...
73 */
74
75 void
76 cupsdResumeListening(void)
77 {
78 int i; /* Looping var */
79 cupsd_listener_t *lis; /* Current listening socket */
80
81
82 if (NumListeners < 1 || FD_ISSET(Listeners[0].fd, InputSet))
83 return;
84
85 if (NumClients >= (MaxClients - 1))
86 cupsdLogMessage(CUPSD_LOG_WARN, "Resuming new connection processing...");
87
88 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdResumeListening: Setting input bits...");
89
90 for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
91 {
92 cupsdLogMessage(CUPSD_LOG_DEBUG2,
93 "cupsdResumeListening: Adding fd %d to InputSet...",
94 lis->fd);
95 FD_SET(lis->fd, InputSet);
96 }
97 }
98
99
100 /*
101 * 'cupsdStartListening()' - Create all listening sockets...
102 */
103
104 void
105 cupsdStartListening(void)
106 {
107 int status; /* Bind result */
108 int i, /* Looping var */
109 p, /* Port number */
110 val; /* Parameter value */
111 cupsd_listener_t *lis; /* Current listening socket */
112 char s[256]; /* String addresss */
113 const char *have_domain; /* Have a domain socket? */
114 static const char * const encryptions[] =
115 { /* Encryption values */
116 "IfRequested",
117 "Never",
118 "Required",
119 "Always"
120 };
121
122
123 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdStartListening: NumListeners=%d",
124 NumListeners);
125
126 /*
127 * Get the server's IP address...
128 */
129
130 if (ServerAddrs)
131 httpAddrFreeList(ServerAddrs);
132
133 if ((ServerAddrs = httpAddrGetList(ServerName, AF_UNSPEC, NULL)) == NULL)
134 {
135 /*
136 * Didn't find it! Use an address of 0...
137 */
138
139 cupsdLogMessage(CUPSD_LOG_ERROR,
140 "cupsdStartListening: Unable to find IP address for server name \"%s\"!\n",
141 ServerName);
142 }
143
144 /*
145 * Setup socket listeners...
146 */
147
148 for (i = NumListeners, lis = Listeners, LocalPort = 0, have_domain = NULL;
149 i > 0; i --, lis ++)
150 {
151 httpAddrString(&(lis->address), s, sizeof(s));
152
153 #ifdef AF_INET6
154 if (lis->address.addr.sa_family == AF_INET6)
155 p = ntohs(lis->address.ipv6.sin6_port);
156 else
157 #endif /* AF_INET6 */
158 #ifdef AF_LOCAL
159 if (lis->address.addr.sa_family == AF_LOCAL)
160 {
161 have_domain = lis->address.un.sun_path;
162 p = 0;
163 }
164 else
165 #endif /* AF_LOCAL */
166 p = ntohs(lis->address.ipv4.sin_port);
167
168 /*
169 * Save the first port that is bound to the local loopback or
170 * "any" address...
171 */
172
173 if (!LocalPort && p > 0 &&
174 (httpAddrLocalhost(&(lis->address)) ||
175 httpAddrAny(&(lis->address))))
176 {
177 LocalPort = p;
178 LocalEncryption = lis->encryption;
179 }
180
181 /*
182 * Create a socket for listening...
183 */
184
185 lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0);
186
187 #ifdef AF_INET6
188 if (lis->fd == -1 && lis->address.addr.sa_family == AF_INET6 &&
189 (httpAddrLocalhost(&(lis->address)) || httpAddrAny(&(lis->address))))
190 {
191 /*
192 * Try binding to an IPv4 address instead...
193 */
194
195 cupsdLogMessage(CUPSD_LOG_NOTICE,
196 "cupsdStartListening: Unable to use IPv6 address, trying IPv4...");
197
198 p = ntohs(lis->address.ipv6.sin6_port);
199
200 if (httpAddrAny(&(lis->address)))
201 lis->address.ipv4.sin_addr.s_addr = htonl(0x00000000);
202 else
203 lis->address.ipv4.sin_addr.s_addr = htonl(0x7f000001);
204
205 lis->address.ipv4.sin_port = htons(p);
206 lis->address.addr.sa_family = AF_INET;
207
208 lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0);
209 }
210 #endif /* AF_INET6 */
211
212 if (lis->fd == -1)
213 {
214 cupsdLogMessage(CUPSD_LOG_ERROR,
215 "cupsdStartListening: Unable to open listen socket for address %s:%d - %s.",
216 s, p, strerror(errno));
217 exit(errno);
218 }
219
220 fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC);
221
222 /*
223 * Set things up to reuse the local address for this port.
224 */
225
226 val = 1;
227 #ifdef __sun
228 setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
229 #else
230 setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
231 #endif /* __sun */
232
233 /*
234 * Bind to the port we found...
235 */
236
237 #ifdef AF_INET6
238 if (lis->address.addr.sa_family == AF_INET6)
239 {
240 status = bind(lis->fd, (struct sockaddr *)&(lis->address),
241 httpAddrLength(&(lis->address)));
242
243 #ifdef IPV6_V6ONLY
244 if (status >= 0 &&
245 (httpAddrLocalhost(&(lis->address)) || httpAddrAny(&(lis->address))))
246 {
247 /*
248 * Make sure that wildcard and loopback addresses accept
249 * connections from both IPv6 and IPv4 clients.
250 *
251 * NOTE: This DOES NOT WORK for OpenBSD, since they adopted a
252 * stricter behavior in the name of security. For OpenBSD,
253 * you must list IPv4 and IPv6 listen addresses separately.
254 */
255
256 val = 0;
257 # ifdef __sun
258 setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
259 # else
260 setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
261 # endif /* __sun */
262 }
263 #endif /* IPV6_V6ONLY */
264 }
265 else
266 #endif /* AF_INET6 */
267 #ifdef AF_LOCAL
268 if (lis->address.addr.sa_family == AF_LOCAL)
269 {
270 mode_t mask; /* Umask setting */
271
272
273 /*
274 * Remove any existing domain socket file...
275 */
276
277 unlink(lis->address.un.sun_path);
278
279 /*
280 * Save the curent umask and set it to 0...
281 */
282
283 mask = umask(0);
284
285 /*
286 * Bind the domain socket...
287 */
288
289 status = bind(lis->fd, (struct sockaddr *)&(lis->address),
290 httpAddrLength(&(lis->address)));
291
292 /*
293 * Restore the umask...
294 */
295
296 umask(mask);
297 }
298 else
299 #endif /* AF_LOCAL */
300 status = bind(lis->fd, (struct sockaddr *)&(lis->address),
301 sizeof(lis->address.ipv4));
302
303 if (status < 0)
304 {
305 cupsdLogMessage(CUPSD_LOG_ERROR,
306 "cupsdStartListening: Unable to bind socket for address %s:%d - %s.",
307 s, p, strerror(errno));
308 exit(errno);
309 }
310
311 /*
312 * Listen for new clients.
313 */
314
315 if (listen(lis->fd, ListenBackLog) < 0)
316 {
317 cupsdLogMessage(CUPSD_LOG_ERROR,
318 "cupsdStartListening: Unable to listen for clients on address %s:%d - %s.",
319 s, p, strerror(errno));
320 exit(errno);
321 }
322
323 if (p)
324 cupsdLogMessage(CUPSD_LOG_INFO,
325 "cupsdStartListening: Listening to %s:%d on fd %d...",
326 s, p, lis->fd);
327 else
328 cupsdLogMessage(CUPSD_LOG_INFO,
329 "cupsdStartListening: Listening to %s on fd %d...",
330 s, lis->fd);
331 }
332
333 /*
334 * Make sure that we are listening on localhost!
335 */
336
337 if (!LocalPort && !have_domain)
338 {
339 cupsdLogMessage(CUPSD_LOG_EMERG,
340 "No Listen or Port lines were found to allow access via localhost!");
341
342 /*
343 * Commit suicide...
344 */
345
346 cupsdEndProcess(getpid(), 0);
347 }
348
349 /*
350 * Set the CUPS_SERVER and IPP_PORT variables based on the listeners...
351 */
352
353 if (have_domain)
354 {
355 /*
356 * Use domain sockets for the local connection...
357 */
358
359 cupsdSetEnv("CUPS_SERVER", have_domain);
360 }
361 else
362 {
363 /*
364 * Use the default local loopback address for the server...
365 */
366
367 cupsdSetEnv("CUPS_SERVER", "localhost");
368 cupsdSetEnvf("IPP_PORT", "%d", LocalPort);
369 cupsdSetEnv("CUPS_ENCRYPTION", encryptions[LocalEncryption]);
370 }
371
372 /*
373 * Resume listening for connections...
374 */
375
376 cupsdResumeListening();
377 }
378
379
380 /*
381 * 'cupsdStopListening()' - Close all listening sockets...
382 */
383
384 void
385 cupsdStopListening(void)
386 {
387 int i; /* Looping var */
388 cupsd_listener_t *lis; /* Current listening socket */
389
390
391 cupsdLogMessage(CUPSD_LOG_DEBUG,
392 "cupsdStopListening: closing all listen sockets.");
393
394 cupsdPauseListening();
395
396 for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
397 {
398 #ifdef WIN32
399 closesocket(lis->fd);
400 #else
401 close(lis->fd);
402 #endif /* WIN32 */
403
404 #ifdef AF_LOCAL
405 /*
406 * Remove domain sockets...
407 */
408
409 if (lis->address.addr.sa_family == AF_LOCAL)
410 unlink(lis->address.un.sun_path);
411 #endif /* AF_LOCAL */
412 }
413 }
414
415
416 /*
417 * End of "$Id$".
418 */