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