]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * "$Id: listen.c 6123 2006-11-21 15:36:04Z mike $" | |
3 | * | |
4 | * Server listening routines for the Common UNIX Printing System (CUPS) | |
5 | * scheduler. | |
6 | * | |
7 | * Copyright 1997-2006 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 | * cupsdDeleteAllListeners() - Delete all listeners. | |
28 | * cupsdPauseListening() - Clear input polling on all listening sockets... | |
29 | * cupsdResumeListening() - Set input polling on all listening sockets... | |
30 | * cupsdStartListening() - Create all listening sockets... | |
31 | * cupsdStopListening() - Close all listening sockets... | |
32 | */ | |
33 | ||
34 | /* | |
35 | * Include necessary headers... | |
36 | */ | |
37 | ||
38 | #include "cupsd.h" | |
39 | ||
40 | ||
41 | /* | |
42 | * Make sure the IPV6_V6ONLY is defined on Linux - older versions of | |
43 | * glibc don't define it even if the kernel supports it... | |
44 | */ | |
45 | ||
46 | #if defined(__linux) && !defined(IPV6_V6ONLY) | |
47 | # define IPV6_V6ONLY 26 | |
48 | #endif /* __linux && !IPV6_V6ONLY */ | |
49 | ||
50 | ||
51 | /* | |
52 | * 'cupsdDeleteAllListeners()' - Delete all listeners. | |
53 | */ | |
54 | ||
55 | void | |
56 | cupsdDeleteAllListeners(void) | |
57 | { | |
58 | cupsd_listener_t *lis; /* Current listening socket */ | |
59 | ||
60 | ||
61 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); | |
62 | lis; | |
63 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
64 | free(lis); | |
65 | ||
66 | cupsArrayDelete(Listeners); | |
67 | Listeners = NULL; | |
68 | } | |
69 | ||
70 | ||
71 | /* | |
72 | * 'cupsdPauseListening()' - Clear input polling on all listening sockets... | |
73 | */ | |
74 | ||
75 | void | |
76 | cupsdPauseListening(void) | |
77 | { | |
78 | cupsd_listener_t *lis; /* Current listening socket */ | |
79 | ||
80 | ||
81 | if (cupsArrayCount(Listeners) < 1) | |
82 | return; | |
83 | ||
84 | if (cupsArrayCount(Clients) == MaxClients) | |
85 | cupsdLogMessage(CUPSD_LOG_WARN, | |
86 | "Max clients reached, holding new connections..."); | |
87 | ||
88 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdPauseListening: Clearing input bits..."); | |
89 | ||
90 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); | |
91 | lis; | |
92 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
93 | cupsdRemoveSelect(lis->fd); | |
94 | } | |
95 | ||
96 | ||
97 | /* | |
98 | * 'cupsdResumeListening()' - Set input polling on all listening sockets... | |
99 | */ | |
100 | ||
101 | void | |
102 | cupsdResumeListening(void) | |
103 | { | |
104 | cupsd_listener_t *lis; /* Current listening socket */ | |
105 | ||
106 | ||
107 | if (cupsArrayCount(Listeners) < 1) | |
108 | return; | |
109 | ||
110 | if (cupsArrayCount(Clients) >= (MaxClients - 1)) | |
111 | cupsdLogMessage(CUPSD_LOG_WARN, "Resuming new connection processing..."); | |
112 | ||
113 | cupsdLogMessage(CUPSD_LOG_DEBUG2, | |
114 | "cupsdResumeListening: Setting input bits..."); | |
115 | ||
116 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); | |
117 | lis; | |
118 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
119 | cupsdAddSelect(lis->fd, (cupsd_selfunc_t)cupsdAcceptClient, NULL, lis); | |
120 | } | |
121 | ||
122 | ||
123 | /* | |
124 | * 'cupsdStartListening()' - Create all listening sockets... | |
125 | */ | |
126 | ||
127 | void | |
128 | cupsdStartListening(void) | |
129 | { | |
130 | int status; /* Bind result */ | |
131 | int p, /* Port number */ | |
132 | val; /* Parameter value */ | |
133 | cupsd_listener_t *lis; /* Current listening socket */ | |
134 | char s[256]; /* String addresss */ | |
135 | const char *have_domain; /* Have a domain socket? */ | |
136 | static const char * const encryptions[] = | |
137 | { /* Encryption values */ | |
138 | "IfRequested", | |
139 | "Never", | |
140 | "Required", | |
141 | "Always" | |
142 | }; | |
143 | ||
144 | ||
145 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartListening: %d Listeners", | |
146 | cupsArrayCount(Listeners)); | |
147 | ||
148 | /* | |
149 | * Get the server's IP address... | |
150 | */ | |
151 | ||
152 | if (ServerAddrs) | |
153 | httpAddrFreeList(ServerAddrs); | |
154 | ||
155 | if ((ServerAddrs = httpAddrGetList(ServerName, AF_UNSPEC, NULL)) == NULL) | |
156 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
157 | "Unable to find IP address for server name \"%s\"!\n", | |
158 | ServerName); | |
159 | ||
160 | /* | |
161 | * Setup socket listeners... | |
162 | */ | |
163 | ||
164 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners), LocalPort = 0, | |
165 | have_domain = NULL; | |
166 | lis; | |
167 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
168 | { | |
169 | httpAddrString(&(lis->address), s, sizeof(s)); | |
170 | ||
171 | #ifdef AF_INET6 | |
172 | if (lis->address.addr.sa_family == AF_INET6) | |
173 | p = ntohs(lis->address.ipv6.sin6_port); | |
174 | else | |
175 | #endif /* AF_INET6 */ | |
176 | #ifdef AF_LOCAL | |
177 | if (lis->address.addr.sa_family == AF_LOCAL) | |
178 | p = 0; | |
179 | else | |
180 | #endif /* AF_LOCAL */ | |
181 | p = ntohs(lis->address.ipv4.sin_port); | |
182 | ||
183 | /* | |
184 | * If needed, create a socket for listening... | |
185 | */ | |
186 | ||
187 | if (lis->fd == -1) | |
188 | { | |
189 | /* | |
190 | * Create a socket for listening... | |
191 | */ | |
192 | ||
193 | lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0); | |
194 | ||
195 | if (lis->fd == -1) | |
196 | { | |
197 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
198 | "Unable to open listen socket for address %s:%d - %s.", | |
199 | s, p, strerror(errno)); | |
200 | continue; | |
201 | } | |
202 | ||
203 | /* | |
204 | * Set things up to reuse the local address for this port. | |
205 | */ | |
206 | ||
207 | val = 1; | |
208 | #ifdef __sun | |
209 | setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); | |
210 | #else | |
211 | setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); | |
212 | #endif /* __sun */ | |
213 | ||
214 | /* | |
215 | * Bind to the port we found... | |
216 | */ | |
217 | ||
218 | #ifdef AF_INET6 | |
219 | if (lis->address.addr.sa_family == AF_INET6) | |
220 | { | |
221 | # ifdef IPV6_V6ONLY | |
222 | /* | |
223 | * Accept only IPv6 connections on this socket, to avoid | |
224 | * potential security issues and to make all platforms behave | |
225 | * the same. | |
226 | */ | |
227 | ||
228 | val = 1; | |
229 | # ifdef __sun | |
230 | setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val)); | |
231 | # else | |
232 | setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); | |
233 | # endif /* __sun */ | |
234 | # endif /* IPV6_V6ONLY */ | |
235 | ||
236 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), | |
237 | httpAddrLength(&(lis->address))); | |
238 | } | |
239 | else | |
240 | #endif /* AF_INET6 */ | |
241 | #ifdef AF_LOCAL | |
242 | if (lis->address.addr.sa_family == AF_LOCAL) | |
243 | { | |
244 | mode_t mask; /* Umask setting */ | |
245 | ||
246 | ||
247 | /* | |
248 | * Remove any existing domain socket file... | |
249 | */ | |
250 | ||
251 | unlink(lis->address.un.sun_path); | |
252 | ||
253 | /* | |
254 | * Save the curent umask and set it to 0... | |
255 | */ | |
256 | ||
257 | mask = umask(0); | |
258 | ||
259 | /* | |
260 | * Bind the domain socket... | |
261 | */ | |
262 | ||
263 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), | |
264 | httpAddrLength(&(lis->address))); | |
265 | ||
266 | /* | |
267 | * Restore the umask... | |
268 | */ | |
269 | ||
270 | umask(mask); | |
271 | } | |
272 | else | |
273 | #endif /* AF_LOCAL */ | |
274 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), | |
275 | sizeof(lis->address.ipv4)); | |
276 | ||
277 | if (status < 0) | |
278 | { | |
279 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
280 | "Unable to bind socket for address %s:%d - %s.", | |
281 | s, p, strerror(errno)); | |
282 | close(lis->fd); | |
283 | lis->fd = -1; | |
284 | continue; | |
285 | } | |
286 | ||
287 | /* | |
288 | * Listen for new clients. | |
289 | */ | |
290 | ||
291 | if (listen(lis->fd, ListenBackLog) < 0) | |
292 | { | |
293 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
294 | "Unable to listen for clients on address %s:%d - %s.", | |
295 | s, p, strerror(errno)); | |
296 | exit(errno); | |
297 | } | |
298 | } | |
299 | ||
300 | fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); | |
301 | ||
302 | if (p) | |
303 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d on fd %d...", | |
304 | s, p, lis->fd); | |
305 | else | |
306 | { | |
307 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s on fd %d...", | |
308 | s, lis->fd); | |
309 | ||
310 | if (chmod(s, 0140777)) | |
311 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
312 | "Unable to change permisssions on domain socket " | |
313 | "\"%s\" - %s", s, strerror(errno)); | |
314 | } | |
315 | ||
316 | /* | |
317 | * Save the first port that is bound to the local loopback or | |
318 | * "any" address... | |
319 | */ | |
320 | ||
321 | if ((!LocalPort || LocalEncryption == HTTP_ENCRYPT_ALWAYS) && p > 0 && | |
322 | (httpAddrLocalhost(&(lis->address)) || | |
323 | httpAddrAny(&(lis->address)))) | |
324 | { | |
325 | LocalPort = p; | |
326 | LocalEncryption = lis->encryption; | |
327 | } | |
328 | ||
329 | #ifdef AF_LOCAL | |
330 | if (lis->address.addr.sa_family == AF_LOCAL && !have_domain) | |
331 | have_domain = lis->address.un.sun_path; | |
332 | #endif /* AF_LOCAL */ | |
333 | } | |
334 | ||
335 | /* | |
336 | * Make sure that we are listening on localhost! | |
337 | */ | |
338 | ||
339 | if (!LocalPort && !have_domain) | |
340 | { | |
341 | cupsdLogMessage(CUPSD_LOG_EMERG, | |
342 | "No Listen or Port lines were found to allow access via " | |
343 | "localhost!"); | |
344 | ||
345 | /* | |
346 | * Commit suicide... | |
347 | */ | |
348 | ||
349 | cupsdEndProcess(getpid(), 0); | |
350 | } | |
351 | ||
352 | /* | |
353 | * Set the CUPS_SERVER, IPP_PORT, and CUPS_ENCRYPTION variables based on | |
354 | * the listeners... | |
355 | */ | |
356 | ||
357 | if (have_domain) | |
358 | { | |
359 | /* | |
360 | * Use domain sockets for the local connection... | |
361 | */ | |
362 | ||
363 | cupsdSetEnv("CUPS_SERVER", have_domain); | |
364 | ||
365 | LocalEncryption = HTTP_ENCRYPT_IF_REQUESTED; | |
366 | } | |
367 | else | |
368 | { | |
369 | /* | |
370 | * Use the default local loopback address for the server... | |
371 | */ | |
372 | ||
373 | cupsdSetEnv("CUPS_SERVER", "localhost"); | |
374 | } | |
375 | ||
376 | cupsdSetEnv("CUPS_ENCRYPTION", encryptions[LocalEncryption]); | |
377 | ||
378 | if (LocalPort) | |
379 | cupsdSetEnvf("IPP_PORT", "%d", LocalPort); | |
380 | ||
381 | /* | |
382 | * Resume listening for connections... | |
383 | */ | |
384 | ||
385 | cupsdResumeListening(); | |
386 | } | |
387 | ||
388 | ||
389 | /* | |
390 | * 'cupsdStopListening()' - Close all listening sockets... | |
391 | */ | |
392 | ||
393 | void | |
394 | cupsdStopListening(void) | |
395 | { | |
396 | cupsd_listener_t *lis; /* Current listening socket */ | |
397 | ||
398 | ||
399 | cupsdLogMessage(CUPSD_LOG_DEBUG2, | |
400 | "cupsdStopListening: closing all listen sockets."); | |
401 | ||
402 | cupsdPauseListening(); | |
403 | ||
404 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); | |
405 | lis; | |
406 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
407 | { | |
408 | if (lis->fd != -1) | |
409 | { | |
410 | #ifdef WIN32 | |
411 | closesocket(lis->fd); | |
412 | #else | |
413 | close(lis->fd); | |
414 | #endif /* WIN32 */ | |
415 | ||
416 | #ifdef AF_LOCAL | |
417 | /* | |
418 | * Remove domain sockets... | |
419 | */ | |
420 | ||
421 | # ifdef HAVE_LAUNCH_H | |
422 | if (lis->address.addr.sa_family == AF_LOCAL && !Launchd) | |
423 | # else | |
424 | if (lis->address.addr.sa_family == AF_LOCAL) | |
425 | # endif /* HAVE_LAUNCH_H */ | |
426 | unlink(lis->address.un.sun_path); | |
427 | #endif /* AF_LOCAL */ | |
428 | } | |
429 | } | |
430 | } | |
431 | ||
432 | ||
433 | /* | |
434 | * End of "$Id: listen.c 6123 2006-11-21 15:36:04Z mike $". | |
435 | */ |