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