#include "cupsd.h"
#include <grp.h>
#ifdef HAVE_DNSSD
# include <dns_sd.h>
# include <nameser.h>
# include <nameser.h>
# include <CoreFoundation/CoreFoundation.h>
# include <SystemConfiguration/SystemConfiguration.h>
# ifdef HAVE_NOTIFY_H
# include <notify.h>
# endif
#endif
# include <CoreFoundation/CoreFoundation.h>
static printer_t* ProcessBrowseData(const char *uri, cups_ptype_t type,
ipp_pstate_t state, const char *location,
const char *info, const char *make_model,
int protocol);
static void SendCUPSBrowse(printer_t *p);
#ifdef HAVE_LIBSLP
static void SendSLPBrowse(printer_t *p);
#endif
#ifdef HAVE_DNSSD
static char dnssdIPPRegType[] = "_ipp._tcp,_cups";
static int dnssdBrowsing = 0;
static SCDynamicStoreRef mSysConfigStore = NULL;
static void dnssdRegisterPrinter(printer_t *p);
static void dnssdDeregisterPrinter(printer_t *p);
static void dnssdRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
DNSServiceErrorType errorCode, const char *name,
const char *regtype, const char *domain, void *context);
static void dnssdBrowseCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
const char *service_name, const char *regtype,
const char *replyDomain, void *context);
static void dnssdResolveCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
const char *fullname, const char *host_target, uint16_t port,
uint16_t txt_len, const char *txtRecord, void *context);
static void dnssdQueryRecordCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
const char *fullname, uint16_t rrtype, uint16_t rrclass,
uint16_t rdlen, const void *rdata, uint32_t ttl, void *context);
static char *dnssdBuildTxtRecord(int *txt_len, printer_t *p);
static char *dnssdPackTxtRecord(int *txt_len, char *keyvalue[][2], int count);
static int dnssdFindAttr(const unsigned char *txtRecord, int txt_len, const char *key, char **value);
#endif
static printer_t *
ProcessBrowseData(const char *uri,
cups_ptype_t type,
ipp_pstate_t state,
const char *location,
const char *info,
const char *make_model,
int protocol)
{
int i;
int update;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
char name[IPP_MAX_NAME],
*hptr,
*sptr;
char local_make_model[IPP_MAX_NAME];
printer_t *p,
*pclass,
*first,
*next;
int offset,
len;
httpSeparate(uri, method, username, host, &port, resource);
if (strncmp(uri, "ipp://", 6) != 0 ||
!host[0] ||
(strncmp(resource, "/printers/", 10) != 0 &&
strncmp(resource, "/classes/", 9) != 0))
{
LogMessage(L_ERROR, "ProcessBrowseData: Bad printer URI in browse data: %s",
uri);
return NULL;
}
if (strchr(resource, '?') != NULL ||
(strncmp(resource, "/printers/", 10) == 0 &&
strchr(resource + 10, '/') != NULL) ||
(strncmp(resource, "/classes/", 9) == 0 &&
strchr(resource + 9, '/') != NULL))
{
LogMessage(L_ERROR, "ProcessBrowseData: Bad resource in browse data: %s",
resource);
return NULL;
}
type |= CUPS_PRINTER_REMOTE;
update = 0;
hptr = strchr(host, '.');
sptr = strchr(ServerName, '.');
if (sptr != NULL && hptr != NULL)
{
while (hptr != NULL)
{
if (strcasecmp(hptr, sptr) == 0)
{
*hptr = '\0';
break;
}
else
hptr = strchr(hptr + 1, '.');
}
}
if (type & CUPS_PRINTER_CLASS)
{
if (strncmp(resource, "/classes/", 9) == 0)
snprintf(name, sizeof(name), "%s@%s", resource + 9, host);
else
return NULL;
if ((p = FindClass(name)) == NULL && BrowseShortNames)
{
if ((p = FindClass(resource + 9)) != NULL)
{
if (p->hostname && strcasecmp(p->hostname, host) != 0)
{
if (p->type & CUPS_PRINTER_REMOTE)
{
SetStringf(&p->name, "%s@%s", p->name, p->hostname);
SetPrinterAttrs(p);
SortPrinters();
}
p = NULL;
}
else if (!p->hostname)
{
SetString(&p->hostname, host);
SetString(&p->uri, uri);
SetString(&p->device_uri, uri);
update = 1;
}
}
else
strlcpy(name, resource + 9, sizeof(name));
}
else if (p != NULL && !p->hostname)
{
SetString(&p->hostname, host);
SetString(&p->uri, uri);
SetString(&p->device_uri, uri);
update = 1;
}
if (p == NULL && !(type & CUPS_PRINTER_DELETE))
{
p = AddClass(name, 1);
LogMessage(L_INFO, "Added remote class \"%s\"...", name);
p->type = type & ~CUPS_PRINTER_REJECTING;
p->accepting = 1;
SetString(&p->uri, uri);
SetString(&p->device_uri, uri);
SetString(&p->hostname, host);
update = 1;
}
}
else
{
if (strncmp(resource, "/printers/", 10) == 0)
snprintf(name, sizeof(name), "%s@%s", resource + 10, host);
else
return NULL;
if ((p = FindPrinter(name)) == NULL && BrowseShortNames)
{
if ((p = FindPrinter(resource + 10)) != NULL)
{
if (p->hostname && strcasecmp(p->hostname, host) != 0)
{
if (p->type & CUPS_PRINTER_REMOTE)
{
SetStringf(&p->name, "%s@%s", p->name, p->hostname);
SetPrinterAttrs(p);
SortPrinters();
}
p = NULL;
}
else if (!p->hostname)
{
SetString(&p->hostname, host);
SetString(&p->uri, uri);
SetString(&p->device_uri, uri);
update = 1;
}
}
else
strlcpy(name, resource + 10, sizeof(name));
}
else if (p != NULL && !p->hostname)
{
SetString(&p->hostname, host);
SetString(&p->uri, uri);
SetString(&p->device_uri, uri);
update = 1;
}
if (p == NULL && !(type & CUPS_PRINTER_DELETE))
{
p = AddPrinter(name, 1);
LogMessage(L_INFO, "Added remote printer \"%s\"...", name);
p->type = type & ~CUPS_PRINTER_REJECTING;
p->accepting = 1;
SetString(&p->hostname, host);
SetString(&p->uri, uri);
SetString(&p->device_uri, uri);
update = 1;
}
}
if (p == NULL)
return NULL;
if (p->state != state)
update = 1;
p->state = state;
p->browse_time = time(NULL);
p->browse_protocol |= protocol;
if (type & CUPS_PRINTER_REJECTING)
{
type &= ~CUPS_PRINTER_REJECTING;
if (p->accepting)
{
update = 1;
p->accepting = 0;
}
}
else if (!p->accepting)
{
update = 1;
p->accepting = 1;
}
if (p->type != type)
{
p->type = type;
update = 1;
}
if (location && (!p->location || strcmp(p->location, location)))
{
SetString(&p->location, location);
update = 1;
}
if (info && (!p->info || strcmp(p->info, info)))
{
SetString(&p->info, info);
update = 1;
}
if (!make_model || !make_model[0])
{
if (type & CUPS_PRINTER_CLASS)
snprintf(local_make_model, sizeof(local_make_model),
"Remote Class on %s", host);
else
snprintf(local_make_model, sizeof(local_make_model),
"Remote Printer on %s", host);
}
else
#ifdef __APPLE__
strlcpy(local_make_model, make_model, sizeof(local_make_model));
#else
snprintf(local_make_model, sizeof(local_make_model),
"%s on %s", make_model, host);
#endif
if (!p->make_model || strcmp(p->make_model, local_make_model))
{
SetString(&p->make_model, local_make_model);
update = 1;
}
if (type & CUPS_PRINTER_DELETE)
{
DeletePrinter(p, 1);
UpdateImplicitClasses();
}
else if (update)
{
SetPrinterAttrs(p);
UpdateImplicitClasses();
}
if (DefaultPrinter == NULL && Printers != NULL)
{
#ifdef __APPLE__
for (p = Printers; p != NULL; p = p->next)
if (!(p->type & CUPS_PRINTER_REMOTE))
{
DefaultPrinter = p;
WritePrintcap();
break;
}
#else
DefaultPrinter = Printers;
WritePrintcap();
#endif
}
if (ImplicitClasses)
{
for (p = Printers, len = 0, offset = 0, first = NULL;
p != NULL;
p = next)
{
next = p->next;
if (p->type & CUPS_PRINTER_IMPLICIT)
{
len = 0;
continue;
}
if (len > 0 &&
strncasecmp(p->name, name + offset, len) == 0 &&
(p->name[len] == '\0' || p->name[len] == '@'))
{
if ((pclass = FindDest(name)) == NULL)
{
pclass = AddPrinter(name, 1);
pclass->type |= CUPS_PRINTER_IMPLICIT;
pclass->accepting = 1;
pclass->state = IPP_PRINTER_IDLE;
SetString(&pclass->location, p->location);
SetString(&pclass->info, p->info);
SetPrinterAttrs(pclass);
LogMessage(L_INFO, "Added implicit class \"%s\"...", name);
}
if (first != NULL)
{
for (i = 0; i < pclass->num_printers; i ++)
if (pclass->printers[i] == first)
break;
if (i >= pclass->num_printers)
AddPrinterToClass(pclass, first);
first = NULL;
}
for (i = 0; i < pclass->num_printers; i ++)
if (pclass->printers[i] == p)
break;
if (i >= pclass->num_printers)
AddPrinterToClass(pclass, p);
}
else
{
if ((hptr = strchr(p->name, '@')) != NULL)
len = hptr - p->name;
else
len = strlen(p->name);
strncpy(name, p->name, len);
name[len] = '\0';
offset = 0;
if ((pclass = FindDest(name)) != NULL &&
!(pclass->type & CUPS_PRINTER_IMPLICIT))
{
if (ImplicitAnyClasses && len < (sizeof(name) - 4))
{
strcpy(name, "Any");
strncpy(name + 3, p->name, len);
name[len + 3] = '\0';
offset = 3;
}
else
{
len = 0;
continue;
}
}
first = p;
}
}
}
return p;
}
void
SendBrowseList(void)
{
int count,
num_printers;
printer_t *p,
*next;
time_t ut,
to;
if (!Browsing || !BrowseLocalProtocols)
return;
ut = time(NULL) - BrowseInterval;
to = time(NULL) - BrowseTimeout;
if (BrowseInterval > 0)
{
int max_count;
max_count = 2 * NumPrinters / BrowseInterval + 1;
for (count = 0, p = Printers; count < max_count && p != NULL; p = p->next)
if (!(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
p->browse_time < ut && p->shared)
count ++;
for (p = BrowseNext; count > 0; p = p->next)
{
if (!p)
p = Printers;
if (!p)
break;
else if (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT))
continue;
else if (p->browse_time < ut && p->shared)
{
count --;
p->browse_time = time(NULL);
if (BrowseLocalProtocols & BROWSE_CUPS)
SendCUPSBrowse(p);
#ifdef HAVE_LIBSLP
if (BrowseLocalProtocols & BROWSE_SLP)
SendSLPBrowse(p);
#endif
}
}
BrowseNext = p;
}
for (p = Printers; p != NULL; p = next)
{
next = p->next;
if (p->type & CUPS_PRINTER_REMOTE)
{
if (p->browse_time < to && p->browse_protocol == BROWSE_CUPS)
{
LogMessage(L_INFO, "Remote destination \"%s\" has timed out; deleting it...",
p->name);
num_printers = NumPrinters;
DeletePrinter(p, 1);
if (NumPrinters != num_printers - 1)
next = Printers;
}
}
}
}
static void
SendCUPSBrowse(printer_t *p)
{
int i;
cups_ptype_t type;
dirsvc_addr_t *b;
int bytes;
char packet[1453];
cups_netif_t *iface;
char *make_model,
*location,
*info,
*ptr;
type = p->type | CUPS_PRINTER_REMOTE;
if (!p->accepting)
type |= CUPS_PRINTER_REJECTING;
make_model = p->make_model;
if (make_model && strchr(make_model, '\"'))
if ((make_model = strdup(make_model)))
while ((ptr = strchr(make_model, '\"')))
*ptr = '\'';
location = p->location;
if (location && strchr(location, '\"'))
if ((location = strdup(location)))
while ((ptr = strchr(location, '\"')))
*ptr = '\'';
info = p->info;
if (info && strchr(info, '\"'))
if ((info = strdup(info)))
while ((ptr = strchr(info, '\"')))
*ptr = '\'';
for (i = NumBrowsers, b = Browsers; i > 0; i --, b ++)
if (b->iface[0])
{
if (strcmp(b->iface, "*") == 0)
{
NetIFUpdate(FALSE);
for (iface = NetIFList; iface != NULL; iface = iface->next)
{
if (!iface->is_local || !iface->port)
continue;
snprintf(packet, sizeof(packet), "%x %x ipp://%s:%d/%s/%s \"%s\" \"%s\" \"%s\"\n",
type, p->state, iface->hostname, iface->port,
(p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers",
p->name, location ? location : "",
info ? info : "",
make_model ? make_model : "Unknown");
bytes = strlen(packet);
LogMessage(L_DEBUG2, "SendBrowseList: (%d bytes to \"%s\") %s", bytes,
iface->name, packet);
iface->broadcast.sin_port = htons(BrowsePort);
sendto(BrowseSocket, packet, bytes, 0,
(struct sockaddr *)&(iface->broadcast),
sizeof(struct sockaddr_in));
}
}
else if ((iface = NetIFFind(b->iface)) != NULL)
{
if (!iface->port)
continue;
snprintf(packet, sizeof(packet), "%x %x ipp://%s:%d/%s/%s \"%s\" \"%s\" \"%s\"\n",
type, p->state, iface->hostname, iface->port,
(p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers",
p->name, location ? location : "",
info ? info : "",
make_model ? make_model : "Unknown");
bytes = strlen(packet);
LogMessage(L_DEBUG2, "SendBrowseList: (%d bytes to \"%s\") %s", bytes,
iface->name, packet);
iface->broadcast.sin_port = htons(BrowsePort);
sendto(BrowseSocket, packet, bytes, 0,
(struct sockaddr *)&(iface->broadcast),
sizeof(struct sockaddr_in));
}
}
else
{
snprintf(packet, sizeof(packet), "%x %x %s \"%s\" \"%s\" \"%s\"\n",
type, p->state, p->uri,
location ? location : "",
info ? info : "",
make_model ? make_model : "Unknown");
bytes = strlen(packet);
LogMessage(L_DEBUG2, "SendBrowseList: (%d bytes to %x) %s", bytes,
(unsigned)ntohl(b->to.sin_addr.s_addr), packet);
if (sendto(BrowseSocket, packet, bytes, 0,
(struct sockaddr *)&(b->to), sizeof(struct sockaddr_in)) <= 0)
{
LogMessage(L_ERROR, "SendBrowseList: sendto failed for browser %d - %s.",
b - Browsers + 1, strerror(errno));
if (i > 1)
memmove(b, b + 1, (i - 1) * sizeof(dirsvc_addr_t));
b --;
NumBrowsers --;
}
}
if (make_model != p->make_model)
free(make_model);
if (location != p->location)
free(location);
if (info != p->info)
free(info);
}
void
StartBrowsing(void)
{
int val;
struct sockaddr_in addr;
printer_t *p;
if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
return;
if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
{
if ((BrowseSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
LogMessage(L_ERROR, "StartBrowsing: Unable to create broadcast socket - %s.",
strerror(errno));
BrowseLocalProtocols &= ~BROWSE_CUPS;
BrowseRemoteProtocols &= ~BROWSE_CUPS;
return;
}
val = 1;
if (setsockopt(BrowseSocket, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
{
LogMessage(L_ERROR, "StartBrowsing: Unable to set broadcast mode - %s.",
strerror(errno));
#ifdef WIN32
closesocket(BrowseSocket);
#else
close(BrowseSocket);
#endif
BrowseSocket = -1;
BrowseLocalProtocols &= ~BROWSE_CUPS;
BrowseRemoteProtocols &= ~BROWSE_CUPS;
return;
}
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_family = AF_INET;
addr.sin_port = htons(BrowsePort);
if (bind(BrowseSocket, (struct sockaddr *)&addr, sizeof(addr)))
{
LogMessage(L_ERROR, "StartBrowsing: Unable to bind broadcast socket - %s.",
strerror(errno));
#ifdef WIN32
closesocket(BrowseSocket);
#else
close(BrowseSocket);
#endif
BrowseSocket = -1;
BrowseLocalProtocols &= ~BROWSE_CUPS;
BrowseRemoteProtocols &= ~BROWSE_CUPS;
return;
}
fcntl(BrowseSocket, F_SETFD, fcntl(BrowseSocket, F_GETFD) | FD_CLOEXEC);
LogMessage(L_DEBUG2, "StartBrowsing: Adding fd %d to InputSet...",
BrowseSocket);
FD_SET(BrowseSocket, InputSet);
}
else
BrowseSocket = -1;
#ifdef HAVE_LIBSLP
if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
{
if (SLPOpen("en", SLP_FALSE, &BrowseSLPHandle) != SLP_OK)
{
LogMessage(L_ERROR, "Unable to open an SLP handle; disabling SLP browsing!");
BrowseLocalProtocols &= ~BROWSE_SLP;
BrowseRemoteProtocols &= ~BROWSE_SLP;
}
BrowseSLPRefresh = 0;
}
#endif
#ifdef HAVE_DNSSD
if (BrowseRemoteProtocols & BROWSE_DNSSD)
{
DNSServiceErrorType se;
dnssdBrowsing = 1;
if ((se = DNSServiceBrowse(&BrowseDNSSDRef, 0, 0, dnssdIPPRegType,
NULL, dnssdBrowseCallback, NULL)) == 0)
{
BrowseDNSSDfd = DNSServiceRefSockFD(BrowseDNSSDRef);
LogMessage(L_DEBUG2, "StartBrowsing: Adding fd %d to InputSet...",
BrowseDNSSDfd);
FD_SET(BrowseDNSSDfd, InputSet);
}
}
#endif
for (p = Printers; p != NULL; p = p->next)
BrowseRegisterPrinter(p);
}
void
StartPolling(void)
{
int i;
dirsvc_poll_t *poll;
int pid;
char sport[10];
char bport[10];
char interval[10];
int statusfds[2];
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action;
#endif
if (NumPolled == 0)
{
PollPipe = -1;
return;
}
sprintf(bport, "%d", BrowsePort);
if (BrowseInterval)
sprintf(interval, "%d", BrowseInterval);
else
strcpy(interval, "30");
if (cupsdOpenPipe(statusfds))
{
LogMessage(L_ERROR, "Unable to create polling status pipes - %s.",
strerror(errno));
PollPipe = -1;
return;
}
PollPipe = statusfds[0];
for (i = 0, poll = Polled; i < NumPolled; i ++, poll ++)
{
sprintf(sport, "%d", poll->port);
HoldSignals();
if ((pid = fork()) == 0)
{
if (getuid() == 0)
{
if (setgid(Group))
exit(errno);
if (setgroups(1, &Group))
exit(errno);
if (setuid(User))
exit(errno);
}
else
{
setgroups(1, &Group);
}
close(0);
open("/dev/null", O_RDONLY);
close(1);
open("/dev/null", O_WRONLY);
close(2);
dup(statusfds[1]);
#ifdef HAVE_SIGSET
sigset(SIGTERM, SIG_DFL);
sigset(SIGCHLD, SIG_DFL);
#elif defined(HAVE_SIGACTION)
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_DFL;
sigaction(SIGTERM, &action, NULL);
sigaction(SIGCHLD, &action, NULL);
#else
signal(SIGTERM, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
#endif
ReleaseSignals();
execl(CUPS_SERVERBIN "/daemon/cups-polld", "cups-polld", poll->hostname,
sport, interval, bport, NULL);
exit(errno);
}
else if (pid < 0)
{
LogMessage(L_ERROR, "StartPolling: Unable to fork polling daemon - %s",
strerror(errno));
poll->pid = 0;
break;
}
else
{
poll->pid = pid;
LogMessage(L_DEBUG, "StartPolling: Started polling daemon for %s:%d, pid = %d",
poll->hostname, poll->port, pid);
}
ReleaseSignals();
}
close(statusfds[1]);
LogMessage(L_DEBUG2, "StartPolling: Adding fd %d to InputSet...",
PollPipe);
FD_SET(PollPipe, InputSet);
}
void
StopBrowsing(void)
{
printer_t *p;
if (!Browsing || !(BrowseLocalProtocols | BrowseRemoteProtocols))
return;
if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_CUPS)
{
if (BrowseSocket >= 0)
{
#ifdef WIN32
closesocket(BrowseSocket);
#else
close(BrowseSocket);
#endif
LogMessage(L_DEBUG2, "StopBrowsing: Removing fd %d from InputSet...",
BrowseSocket);
FD_CLR(BrowseSocket, InputFds);
FD_CLR(BrowseSocket, InputSet);
BrowseSocket = -1;
}
}
#ifdef HAVE_LIBSLP
if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP)
{
SLPClose(BrowseSLPHandle);
}
#endif
#ifdef HAVE_DNSSD
if ((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_DNSSD)
{
dnssdBrowsing = 0;
if (BrowseDNSSDRef)
{
LogMessage(L_DEBUG2, "StopBrowsing: Removing fd %d from InputSet...",
BrowseDNSSDfd);
FD_CLR(BrowseDNSSDfd, InputFds);
FD_CLR(BrowseDNSSDfd, InputSet);
DNSServiceRefDeallocate(BrowseDNSSDRef);
BrowseDNSSDRef = NULL;
BrowseDNSSDfd = -1;
}
}
for (p = Printers; p != NULL; p = p->next)
BrowseDeregisterPrinter(p, 1);
#endif
}
void
StopPolling(void)
{
int i;
dirsvc_poll_t *poll;
if (PollPipe >= 0)
{
close(PollPipe);
LogMessage(L_DEBUG2, "StopPolling: removing fd %d from InputSet.",
PollPipe);
FD_CLR(PollPipe, InputFds);
FD_CLR(PollPipe, InputSet);
PollPipe = -1;
}
for (i = 0, poll = Polled; i < NumPolled; i ++, poll ++)
if (poll->pid)
kill(poll->pid, SIGTERM);
}
void
UpdateCUPSBrowse(void)
{
int i;
int auth;
int len;
int bytes;
char packet[1541],
*pptr;
struct sockaddr_in srcaddr;
char srcname[1024];
unsigned address;
struct hostent *srchost;
unsigned type;
unsigned state;
char uri[HTTP_MAX_URI],
method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI],
info[IPP_MAX_NAME],
location[IPP_MAX_NAME],
make_model[IPP_MAX_NAME];
int port;
cups_netif_t *iface;
len = sizeof(srcaddr);
if ((bytes = recvfrom(BrowseSocket, packet, sizeof(packet) - 1, 0,
(struct sockaddr *)&srcaddr, &len)) < 0)
{
if (errno != ECONNREFUSED && errno != EAGAIN)
{
LogMessage(L_ERROR, "Browse recv failed - %s.", strerror(errno));
LogMessage(L_ERROR, "Browsing turned off.");
StopBrowsing();
Browsing = 0;
}
return;
}
packet[bytes] = '\0';
#ifdef __APPLE__
if (Sleeping)
return;
#endif
address = ntohl(srcaddr.sin_addr.s_addr);
if (HostNameLookups)
#ifndef __sgi
srchost = gethostbyaddr((char *)&(srcaddr.sin_addr), sizeof(struct in_addr),
AF_INET);
#else
srchost = gethostbyaddr(&(srcaddr.sin_addr), sizeof(struct in_addr),
AF_INET);
#endif
else
srchost = NULL;
if (srchost == NULL)
sprintf(srcname, "%d.%d.%d.%d", address >> 24, (address >> 16) & 255,
(address >> 8) & 255, address & 255);
else
strlcpy(srcname, srchost->h_name, sizeof(srcname));
len = strlen(srcname);
if (BrowseACL && (BrowseACL->num_allow || BrowseACL->num_deny))
{
if (address == 0x7f000001 || strcasecmp(srcname, "localhost") == 0)
{
auth = AUTH_ALLOW;
}
else
{
switch (BrowseACL->order_type)
{
default :
auth = AUTH_DENY;
break;
case AUTH_ALLOW :
auth = AUTH_ALLOW;
if (CheckAuth(address, srcname, len,
BrowseACL->num_deny, BrowseACL->deny))
auth = AUTH_DENY;
if (CheckAuth(address, srcname, len,
BrowseACL->num_allow, BrowseACL->allow))
auth = AUTH_ALLOW;
break;
case AUTH_DENY :
auth = AUTH_DENY;
if (CheckAuth(address, srcname, len,
BrowseACL->num_allow, BrowseACL->allow))
auth = AUTH_ALLOW;
if (CheckAuth(address, srcname, len,
BrowseACL->num_deny, BrowseACL->deny))
auth = AUTH_DENY;
break;
}
}
}
else
auth = AUTH_ALLOW;
if (auth == AUTH_DENY)
{
LogMessage(L_DEBUG, "UpdateCUPSBrowse: Refused %d bytes from %s", bytes,
srcname);
return;
}
LogMessage(L_DEBUG2, "UpdateCUPSBrowse: (%d bytes from %s) %s", bytes, srcname,
packet);
if (sscanf(packet, "%x%x%1023s", &type, &state, uri) < 3)
{
LogMessage(L_WARN, "UpdateCUPSBrowse: Garbled browse packet - %s",
packet);
return;
}
strcpy(location, "Location Unknown");
strcpy(info, "No Information Available");
make_model[0] = '\0';
if ((pptr = strchr(packet, '\"')) != NULL)
{
for (i = 0, pptr ++;
i < (sizeof(location) - 1) && *pptr && *pptr != '\"';
i ++, pptr ++)
location[i] = *pptr;
if (i)
location[i] = '\0';
if (*pptr == '\"')
pptr ++;
while (*pptr && isspace(*pptr & 255))
pptr ++;
if (*pptr == '\"')
{
for (i = 0, pptr ++;
i < (sizeof(info) - 1) && *pptr && *pptr != '\"';
i ++, pptr ++)
info[i] = *pptr;
if (i)
info[i] = '\0';
if (*pptr == '\"')
pptr ++;
while (*pptr && isspace(*pptr & 255))
pptr ++;
if (*pptr == '\"')
{
for (i = 0, pptr ++;
i < (sizeof(make_model) - 1) && *pptr && *pptr != '\"';
i ++, pptr ++)
make_model[i] = *pptr;
if (i)
make_model[i] = '\0';
}
}
}
DEBUG_puts(packet);
DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n"
"location=\"%s\", info=\"%s\", make_model=\"%s\"\n",
type, state, uri, location, info, make_model));
httpSeparate(uri, method, username, host, &port, resource);
DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host, ServerName));
if (strcasecmp(host, ServerName) == 0)
return;
NetIFUpdate(FALSE);
for (iface = NetIFList; iface != NULL; iface = iface->next)
if (strcasecmp(host, iface->hostname) == 0)
return;
for (i = 0; i < NumRelays; i ++)
if (CheckAuth(address, srcname, len, 1, &(Relays[i].from)))
if (sendto(BrowseSocket, packet, bytes, 0,
(struct sockaddr *)&(Relays[i].to),
sizeof(struct sockaddr_in)) <= 0)
{
LogMessage(L_ERROR, "UpdateCUPSBrowse: sendto failed for relay %d - %s.",
i + 1, strerror(errno));
return;
}
ProcessBrowseData(uri, type, state, location, info, make_model, BROWSE_CUPS);
}
void
UpdatePolling(void)
{
int bytes;
char *lineptr;
static int bufused = 0;
static char buffer[1024];
if ((bytes = read(PollPipe, buffer + bufused,
sizeof(buffer) - bufused - 1)) > 0)
{
bufused += bytes;
buffer[bufused] = '\0';
lineptr = strchr(buffer, '\n');
}
else if (bytes < 0 && errno == EINTR)
return;
else
{
lineptr = buffer + bufused;
lineptr[1] = 0;
}
if (bytes == 0 && bufused == 0)
lineptr = NULL;
while (lineptr != NULL)
{
*lineptr++ = '\0';
if (!strncmp(buffer, "ERROR: ", 7))
LogMessage(L_ERROR, "%s", buffer + 7);
else if (!strncmp(buffer, "DEBUG: ", 7))
LogMessage(L_DEBUG, "%s", buffer + 7);
else if (!strncmp(buffer, "DEBUG2: ", 8))
LogMessage(L_DEBUG2, "%s", buffer + 8);
else
LogMessage(L_DEBUG, "%s", buffer);
cups_strcpy(buffer, lineptr);
bufused -= lineptr - buffer;
if (bufused < 0)
bufused = 0;
lineptr = strchr(buffer, '\n');
}
if (bytes <= 0)
{
LogMessage(L_ERROR, "UpdatePolling: all polling processes have exited!");
StopPolling();
}
}
#ifdef HAVE_LIBSLP
# define SLP_CUPS_SRVTYPE "service:printer"
# define SLP_CUPS_SRVLEN 15
typedef struct _slpsrvurl
{
struct _slpsrvurl *next;
char url[HTTP_MAX_URI];
} slpsrvurl_t;
void
RegReportCallback(SLPHandle hslp,
SLPError errcode,
void *cookie)
{
(void)hslp;
(void)errcode;
(void)cookie;
return;
}
static void
SendSLPBrowse(printer_t *p)
{
char srvurl[HTTP_MAX_URI],
attrs[8192],
finishings[1024],
make_model[IPP_MAX_NAME * 2],
location[IPP_MAX_NAME * 2],
info[IPP_MAX_NAME * 2],
*src,
*dst;
ipp_attribute_t *authentication;
SLPError error;
LogMessage(L_DEBUG, "SendSLPBrowse(%p = \"%s\")", p, p->name);
snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
LogMessage(L_DEBUG2, "Service URL = \"%s\"", srvurl);
if (p->type & CUPS_PRINTER_STAPLE)
strcpy(finishings, "staple");
else
finishings[0] = '\0';
if (p->type & CUPS_PRINTER_BIND)
{
if (finishings[0])
strlcat(finishings, ",bind", sizeof(finishings));
else
strcpy(finishings, "bind");
}
if (p->type & CUPS_PRINTER_PUNCH)
{
if (finishings[0])
strlcat(finishings, ",punch", sizeof(finishings));
else
strcpy(finishings, "punch");
}
if (p->type & CUPS_PRINTER_COVER)
{
if (finishings[0])
strlcat(finishings, ",cover", sizeof(finishings));
else
strcpy(finishings, "cover");
}
if (p->type & CUPS_PRINTER_SORT)
{
if (finishings[0])
strlcat(finishings, ",sort", sizeof(finishings));
else
strcpy(finishings, "sort");
}
if (!finishings[0])
strcpy(finishings, "none");
for (src = p->make_model, dst = make_model;
src && *src && dst < (make_model + sizeof(make_model) - 2);)
{
if (*src == ',' || *src == '\\' || *src == ')')
*dst++ = '\\';
*dst++ = *src++;
}
*dst = '\0';
if (!make_model[0])
strcpy(make_model, "Unknown");
for (src = p->location, dst = location;
src && *src && dst < (location + sizeof(location) - 2);)
{
if (*src == ',' || *src == '\\' || *src == ')')
*dst++ = '\\';
*dst++ = *src++;
}
*dst = '\0';
if (!location[0])
strcpy(location, "Unknown");
for (src = p->info, dst = info;
src && *src && dst < (info + sizeof(info) - 2);)
{
if (*src == ',' || *src == '\\' || *src == ')')
*dst++ = '\\';
*dst++ = *src++;
}
*dst = '\0';
if (!info[0])
strcpy(info, "Unknown");
authentication = ippFindAttribute(p->attrs, "uri-authentication-supported",
IPP_TAG_KEYWORD);
snprintf(attrs, sizeof(attrs),
"(printer-uri-supported=%s),"
"(uri-authentication-supported=%s>),"
#ifdef HAVE_SSL
"(uri-security-supported=tls>),"
#else
"(uri-security-supported=none>),"
#endif
"(printer-name=%s),"
"(printer-location=%s),"
"(printer-info=%s),"
"(printer-more-info=%s),"
"(printer-make-and-model=%s),"
"(charset-supported=utf-8),"
"(natural-language-configured=%s),"
"(natural-language-supported=de,en,es,fr,it),"
"(color-supported=%s),"
"(finishings-supported=%s),"
"(sides-supported=one-sided%s),"
"(multiple-document-jobs-supported=true)"
"(ipp-versions-supported=1.0,1.1)",
p->uri, authentication->values[0].string.text, p->name, location,
info, p->uri, make_model, DefaultLanguage,
p->type & CUPS_PRINTER_COLOR ? "true" : "false",
finishings,
p->type & CUPS_PRINTER_DUPLEX ?
",two-sided-long-edge,two-sided-short-edge" : "");
LogMessage(L_DEBUG2, "Attributes = \"%s\"", attrs);
error = SLPReg(BrowseSLPHandle, srvurl, BrowseTimeout,
SLP_CUPS_SRVTYPE, attrs, SLP_TRUE, RegReportCallback, 0);
if (error != SLP_OK)
LogMessage(L_ERROR, "SLPReg of \"%s\" failed with status %d!", p->name,
error);
}
void
SLPDeregPrinter(printer_t *p)
{
char srvurl[HTTP_MAX_URI];
if((p->type & CUPS_PRINTER_REMOTE) == 0)
{
snprintf(srvurl, sizeof(srvurl), SLP_CUPS_SRVTYPE ":%s", p->uri);
SLPDereg(BrowseSLPHandle, srvurl, RegReportCallback, 0);
}
}
int
GetSlpAttrVal(const char *attrlist,
const char *tag,
char **valbuf)
{
char *ptr1,
*ptr2;
ClearString(valbuf);
if ((ptr1 = strstr(attrlist, tag)) != NULL)
{
ptr1 += strlen(tag);
if ((ptr2 = strchr(ptr1,')')) != NULL)
{
*valbuf = calloc(ptr2 - ptr1 + 1, 1);
strncpy(*valbuf, ptr1, ptr2 - ptr1);
for (ptr1 = *valbuf; *ptr1; ptr1 ++)
if (*ptr1 == '\\' && ptr1[1])
cups_strcpy(ptr1, ptr1 + 1);
return (0);
}
}
return (-1);
}
SLPBoolean
AttrCallback(SLPHandle hslp,
const char *attrlist,
SLPError errcode,
void *cookie)
{
char *tmp = 0;
printer_t *p = (printer_t*)cookie;
(void)hslp;
if (errcode != SLP_OK)
return (SLP_TRUE);
memset(p, 0, sizeof(printer_t));
p->type = CUPS_PRINTER_REMOTE;
if (GetSlpAttrVal(attrlist, "(printer-location=", &(p->location)))
return (SLP_FALSE);
if (GetSlpAttrVal(attrlist, "(printer-info=", &(p->info)))
return (SLP_FALSE);
if (GetSlpAttrVal(attrlist, "(printer-make-and-model=", &(p->make_model)))
return (SLP_FALSE);
if (GetSlpAttrVal(attrlist, "(color-supported=", &tmp))
return (SLP_FALSE);
if (strcasecmp(tmp, "true") == 0)
p->type |= CUPS_PRINTER_COLOR;
if (GetSlpAttrVal(attrlist, "(finishings-supported=", &tmp))
return (SLP_FALSE);
if (strstr(tmp, "staple"))
p->type |= CUPS_PRINTER_STAPLE;
if (strstr(tmp, "bind"))
p->type |= CUPS_PRINTER_BIND;
if (strstr(tmp, "punch"))
p->type |= CUPS_PRINTER_PUNCH;
if (GetSlpAttrVal(attrlist, "(sides-supported=", &tmp))
return (SLP_FALSE);
if (strstr(tmp,"two-sided"))
p->type |= CUPS_PRINTER_DUPLEX;
ClearString(&tmp);
return (SLP_TRUE);
}
SLPBoolean
SrvUrlCallback(SLPHandle hslp,
const char *srvurl,
unsigned short lifetime,
SLPError errcode,
void *cookie)
{
slpsrvurl_t *s,
**head;
(void)hslp;
(void)lifetime;
if (errcode != SLP_OK)
return (SLP_TRUE);
head = (slpsrvurl_t**)cookie;
if ((s = (slpsrvurl_t *)calloc(1, sizeof(slpsrvurl_t))) == NULL)
return (SLP_FALSE);
strlcpy(s->url, srvurl, sizeof(s->url));
if (*head)
s->next = *head;
*head = s;
return (SLP_TRUE);
}
void
UpdateSLPBrowse(void)
{
slpsrvurl_t *s,
*next;
printer_t p;
const char *uri;
char method[HTTP_MAX_URI],
username[HTTP_MAX_URI],
host[HTTP_MAX_URI],
resource[HTTP_MAX_URI];
int port;
LogMessage(L_DEBUG, "UpdateSLPBrowse() Start...");
BrowseSLPRefresh = time(NULL) + BrowseInterval;
s = NULL;
SLPFindSrvs(BrowseSLPHandle, SLP_CUPS_SRVTYPE, "", "",
SrvUrlCallback, &s);
for (; s; s = next)
{
next = s->next;
SLPFindAttrs(BrowseSLPHandle, s->url, "", "", AttrCallback, &p);
uri = s->url + SLP_CUPS_SRVLEN + 1;
if (strncmp(uri, "http://", 7) == 0 ||
strncmp(uri, "ipp://", 6) == 0)
{
httpSeparate(uri, method, username, host, &port, resource);
if (strcasecmp(host, ServerName) == 0)
continue;
if (strstr(uri, "/printers/") != NULL)
ProcessBrowseData(uri, p.type, IPP_PRINTER_IDLE, p.location,
p.info, p.make_model, BROWSE_SLP);
else if (strstr(uri, "/classes/") != NULL)
ProcessBrowseData(uri, p.type | CUPS_PRINTER_CLASS, IPP_PRINTER_IDLE,
p.location, p.info, p.make_model, BROWSE_SLP);
}
free(s);
}
LogMessage(L_DEBUG, "UpdateSLPBrowse() End...");
}
#endif
void BrowseRegisterPrinter(printer_t *p)
{
if (!Browsing || !BrowseLocalProtocols || !BrowseInterval || !Browsers ||
(p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
return;
#ifdef HAVE_DNSSD
if ((BrowseLocalProtocols & BROWSE_DNSSD))
dnssdRegisterPrinter(p);
#endif
}
void BrowseDeregisterPrinter(printer_t *p,
int delete)
{
cups_ptype_t savedtype = p->type;
if (!(p->type & CUPS_PRINTER_REMOTE))
{
p->type |= CUPS_PRINTER_DELETE;
if ((BrowseLocalProtocols & BROWSE_CUPS))
SendCUPSBrowse(p);
p->type = savedtype;
#ifdef HAVE_LIBSLP
#endif
#ifdef HAVE_DNSSD
if (delete && (BrowseLocalProtocols & BROWSE_DNSSD))
dnssdDeregisterPrinter(p);
#endif
}
}
#ifdef HAVE_DNSSD
static void
dnssdRegisterPrinter(printer_t *p)
{
DNSServiceErrorType se;
char *txt_record,
*name;
int txt_len,
i,
port;
CFStringRef computerNameRef;
CFStringEncoding nameEncoding;
CFMutableStringRef shortNameRef;
CFIndex nameLength;
char str_buffer[1024];
const char *computerName;
LogMessage(L_DEBUG, "dnssdRegisterPrinter(%s) %s\n", p->name, !p->dnssd_ipp_ref ? "new" : "update");
if (!p->shared)
{
dnssdDeregisterPrinter(p);
return;
}
computerName = NULL;
if ((computerNameRef = SCDynamicStoreCopyComputerName(mSysConfigStore, &nameEncoding)))
if ((computerName = CFStringGetCStringPtr(computerNameRef, kCFStringEncodingUTF8)) == NULL)
if (CFStringGetCString(computerNameRef, str_buffer, sizeof(str_buffer), kCFStringEncodingUTF8))
computerName = str_buffer;
name = NULL;
if (computerName)
SetStringf(&name, "%s @ %s", (p->info && strlen(p->info)) ? p->info : p->name, computerName);
else
SetString(&name, (p->info && strlen(p->info)) ? p->info : p->name);
if (computerNameRef)
CFRelease(computerNameRef);
if (p->reg_name && strcmp(p->reg_name, name))
dnssdDeregisterPrinter(p);
txt_record = dnssdBuildTxtRecord(&txt_len, p);
if (p->dnssd_ipp_ref == NULL)
{
SetString(&p->reg_name, name);
port = ippPort();
for (i = 0; i < NumListeners; i++)
if (Listeners[i].address.sin_family == AF_INET)
{
port = ntohs(Listeners[i].address.sin_port);
break;
}
se = DNSServiceRegister(&p->dnssd_ipp_ref, 0, 0, name, dnssdIPPRegType,
NULL, NULL, htons(port), txt_len, txt_record,
dnssdRegisterCallback, p);
if (se == kDNSServiceErr_BadParam)
{
if ((shortNameRef = CFStringCreateMutable(NULL, 0)) != NULL)
{
CFStringAppendCString(shortNameRef, name, kCFStringEncodingUTF8);
nameLength = CFStringGetLength(shortNameRef);
while (se == kDNSServiceErr_BadParam && nameLength > 1)
{
CFStringDelete(shortNameRef, CFRangeMake(--nameLength, 1));
if (CFStringGetCString(shortNameRef, str_buffer, sizeof(str_buffer), kCFStringEncodingUTF8))
{
se = DNSServiceRegister(&p->dnssd_ipp_ref, 0, 0, str_buffer, dnssdIPPRegType,
NULL, NULL, htons(port), txt_len, txt_record,
dnssdRegisterCallback, p);
}
}
CFRelease(shortNameRef);
}
}
if (se == kDNSServiceErr_NoError)
{
p->dnssd_ipp_fd = DNSServiceRefSockFD(p->dnssd_ipp_ref);
p->txt_record = txt_record;
p->txt_len = txt_len;
txt_record = NULL;
LogMessage(L_DEBUG2, "dnssdRegisterCallback: Adding fd %d to InputSet...",
p->dnssd_ipp_fd);
FD_SET(p->dnssd_ipp_fd, InputSet);
}
else
LogMessage(L_WARN, "dnssd registration of \"%s\" failed with %d", p->name, se);
}
else if (txt_len != p->txt_len || memcmp(txt_record, p->txt_record, txt_len) != 0)
{
se = DNSServiceUpdateRecord(p->dnssd_ipp_ref, NULL, 0,
txt_len, txt_record, 0);
if (p->txt_record)
free(p->txt_record);
p->txt_record = txt_record;
p->txt_len = txt_len;
txt_record = NULL;
}
if (txt_record)
free(txt_record);
if (name)
free(name);
}
static void dnssdDeregisterPrinter(printer_t *p)
{
LogMessage(L_DEBUG, "dnssdDeregisterPrinter(%s)", p->name);
if (p->dnssd_ipp_ref)
{
LogMessage(L_DEBUG2, "dnssdDeregisterPrinter: Removing fd %d from InputSet...",
p->dnssd_ipp_fd);
FD_CLR(p->dnssd_ipp_fd, InputFds);
FD_CLR(p->dnssd_ipp_fd, InputSet);
DNSServiceRefDeallocate(p->dnssd_ipp_ref);
p->dnssd_ipp_ref = NULL;
p->dnssd_ipp_fd = -1;
}
if (p->dnssd_query_ref)
{
LogMessage(L_DEBUG2, "dnssdDeregisterPrinter: Removing fd %d from InputSet...",
p->dnssd_query_fd);
FD_CLR(p->dnssd_query_fd, InputFds);
FD_CLR(p->dnssd_query_fd, InputSet);
DNSServiceRefDeallocate(p->dnssd_query_ref);
p->dnssd_query_ref = NULL;
p->dnssd_query_fd = -1;
}
ClearString(&p->reg_name);
ClearString(&p->service_name);
ClearString(&p->host_target);
ClearString(&p->txt_record);
p->browse_protocol &= ~BROWSE_DNSSD;
}
static void dnssdRegisterCallback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
void *context)
{
printer_t *p;
LogMessage(L_DEBUG, "dnssdRegisterCallback(%s, %s)\n", name, regtype);
if (errorCode != 0)
{
LogMessage(L_ERROR, "DNSServiceRegister failed with error %d", (int)errorCode);
return;
}
p = (printer_t*)context;
SetString(&p->service_name, name);
}
static void dnssdBrowseCallback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *service_name,
const char *regtype,
const char *replyDomain,
void *context)
{
DNSServiceErrorType se;
printer_t *p;
dnssd_resolve_t *dnssd_resolve,
*prev;
LogMessage(L_DEBUG, "dnssdBrowseCallback(%s) 0x%X %s\n", service_name, (unsigned int)flags,
(flags & kDNSServiceFlagsAdd) ? "Add" : "Remove" );
if (errorCode != 0)
{
LogMessage(L_ERROR, "DNSServiceBrowse failed with error %d", (int)errorCode);
return;
}
for (p = Printers; p != NULL; p = p->next)
if (p->service_name && strcmp(service_name, p->service_name) == 0)
break;
if ( (flags & kDNSServiceFlagsAdd) )
{
if (p)
{
LogMessage(L_DEBUG, "dnssdBrowseCallback() ignoring existing printer");
return;
}
dnssd_resolve = calloc(1, sizeof(dnssd_resolve_t));
if (dnssd_resolve != NULL)
{
if ((se = DNSServiceResolve(&dnssd_resolve->sdRef, flags, interfaceIndex, service_name,
regtype, replyDomain, dnssdResolveCallback, NULL)) == 0)
{
dnssd_resolve->fd = DNSServiceRefSockFD(dnssd_resolve->sdRef);
LogMessage(L_DEBUG2, "dnssdBrowseCallback: Adding fd %d to InputSet...",
dnssd_resolve->fd);
FD_SET(dnssd_resolve->fd, InputSet);
SetString(&dnssd_resolve->service_name, service_name);
dnssd_resolve->next = DNSSDPendingResolves;
DNSSDPendingResolves = dnssd_resolve;
}
else
free(dnssd_resolve);
}
}
else
{
if (p && (p->type & CUPS_PRINTER_REMOTE))
{
LogMessage(L_INFO, "Remote destination \"%s\" unregistered; deleting it...",
p->name);
DeletePrinter(p, 1);
}
else
{
for (prev = NULL, dnssd_resolve = DNSSDPendingResolves; dnssd_resolve; prev = dnssd_resolve, dnssd_resolve = dnssd_resolve->next)
if (!strcmp(service_name, dnssd_resolve->service_name))
{
if (prev == NULL)
DNSSDPendingResolves = dnssd_resolve->next;
else
prev->next = dnssd_resolve->next;
LogMessage(L_DEBUG2, "dnssdBrowseCallback: Removing fd %d from InputSet...",
dnssd_resolve->fd);
FD_CLR(dnssd_resolve->fd, InputFds);
FD_CLR(dnssd_resolve->fd, InputSet);
DNSServiceRefDeallocate(dnssd_resolve->sdRef);
free(dnssd_resolve);
break;
}
}
}
}
static void dnssdResolveCallback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *fullname,
const char *host_target,
uint16_t port,
uint16_t txt_len,
const char *txtRecord,
void *context)
{
DNSServiceErrorType se;
char uri[1024];
char *txtvers,
*rp,
*make_model,
*location,
*state_str,
*type_str,
*info;
ipp_pstate_t state;
unsigned int type;
printer_t *p;
dnssd_resolve_t *dnssd_resolve,
*prev;
LogMessage(L_DEBUG, "dnssdResolveCallback(%s)\n", fullname);
for (prev = NULL, dnssd_resolve = DNSSDPendingResolves; dnssd_resolve; prev = dnssd_resolve, dnssd_resolve = dnssd_resolve->next)
if (sdRef == dnssd_resolve->sdRef)
break;
if (!dnssd_resolve)
{
LogMessage(L_ERROR, "dnssdResolveCallback missing request!");
return;
}
if (prev == NULL)
DNSSDPendingResolves = dnssd_resolve->next;
else
prev->next = dnssd_resolve->next;
LogMessage(L_DEBUG2, "dnssdResolveCallback: Removing fd %d from InputSet...",
dnssd_resolve->fd);
FD_CLR(dnssd_resolve->fd, InputFds);
FD_CLR(dnssd_resolve->fd, InputSet);
DNSServiceRefDeallocate(dnssd_resolve->sdRef);
if (errorCode != 0)
{
LogMessage(L_ERROR, "DNSServiceResolve failed with error %d", (int)errorCode);
ClearString(&dnssd_resolve->service_name);
free(dnssd_resolve);
return;
}
dnssdFindAttr(txtRecord, txt_len, "txtvers", &txtvers);
dnssdFindAttr(txtRecord, txt_len, "rp", &rp);
dnssdFindAttr(txtRecord, txt_len, "ty", &make_model);
dnssdFindAttr(txtRecord, txt_len, "note", &location);
dnssdFindAttr(txtRecord, txt_len, "printer-state", &state_str);
dnssdFindAttr(txtRecord, txt_len, "printer-type", &type_str);
if (type_str && state_str && rp)
{
type = strtod(type_str, NULL);
state = strtod(state_str, NULL);
snprintf(uri, sizeof(uri), "ipp://%s/%s/%s", host_target,
(type & CUPS_PRINTER_CLASS) ? "classes" : "printers", rp);
info = !strcmp(dnssd_resolve->service_name, rp) ? NULL : dnssd_resolve->service_name;
p = ProcessBrowseData(uri, type, state, location, info, make_model, BROWSE_DNSSD);
if (p)
{
SetString(&p->service_name, dnssd_resolve->service_name);
SetString(&p->host_target, host_target);
if ((se = DNSServiceQueryRecord(&p->dnssd_query_ref, 0, 0, fullname, ns_t_txt, ns_c_in,
dnssdQueryRecordCallback, p)) == 0)
{
p->dnssd_query_fd = DNSServiceRefSockFD(p->dnssd_query_ref);
LogMessage(L_DEBUG2, "dnssdResolveCallback: Adding fd %d to InputSet...",
p->dnssd_query_fd);
FD_SET(p->dnssd_query_fd, InputSet);
}
}
}
else
LogMessage(L_DEBUG, "dnssdResolveCallback missing TXT record keys");
ClearString(&txtvers);
ClearString(&rp);
ClearString(&make_model);
ClearString(&location);
ClearString(&state_str);
ClearString(&type_str);
ClearString(&dnssd_resolve->service_name);
free(dnssd_resolve);
}
static void dnssdQueryRecordCallback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *fullname,
uint16_t rrtype,
uint16_t rrclass,
uint16_t rdlen,
const void *rdata,
uint32_t ttl,
void *context)
{
char uri[1024];
char *txtvers,
*rp,
*make_model,
*location,
*state_str,
*type_str;
ipp_pstate_t state;
unsigned int type;
printer_t *p;
LogMessage(L_DEBUG, "dnssdQueryRecordCallback(%s) %s\n", fullname, (flags & kDNSServiceFlagsAdd) ? "Add" : "Remove");
if (errorCode != 0)
{
LogMessage(L_ERROR, "DNSServiceQueryRecord failed with error %d", (int)errorCode);
return;
}
if ((flags & kDNSServiceFlagsAdd))
{
p = (printer_t *)context;
dnssdFindAttr(rdata, rdlen, "txtvers", &txtvers);
dnssdFindAttr(rdata, rdlen, "rp", &rp);
dnssdFindAttr(rdata, rdlen, "ty", &make_model);
dnssdFindAttr(rdata, rdlen, "note", &location);
dnssdFindAttr(rdata, rdlen, "printer-state", &state_str);
dnssdFindAttr(rdata, rdlen, "printer-type", &type_str);
if (type_str && state_str && rp)
{
type = strtod(type_str, NULL);
state = strtod(state_str, NULL);
snprintf(uri, sizeof(uri), "ipp://%s/%s/%s", p->host_target,
(type & CUPS_PRINTER_CLASS) ? "classes" : "printers", rp);
ProcessBrowseData(uri, type, state, location, p->info, make_model, BROWSE_DNSSD);
}
else
LogMessage(L_DEBUG, "dnssdQueryRecordCallback missing TXT record keys");
ClearString(&txtvers);
ClearString(&rp);
ClearString(&make_model);
ClearString(&location);
ClearString(&state_str);
ClearString(&type_str);
}
}
static char *dnssdBuildTxtRecord(int *txt_len,
printer_t *p)
{
int i;
char type_str[32],
state_str[32],
rp_str[1024],
*keyvalue[32][2];
i = 0;
keyvalue[i ][0] = "txtvers";
keyvalue[i++][1] = "1";
keyvalue[i ][0] = "qtotal";
keyvalue[i++][1] = "1";
keyvalue[i ][0] = "rp";
keyvalue[i++][1] = rp_str;
snprintf(rp_str, sizeof(rp_str), "%s/%s",
(p->type & CUPS_PRINTER_CLASS) ? "classes" : "printers", p->name);
keyvalue[i ][0] = "ty";
keyvalue[i++][1] = p->make_model;
if (p->location && *p->location != '\0')
{
keyvalue[i ][0] = "note";
keyvalue[i++][1] = p->location;
}
keyvalue[i ][0] = "product";
keyvalue[i++][1] = p->product ? p->product : "Unknown";
snprintf(type_str, sizeof(type_str), "0x%X", p->type | CUPS_PRINTER_REMOTE);
snprintf(state_str, sizeof(state_str), "%d", p->state);
keyvalue[i ][0] = "printer-state";
keyvalue[i++][1] = state_str;
keyvalue[i ][0] = "printer-type";
keyvalue[i++][1] = type_str;
keyvalue[i ][0] = "Transparent";
keyvalue[i++][1] = "T";
keyvalue[i ][0] = "Binary";
keyvalue[i++][1] = "T";
if ((p->type & CUPS_PRINTER_FAX))
{
keyvalue[i ][0] = "Fax";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_COLOR))
{
keyvalue[i ][0] = "Color";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_DUPLEX))
{
keyvalue[i ][0] = "Duplex";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_STAPLE))
{
keyvalue[i ][0] = "Staple";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_COPIES))
{
keyvalue[i ][0] = "Copies";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_COLLATE))
{
keyvalue[i ][0] = "Collate";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_PUNCH))
{
keyvalue[i ][0] = "Punch";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_BIND))
{
keyvalue[i ][0] = "Bind";
keyvalue[i++][1] = "T";
}
if ((p->type & CUPS_PRINTER_SORT))
{
keyvalue[i ][0] = "Sort";
keyvalue[i++][1] = "T";
}
keyvalue[i ][0] = "pdl";
keyvalue[i++][1] = p->pdl ? p->pdl : "application/postscript";
return dnssdPackTxtRecord(txt_len, keyvalue, i);
}
static char *dnssdPackTxtRecord(int *txt_len,
char *keyvalue[][2],
int count)
{
int index;
int length;
char *txtRecord;
char *cursor;
for (length = index = 0; index < count; index++)
length += 1 + strlen(keyvalue[index][0]) +
(keyvalue[index][1] ? 1 + strlen(keyvalue[index][1]) : 0);
txtRecord = malloc(length);
if (txtRecord)
{
*txt_len = length;
for (cursor = txtRecord, index = 0; index < count; index++)
{
*cursor++ = (unsigned char)(strlen(keyvalue[index][0]) +
(keyvalue[index][1] ? 1 + strlen(keyvalue[index][1]) : 0));
length = strlen(keyvalue[index][0]);
memcpy(cursor, keyvalue[index][0], length);
cursor += length;
if (keyvalue[index][1])
{
*cursor++ = '=';
length = strlen(keyvalue[index][1]);
memcpy(cursor, keyvalue[index][1], length);
cursor += length;
}
}
}
return txtRecord;
}
static int dnssdFindAttr(const unsigned char *txtRecord,
int txt_len,
const char *key,
char **value)
{
int result = -1;
int keyLen;
int valueLen;
const unsigned char *txtRecordEnd;
*value = NULL;
keyLen = strlen(key);
txtRecordEnd = txtRecord + txt_len;
for (txtRecordEnd = txtRecord + txt_len; txtRecord < txtRecordEnd; txtRecord = txtRecord + *txtRecord + 1)
{
if (*txtRecord >= keyLen && memcmp(key, txtRecord+1, keyLen) == 0 && (*txtRecord == keyLen || *(txtRecord + keyLen + 1) == '='))
{
result = 0;
valueLen = *txtRecord - keyLen - 1;
if (valueLen < 0)
result = -2;
else
{
if ((*value = malloc(valueLen + 1)) == NULL)
result = -3;
else
{
memcpy(*value, txtRecord + keyLen + 2, valueLen);
(*value)[valueLen] = '\0';
}
}
break;
}
}
return result;
}
#endif