]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/network.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / network.c
1 /*
2 * "$Id: network.c 5069 2006-02-04 05:24:35Z mike $"
3 *
4 * Network interface functions for the Common UNIX Printing System
5 * (CUPS) 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" 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 * cupsdNetIFFind() - Find a network interface.
28 * cupsdNetIFFree() - Free the current network interface list.
29 * cupsdNetIFUpdate() - Update the network interface list as needed...
30 * compare_netif() - Compare two network interfaces.
31 * getifaddrs() - Get a list of network interfaces on the system.
32 * freeifaddrs() - Free an interface list...
33 */
34
35 /*
36 * Include necessary headers.
37 */
38
39 #include "cupsd.h"
40
41 #include <net/if.h>
42
43
44 /*
45 * Local functions...
46 */
47
48 static void cupsdNetIFFree(void);
49 static int compare_netif(cupsd_netif_t *a, cupsd_netif_t *b);
50
51
52 #ifdef HAVE_GETIFADDRS
53 /*
54 * Use native getifaddrs() function...
55 */
56 # include <ifaddrs.h>
57 #else
58 /*
59 * Use getifaddrs() emulation...
60 */
61
62 # include <sys/ioctl.h>
63 # ifdef HAVE_SYS_SOCKIO_H
64 # include <sys/sockio.h>
65 # endif
66
67 # ifdef ifa_dstaddr
68 # undef ifa_dstaddr
69 # endif /* ifa_dstaddr */
70 # ifndef ifr_netmask
71 # define ifr_netmask ifr_addr
72 # endif /* !ifr_netmask */
73
74 struct ifaddrs /**** Interface Structure ****/
75 {
76 struct ifaddrs *ifa_next; /* Next interface in list */
77 char *ifa_name; /* Name of interface */
78 unsigned int ifa_flags; /* Flags (up, point-to-point, etc.) */
79 struct sockaddr *ifa_addr, /* Network address */
80 *ifa_netmask, /* Address mask */
81 *ifa_dstaddr; /* Broadcast or destination address */
82 void *ifa_data; /* Interface statistics */
83 };
84
85 static int getifaddrs(struct ifaddrs **addrs);
86 static void freeifaddrs(struct ifaddrs *addrs);
87 #endif /* HAVE_GETIFADDRS */
88
89
90 /*
91 * 'cupsdNetIFFind()' - Find a network interface.
92 */
93
94 cupsd_netif_t * /* O - Network interface data */
95 cupsdNetIFFind(const char *name) /* I - Name of interface */
96 {
97 cupsd_netif_t key; /* Search key */
98
99
100 /*
101 * Update the interface list as needed...
102 */
103
104 cupsdNetIFUpdate();
105
106 /*
107 * Search for the named interface...
108 */
109
110 strlcpy(key.name, name, sizeof(key.name));
111
112 return ((cupsd_netif_t *)cupsArrayFind(NetIFList, &key));
113 }
114
115
116 /*
117 * 'cupsdNetIFFree()' - Free the current network interface list.
118 */
119
120 static void
121 cupsdNetIFFree(void)
122 {
123 cupsd_netif_t *current; /* Current interface in array */
124
125
126 /*
127 * Loop through the interface list and free all the records...
128 */
129
130 for (current = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
131 current;
132 current = (cupsd_netif_t *)cupsArrayNext(NetIFList))
133 {
134 cupsArrayRemove(NetIFList, current);
135 free(current);
136 }
137 }
138
139
140 /*
141 * 'cupsdNetIFUpdate()' - Update the network interface list as needed...
142 */
143
144 void
145 cupsdNetIFUpdate(void)
146 {
147 int match; /* Matching address? */
148 cupsd_listener_t *lis; /* Listen address */
149 cupsd_netif_t *temp; /* New interface */
150 struct ifaddrs *addrs, /* Interface address list */
151 *addr; /* Current interface address */
152 http_addrlist_t *saddr; /* Current server address */
153 char hostname[1024]; /* Hostname for address */
154
155
156 /*
157 * Update the network interface list no more often than once a
158 * minute...
159 */
160
161 if ((time(NULL) - NetIFTime) < 60)
162 return;
163
164 NetIFTime = time(NULL);
165
166 /*
167 * Free the old interfaces...
168 */
169
170 cupsdNetIFFree();
171
172 /*
173 * Make sure we have an array...
174 */
175
176 if (!NetIFList)
177 NetIFList = cupsArrayNew((cups_array_func_t)compare_netif, NULL);
178
179 if (!NetIFList)
180 return;
181
182 /*
183 * Grab a new list of interfaces...
184 */
185
186 if (getifaddrs(&addrs) < 0)
187 return;
188
189 for (addr = addrs; addr != NULL; addr = addr->ifa_next)
190 {
191 /*
192 * See if this interface address is IPv4 or IPv6...
193 */
194
195 if (addr->ifa_addr == NULL ||
196 (addr->ifa_addr->sa_family != AF_INET
197 #ifdef AF_INET6
198 && addr->ifa_addr->sa_family != AF_INET6
199 #endif
200 ) ||
201 addr->ifa_netmask == NULL || addr->ifa_name == NULL)
202 continue;
203
204 /*
205 * Try looking up the hostname for the address as needed...
206 */
207
208 if (HostNameLookups)
209 httpAddrLookup((http_addr_t *)(addr->ifa_addr), hostname,
210 sizeof(hostname));
211 else
212 {
213 /*
214 * Map the default server address and localhost to the server name
215 * and localhost, respectively; for all other addresses, use the
216 * dotted notation...
217 */
218
219 if (httpAddrLocalhost((http_addr_t *)(addr->ifa_addr)))
220 strcpy(hostname, "localhost");
221 else
222 {
223 for (saddr = ServerAddrs; saddr; saddr = saddr->next)
224 if (httpAddrEqual((http_addr_t *)(addr->ifa_addr), &(saddr->addr)))
225 break;
226
227 if (saddr)
228 strlcpy(hostname, ServerName, sizeof(hostname));
229 else
230 httpAddrString((http_addr_t *)(addr->ifa_addr), hostname,
231 sizeof(hostname));
232 }
233 }
234
235 /*
236 * Create a new address element...
237 */
238
239 if ((temp = calloc(1, sizeof(cupsd_netif_t) +
240 strlen(hostname))) == NULL)
241 break;
242
243 /*
244 * Copy all of the information...
245 */
246
247 strlcpy(temp->name, addr->ifa_name, sizeof(temp->name));
248 strcpy(temp->hostname, hostname); /* Safe because hostname is allocated */
249
250 if (addr->ifa_addr->sa_family == AF_INET)
251 {
252 /*
253 * Copy IPv4 addresses...
254 */
255
256 memcpy(&(temp->address), addr->ifa_addr, sizeof(struct sockaddr_in));
257 memcpy(&(temp->mask), addr->ifa_netmask, sizeof(struct sockaddr_in));
258
259 if (addr->ifa_dstaddr)
260 memcpy(&(temp->broadcast), addr->ifa_dstaddr,
261 sizeof(struct sockaddr_in));
262 }
263 #ifdef AF_INET6
264 else
265 {
266 /*
267 * Copy IPv6 addresses...
268 */
269
270 memcpy(&(temp->address), addr->ifa_addr, sizeof(struct sockaddr_in6));
271 memcpy(&(temp->mask), addr->ifa_netmask, sizeof(struct sockaddr_in6));
272
273 if (addr->ifa_dstaddr)
274 memcpy(&(temp->broadcast), addr->ifa_dstaddr,
275 sizeof(struct sockaddr_in6));
276 }
277 #endif /* AF_INET6 */
278
279 if (!(addr->ifa_flags & IFF_POINTOPOINT) &&
280 !httpAddrLocalhost(&(temp->address)))
281 temp->is_local = 1;
282
283 /*
284 * Determine which port to use when advertising printers...
285 */
286
287 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
288 lis;
289 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
290 {
291 match = 0;
292
293 if (httpAddrAny(&(lis->address)))
294 match = 1;
295 else if (addr->ifa_addr->sa_family == AF_INET &&
296 lis->address.addr.sa_family == AF_INET &&
297 (lis->address.ipv4.sin_addr.s_addr &
298 temp->mask.ipv4.sin_addr.s_addr) ==
299 temp->address.ipv4.sin_addr.s_addr)
300 match = 1;
301 #ifdef AF_INET6
302 else if (addr->ifa_addr->sa_family == AF_INET6 &&
303 lis->address.addr.sa_family == AF_INET6 &&
304 (lis->address.ipv6.sin6_addr.s6_addr[0] &
305 temp->mask.ipv6.sin6_addr.s6_addr[0]) ==
306 temp->address.ipv6.sin6_addr.s6_addr[0] &&
307 (lis->address.ipv6.sin6_addr.s6_addr[1] &
308 temp->mask.ipv6.sin6_addr.s6_addr[1]) ==
309 temp->address.ipv6.sin6_addr.s6_addr[1] &&
310 (lis->address.ipv6.sin6_addr.s6_addr[2] &
311 temp->mask.ipv6.sin6_addr.s6_addr[2]) ==
312 temp->address.ipv6.sin6_addr.s6_addr[2] &&
313 (lis->address.ipv6.sin6_addr.s6_addr[3] &
314 temp->mask.ipv6.sin6_addr.s6_addr[3]) ==
315 temp->address.ipv6.sin6_addr.s6_addr[3])
316 match = 1;
317 #endif /* AF_INET6 */
318
319 if (match)
320 {
321 if (lis->address.addr.sa_family == AF_INET)
322 temp->port = ntohs(lis->address.ipv4.sin_port);
323 #ifdef AF_INET6
324 else if (lis->address.addr.sa_family == AF_INET6)
325 temp->port = ntohs(lis->address.ipv6.sin6_port);
326 #endif /* AF_INET6 */
327 break;
328 }
329 }
330
331 /*
332 * Add it to the array...
333 */
334
335 cupsArrayAdd(NetIFList, temp);
336
337 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdNetIFUpdate: \"%s\" = %s...",
338 temp->name, temp->hostname);
339 }
340
341 freeifaddrs(addrs);
342 }
343
344
345 /*
346 * 'compare_netif()' - Compare two network interfaces.
347 */
348
349 static int /* O - Result of comparison */
350 compare_netif(cupsd_netif_t *a, /* I - First network interface */
351 cupsd_netif_t *b) /* I - Second network interface */
352 {
353 return (strcmp(a->name, b->name));
354 }
355
356
357 #ifndef HAVE_GETIFADDRS
358 /*
359 * 'getifaddrs()' - Get a list of network interfaces on the system.
360 */
361
362 static int /* O - 0 on success, -1 on error */
363 getifaddrs(struct ifaddrs **addrs) /* O - List of interfaces */
364 {
365 int sock; /* Socket */
366 char buffer[65536], /* Buffer for address info */
367 *bufptr, /* Pointer into buffer */
368 *bufend; /* End of buffer */
369 struct ifconf conf; /* Interface configurations */
370 struct sockaddr addr; /* Address data */
371 struct ifreq *ifp; /* Interface data */
372 int ifpsize; /* Size of interface data */
373 struct ifaddrs *temp; /* Pointer to current interface */
374 struct ifreq request; /* Interface request */
375
376
377 /*
378 * Start with an empty list...
379 */
380
381 if (addrs == NULL)
382 return (-1);
383
384 *addrs = NULL;
385
386 /*
387 * Create a UDP socket to get the interface data...
388 */
389
390 memset (&addr, 0, sizeof(addr));
391 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
392 return (-1);
393
394 /*
395 * Try to get the list of interfaces...
396 */
397
398 conf.ifc_len = sizeof(buffer);
399 conf.ifc_buf = buffer;
400
401 if (ioctl(sock, SIOCGIFCONF, &conf) < 0)
402 {
403 /*
404 * Couldn't get the list of interfaces...
405 */
406
407 close(sock);
408 return (-1);
409 }
410
411 /*
412 * OK, got the list of interfaces, now lets step through the
413 * buffer to pull them out...
414 */
415
416 # ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
417 # define sockaddr_len(a) ((a)->sa_len)
418 # else
419 # define sockaddr_len(a) (sizeof(struct sockaddr))
420 # endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
421
422 for (bufptr = buffer, bufend = buffer + conf.ifc_len;
423 bufptr < bufend;
424 bufptr += ifpsize)
425 {
426 /*
427 * Get the current interface information...
428 */
429
430 ifp = (struct ifreq *)bufptr;
431 ifpsize = sizeof(ifp->ifr_name) + sockaddr_len(&(ifp->ifr_addr));
432
433 if (ifpsize < sizeof(struct ifreq))
434 ifpsize = sizeof(struct ifreq);
435
436 memset(&request, 0, sizeof(request));
437 memcpy(request.ifr_name, ifp->ifr_name, sizeof(ifp->ifr_name));
438
439 /*
440 * Check the status of the interface...
441 */
442
443 if (ioctl(sock, SIOCGIFFLAGS, &request) < 0)
444 continue;
445
446 /*
447 * Allocate memory for a single interface record...
448 */
449
450 if ((temp = calloc(1, sizeof(struct ifaddrs))) == NULL)
451 {
452 /*
453 * Unable to allocate memory...
454 */
455
456 close(sock);
457 return (-1);
458 }
459
460 /*
461 * Add this record to the front of the list and copy the name, flags,
462 * and network address...
463 */
464
465 temp->ifa_next = *addrs;
466 *addrs = temp;
467 temp->ifa_name = strdup(ifp->ifr_name);
468 temp->ifa_flags = request.ifr_flags;
469 if ((temp->ifa_addr = calloc(1, sockaddr_len(&(ifp->ifr_addr)))) != NULL)
470 memcpy(temp->ifa_addr, &(ifp->ifr_addr), sockaddr_len(&(ifp->ifr_addr)));
471
472 /*
473 * Try to get the netmask for the interface...
474 */
475
476 if (!ioctl(sock, SIOCGIFNETMASK, &request))
477 {
478 /*
479 * Got it, make a copy...
480 */
481
482 if ((temp->ifa_netmask = calloc(1, sizeof(request.ifr_netmask))) != NULL)
483 memcpy(temp->ifa_netmask, &(request.ifr_netmask),
484 sizeof(request.ifr_netmask));
485 }
486
487 /*
488 * Then get the broadcast or point-to-point (destination) address,
489 * if applicable...
490 */
491
492 if (temp->ifa_flags & IFF_BROADCAST)
493 {
494 /*
495 * Have a broadcast address, so get it!
496 */
497
498 if (!ioctl(sock, SIOCGIFBRDADDR, &request))
499 {
500 /*
501 * Got it, make a copy...
502 */
503
504 if ((temp->ifa_dstaddr = calloc(1, sizeof(request.ifr_broadaddr))) != NULL)
505 memcpy(temp->ifa_dstaddr, &(request.ifr_broadaddr),
506 sizeof(request.ifr_broadaddr));
507 }
508 }
509 else if (temp->ifa_flags & IFF_POINTOPOINT)
510 {
511 /*
512 * Point-to-point interface; grab the remote address...
513 */
514
515 if (!ioctl(sock, SIOCGIFDSTADDR, &request))
516 {
517 temp->ifa_dstaddr = malloc(sizeof(request.ifr_dstaddr));
518 memcpy(temp->ifa_dstaddr, &(request.ifr_dstaddr),
519 sizeof(request.ifr_dstaddr));
520 }
521 }
522 }
523
524 /*
525 * OK, we're done with the socket, close it and return 0...
526 */
527
528 close(sock);
529
530 return (0);
531 }
532
533
534 /*
535 * 'freeifaddrs()' - Free an interface list...
536 */
537
538 static void
539 freeifaddrs(struct ifaddrs *addrs) /* I - Interface list to free */
540 {
541 struct ifaddrs *next; /* Next interface in list */
542
543
544 while (addrs != NULL)
545 {
546 /*
547 * Make a copy of the next interface pointer...
548 */
549
550 next = addrs->ifa_next;
551
552 /*
553 * Free data values as needed...
554 */
555
556 if (addrs->ifa_name)
557 {
558 free(addrs->ifa_name);
559 addrs->ifa_name = NULL;
560 }
561
562 if (addrs->ifa_addr)
563 {
564 free(addrs->ifa_addr);
565 addrs->ifa_addr = NULL;
566 }
567
568 if (addrs->ifa_netmask)
569 {
570 free(addrs->ifa_netmask);
571 addrs->ifa_netmask = NULL;
572 }
573
574 if (addrs->ifa_dstaddr)
575 {
576 free(addrs->ifa_dstaddr);
577 addrs->ifa_dstaddr = NULL;
578 }
579
580 /*
581 * Free this node and continue to the next...
582 */
583
584 free(addrs);
585
586 addrs = next;
587 }
588 }
589 #endif /* !HAVE_GETIFADDRS */
590
591
592 /*
593 * End of "$Id: network.c 5069 2006-02-04 05:24:35Z mike $".
594 */