Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use domain sockets when available #656

Closed
michaelrsweet opened this issue Mar 25, 2004 · 4 comments
Closed

Use domain sockets when available #656

michaelrsweet opened this issue Mar 25, 2004 · 4 comments
Labels
enhancement New feature or request
Milestone

Comments

@michaelrsweet
Copy link
Collaborator

Version: 1.2-feature
CUPS.org User: jlovell

Using domain sockets more than doubles our throughput. Here's some testspeed results:

INET sockets:
testspeed(8299): 5000 requests in 2.6s (0.001s/r, 1922.3r/s)
testspeed: Simulating 1 clients with 5000 requests to localhost with no encryption...

Domain sockets:
testspeed(6873): 5000 requests in 1.2s (0.000s/r, 4239.1r/s)
testspeed: Simulating 1 clients with 5000 requests to localhost with no encryption...

Domain sockets with the http buffer from STR 547:
testspeed(9990): 5000 requests in 0.9s (0.000s/r, 5404.7r/s)
testspeed: Simulating 1 clients with 5000 requests to localhost with no encryption...

I know you have security concerns with domain sockets but could you give a quick glance at this patch to see if you see any obvious problems or omissions?:

A few main points:

  • autoconf disabled by default.
  • Listen directives that start with '/' are domain sockets
  • Auth checks for localhost or "s_addr == 0x7f000001" also check for "sin_family == AF_LOCAL"
  • In libcups, httpReconnect w/localhost first tries the domain socket before falling back to cups_server

Thanks!

Jim

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

OK, I've holding this for 1.2, since that release has support for different socket address types and will not need as many workarounds as your patch needs for 1.1.

Will add to 1.2 sometime today...

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

OK, I've ported and committed to domain socket changes in CUPS 1.2 CVS. Please let me know if you run into any problems.

Some general notes:

1. I'm using the presence of AF_LOCAL to determine whether to
   support domain sockets (supported out-of-the-box but not used)
2. The http_addr_t union now contains the domain socket address
3. I didn't add a global domain socket address, but client.conf
   and friends will eventually accept a domain socket filename for
   ServerName.
4. I haven't done much testing so far... :)

@michaelrsweet
Copy link
Collaborator Author

CUPS.org User: mike

Fixed in Subversion repository.

@michaelrsweet
Copy link
Collaborator Author

"domainsocket.patch":

--- config.h
+++ config.h
@@ -51,6 +51,14 @@

/*

  • * Do we have domain socket support?
  • /
    +
    +#define HAVE_DOMAINSOCKETS 1
    +#define CUPS_DEFAULT_DOMAINSOCKET "/private/var/run/cupsd"
    +
    +
    +/
    • Where are files stored?
      */

--- config.h.in
+++ config.h.in
@@ -50,6 +50,14 @@

/*

  • * Do we have domain socket support?
  • /
    +
    +#undef HAVE_DOMAINSOCKETS
    +#undef CUPS_DEFAULT_DOMAINSOCKET
    +
    +
    +/
    • Where are files stored?
      */

--- conf/cupsd.conf
+++ conf/cupsd.conf
@@ -398,6 +398,9 @@

Ports/addresses that we listen to. The default port 631 is reserved

for the Internet Printing Protocol (IPP) and is what we use here.

+# If Unix domain socket support is enabled the full path of the socket
+# may also be used.
+#

You can have multiple Port/Listen lines to listen to more than one

port or address, or to restrict access:

@@ -408,6 +411,7 @@

Listen hostname:631

Listen 1.2.3.4

Listen 1.2.3.4:631

+# Listen /private/var/run/cupsd

NOTE: Unfortunately, most web browsers don't support TLS or HTTP Upgrades

for encryption. If you want to support web-based encryption you'll

@@ -418,6 +422,7 @@
#Port 443
#Port 631
Listen 127.0.0.1:631
+Listen /private/var/run/cupsd

HostNameLookups: whether or not to do lookups on IP addresses to get a

--- conf/cupsd.conf.in
+++ conf/cupsd.conf.in
@@ -398,6 +398,9 @@

Ports/addresses that we listen to. The default port 631 is reserved

for the Internet Printing Protocol (IPP) and is what we use here.

+# If Unix domain socket support is enabled the full path of the socket
+# may also be used.
+#

You can have multiple Port/Listen lines to listen to more than one

port or address, or to restrict access:

@@ -408,6 +411,7 @@

Listen hostname:631

Listen 1.2.3.4

Listen 1.2.3.4:631

+# Listen @CUPS_DEFAULT_DOMAINSOCKET@

NOTE: Unfortunately, most web browsers don't support TLS or HTTP Upgrades

for encryption. If you want to support web-based encryption you'll

@@ -418,6 +422,7 @@
#Port 443
#Port 631
Listen 127.0.0.1:631
+Listen @CUPS_DEFAULT_DOMAINSOCKET@

HostNameLookups: whether or not to do lookups on IP addresses to get a

--- config-scripts/cups-network.m4
+++ config-scripts/cups-network.m4
@@ -54,6 +54,47 @@

AC_DEFINE_UNQUOTED(CUPS_MAX_FDS, $maxfiles)

+dnl Check for unix domain socket support...
+AC_ARG_ENABLE(domainsockets, [ --enable-domainsockets enable unix domain socket support, default=no])
+
+if test x$enable_domainsockets = xyes; then

  • AC_MSG_CHECKING(for unix domain socket support)
  • AC_TRY_COMPILE([#include <sys/types.h>
  •   #include <sys/socket.h>
    
  •   #include <sys/un.h>],
    
  •   [struct sockaddr_un sa; sa.sun_family = AF_LOCAL;],
    
  •   cups_cv_domainsockets=yes, cups_cv_domainsockets=no)
    
  • if test x"$cups_cv_domainsockets" = x"yes"; then
  •   AC_DEFINE(HAVE_DOMAINSOCKETS)
    
  •   AC_MSG_RESULT(yes)
    
  • else
  •   AC_MSG_RESULT(no)
    
  • fi
    +fi

+CUPS_DEFAULT_DOMAINSOCKET=""
+AC_ARG_WITH(default_domainsocket, [ --with-domainsocket set unix domain socket name],default_domainsocket="$withval",default_domainsocket="")
+if test x$default_domainsocket = x; then
+

  • case "$uname" in
  •   Darwin*)
    
  •       # Darwin and MacOS X...
    
  •       CUPS_DEFAULT_DOMAINSOCKET="$localstatedir/run/cupsd"
    
  •       AC_DEFINE_UNQUOTED(CUPS_DEFAULT_DOMAINSOCKET, "$localstatedir/run/cupsd")
    
  •       ;;
    
  •   *)
    
  •       # All others...
    
  •       CUPS_DEFAULT_DOMAINSOCKET="$localstatedir/run/cupsd"
    
  •       AC_DEFINE_UNQUOTED(CUPS_DEFAULT_DOMAINSOCKET, "$localstatedir/run/cupsd")
    
  •       ;;
    
  • esac
    +fi
    +AC_SUBST(CUPS_DEFAULT_DOMAINSOCKET)

dnl
-dnl End of "$Id$".
+dnl End of "$Id: cups-network.m4,v 1.1.1.10 2003/12/04 20:36:29 jlovell Exp $".
dnl
--- cups/auth.c
+++ cups/auth.c
@@ -180,7 +180,11 @@
*/

if (ntohl(http->hostaddr.sin_addr.s_addr) != 0x7f000001 &&

  •  strcasecmp(http->hostname, "localhost") != 0)
    
  •  strcasecmp(http->hostname, "localhost") != 0
    
    +#ifdef HAVE_DOMAINSOCKETS
  •  && http->hostaddr.sin_family != AF_LOCAL
    
    +#endif /* HAVE_DOMAINSOCKETS */
  •  )
    
    {
    DEBUG_puts("cups_local_auth: Not a local connection!");
    return (-1);
    --- cups/http-private.h
    +++ cups/http-private.h
    @@ -95,8 +95,12 @@
    extern const char _hstrerror(int error);

    endif /_ !HAVE_HSTRERROR */

+#ifdef HAVE_DOMAINSOCKETS
+extern char cups_server_domainsocket[104];
+#endif /* HAVE_DOMAINSOCKETS /
+
#endif /
!CUPS_HTTP_PRIVATE_H */

/*

  • * End of "$Id$".
  • * End of "$Id: http-private.h,v 1.1.1.2 2003/12/04 20:36:29 jlovell Exp $".
    */
    --- cups/http.c
    +++ cups/http.c
    @@ -88,6 +88,10 @@
    #include "http.h"
    #include "debug.h"

+#ifdef HAVE_DOMAINSOCKETS
+# include <sys/un.h>
+#endif /* HAVE_DOMAINSOCKETS _/
+
#ifndef WIN32

include <signal.h>

include <sys/time.h>

@@ -503,10 +507,95 @@
close(http->fd);
#endif /_ WIN32 */

  • http->fd = -1;

/*

  • Create the socket and set options to allow reuse.
    */

+#ifdef HAVE_DOMAINSOCKETS

  • if (http->hostaddr.sin_family == AF_LOCAL ||
  •  (http->hostaddr.sin_family == AF_INET &&
    
  •   strcasecmp(http->hostname, "localhost") == 0 &&
    
  •   cups_server_domainsocket[0] != '\0'))
    
  • {
  • struct sockaddr_un saddr;
  • if ((http->fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0)
  • {
    +#ifdef WIN32
  •  http->error  = WSAGetLastError();
    
    +#else
  •  http->error  = errno;
    
    +#endif /* WIN32 */
  •  http->status = HTTP_ERROR;
    
  •  return (-1);
    
  • }

+#ifdef FD_CLOEXEC

  • fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting *
  •                \* other processes...              _/
    
    +#endif /_ FD_CLOEXEC */
    +
  • val = 1;
  • setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));

+#ifdef SO_REUSEPORT

  • val = 1;
  • setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
    +#endif /* SO_REUSEPORT */
  • /*
  • * Using TCP_NODELAY improves responsiveness, especially on systems
  • * with a slow loopback interface... Since we write large buffers
  • * when sending print files and requests, there shouldn't be any
  • * performance penalty for this...
  • */
  • val = 1;
    +#ifdef WIN32
  • setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
    +#else
  • setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
    +#endif // WIN32
  • /*
  • * Connect to the server...
  • */
  • saddr.sun_family = AF_LOCAL;
  • strlcpy(saddr.sun_path, cups_server_domainsocket, sizeof(saddr.sun_path));
  • if (connect(http->fd, (struct sockaddr *)&saddr, SUN_LEN(&saddr)) < 0)

  • {
    +#ifdef WIN32

  •  http->error  = WSAGetLastError();
    

    +#else

  •  http->error  = errno;
    

    +#endif /* WIN32 */

  •  http->status = HTTP_ERROR;
    

    +#ifdef WIN32

  •  closesocket(http->fd);
    

    +#else

  •  close(http->fd);
    

    +#endif
    +

  •  http->fd = -1;
    
  • /*
    
  •  \* If the domain socket connection failed for reasons other than ENOENT
    
  •  \* or ECONNREFUSED give up; otherwise fall back to using an AF_INET connection.
    
  •  */
    
  •  if (errno != ENOENT && errno != ECONNREFUSED)
    
  • return (-1);

  • }

  • }

  • +#endif /* HAVE_DOMAINSOCKETS */

  • if (http->fd == -1)

  • {
    if ((http->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
    #ifdef WIN32
    @@ -569,6 +658,7 @@

    return (-1);
    }

  • }

http->error = 0;
http->status = HTTP_CONTINUE;
--- cups/usersys.c
+++ cups/usersys.c
@@ -44,6 +44,7 @@

#include "cups.h"
#include "string.h"
+#include "http-private.h"
#include <stdlib.h>
#include <ctype.h>

@@ -69,6 +70,9 @@
cups_server[256] = "";
static const char _(_cups_pwdcb)(const char *) = cups_get_password;

+#ifdef HAVE_DOMAINSOCKETS
+char cups_server_domainsocket[104] = "";
+#endif /* HAVE_DOMAINSOCKETS */

/*

  • 'cupsEncryption()' - Get the default encryption settings...
    @@ -267,7 +271,17 @@
    */

    strlcpy(cups_server, server, sizeof(cups_server));
    +
    +#ifdef HAVE_DOMAINSOCKETS

    • if (cups_server[0] != '/')
    •  strlcpy(cups_server_domainsocket, CUPS_DEFAULT_DOMAINSOCKET, sizeof(cups_server_domainsocket));
      
    • else
    • {
    •  strlcpy(cups_server_domainsocket, cups_server, sizeof(cups_server));
      
    •  strlcpy(cups_server, "localhost", sizeof(cups_server));
      
      }
      +#endif /* HAVE_DOMAINSOCKETS */
    • }

    return (cups_server);
    }
    --- scheduler/auth.c
    +++ scheduler/auth.c
    @@ -882,7 +882,11 @@
    address = ntohl(con->http.hostaddr.sin_addr.s_addr);
    hostlen = strlen(con->http.hostname);

  • if (address == 0x7f000001 || strcasecmp(con->http.hostname, "localhost") == 0)

  • if (address == 0x7f000001 || strcasecmp(con->http.hostname, "localhost") == 0
    +#ifdef HAVE_DOMAINSOCKETS

  •  || con->http.hostaddr.sin_family == AF_LOCAL
    

    +#endif /* HAVE_DOMAINSOCKETS */

  •  )
    

    {
    /*

    • Access from localhost (127.0.0.1) is always allowed...
      --- scheduler/classes.c
      +++ scheduler/classes.c
      @@ -48,6 +48,8 @@
      printer_t * /* O - New class /
      AddClass(const char *name) /
      I - Name of class */
      {
  • int i, /* Looping var */

  •   port;       /\* Port number to use _/
    

    printer_t *c; /_ New class */

@@ -61,9 +63,16 @@
* Change from a printer to a class...
*/

  • port = 0;
  • for (i = 0; i < NumListeners && Listeners[i].address.sin_family != AF_INET; i++)
  • ;
  • if (i < NumListeners)
  • port = ntohs(Listeners[i].address.sin_port);

c->type = CUPS_PRINTER_CLASS;
SetStringf(&c->uri, "ipp://%s:%d/classes/%s", ServerName,

  •           ntohs(Listeners[0].address.sin_port), name);
    
  •           port, name);
    

    }

    return (c);
    --- scheduler/client.c
    +++ scheduler/client.c
    @@ -162,6 +162,15 @@

    • Get the hostname or format the IP address as needed...
      */

+#ifdef HAVE_DOMAINSOCKETS

  • if (con->http.hostaddr.sin_family == AF_LOCAL)
  • {
  • address = 0x7f000001;
  • host = NULL;
  • }
  • else
    +#endif /* HAVE_DOMAINSOCKETS */
  • {
    address = ntohl(con->http.hostaddr.sin_addr.s_addr);

if (HostNameLookups)
@@ -174,6 +183,7 @@
#endif /* !__sgi */
else
host = NULL;

  • }

if (address == 0x7f000001)
{
@@ -216,7 +226,7 @@
else
strlcpy(con->http.hostname, host->h_name, sizeof(con->http.hostname));

  • if (HostNameLookups == 2)
  • if (HostNameLookups == 2 && con->http.hostaddr.sin_family == AF_INET)
    {
    /*
  • Do double lookups as needed...
    --- scheduler/conf.c
    +++ scheduler/conf.c
    @@ -40,6 +40,10 @@
    #include <pwd.h>
    #include <grp.h>

+#ifdef HAVE_DOMAINSOCKETS
+# include <sys/un.h>
+#endif /* HAVE_DOMAINSOCKETS */
+
#ifdef HAVE_CDSASSL

include <Security/SecureTransport.h>

include <Security/SecIdentitySearch.h>

@@ -232,6 +236,15 @@

if (NumListeners > 0)
{
+#ifdef HAVE_DOMAINSOCKETS

  • int i; /* Looping var */
  • listener_t lis; / Current listening socket */
  • for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)

  • if (lis->address.sin_family == AF_LOCAL)

  •  ClearString((char **)&lis->address.sin_addr);
    

    +#endif /* HAVE_DOMAINSOCKETS */
    +
    free(Listeners);

    NumListeners = 0;
    @@ -2008,6 +2021,32 @@
    */

    memset(address, 0, sizeof(struct sockaddr_in));
    +
    +#ifdef HAVE_DOMAINSOCKETS

  • /*

  • * If the value begins with a / it's a Unix domain socket name

  • */

  • if (*value == '/')
  • {
  • if (strlen(value) >= sizeof(((struct sockaddr_un*)NULL)->sun_path))
  • {
  •  LogMessage(L_ERROR, "Domain socket name too long \"%s\"!", value);
    
  •  return (0);
    
  • }
  • address->sin_family = AF_LOCAL;
  • /*
  • * Instead of copying the string into sockaddr_un.sun_path
  • * we just store a pointer to the string in an sockaddr_in.
  • */
  • (char *)address->sin_addr.s_addr = strdup(value);
  • }
  • else
    +#endif /* HAVE_DOMAINSOCKETS */
  • {
    address->sin_family = AF_INET;
    address->sin_addr.s_addr = htonl(defaddress);
    address->sin_port = htons(defport);
    @@ -2071,6 +2110,7 @@
    address->sin_port = htons(port->s_port);
    }
    }
  • }

return (1);
}
--- scheduler/listen.c
+++ scheduler/listen.c
@@ -36,6 +36,10 @@

#include "cupsd.h"

+#ifdef HAVE_DOMAINSOCKETS
+# include <sys/un.h>
+#endif /* HAVE_DOMAINSOCKETS */
+
#ifdef HAVE_NOTIFY_H
#include <notify.h>
#endif
@@ -128,6 +132,7 @@
void
StartListening(void)
{

  • int err; /* Bind result /
    int i, /
    Looping var /
    val; /
    Parameter value /
    listener_t *lis; /
    Current listening socket */
    @@ -169,6 +174,11 @@

for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
{
+#ifdef HAVE_DOMAINSOCKETS

  • if (lis->address.sin_family == AF_LOCAL)

  •  LogMessage(L_DEBUG, "StartListening: domain socket=%s", (char*)lis->address.sin_addr.s_addr);
    
  • else
    +#endif /* HAVE_DOMAINSOCKETS */
    LogMessage(L_DEBUG, "StartListening: address=%08x port=%d",
    (unsigned)ntohl(lis->address.sin_addr.s_addr),
    ntohs(lis->address.sin_port));
    @@ -178,15 +188,16 @@

  • "any" address...
    */

  • if (ntohl(lis->address.sin_addr.s_addr) == 0x7f000001 ||

  •    ntohl(lis->address.sin_addr.s_addr) == 0x00000000)
    
  • if (lis->address.sin_family == AF_INET &&

  •    (ntohl(lis->address.sin_addr.s_addr) == 0x7f000001 ||
    
  •     ntohl(lis->address.sin_addr.s_addr) == 0x00000000))
    

    LocalPort = ntohs(lis->address.sin_port);

    /*

    • Create a socket for listening...
      */
  • if ((lis->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

  • if ((lis->fd = socket(lis->address.sin_family, SOCK_STREAM, 0)) == -1)
    {
    LogMessage(L_ERROR, "StartListening: Unable to open listen socket - %s.",
    strerror(errno));
    @@ -210,7 +221,27 @@

  • Bind to the port we found...
    */

  • if (bind(lis->fd, (struct sockaddr *)&(lis->address), sizeof(lis->address)) < 0)

  • if (lis->address.sin_family == AF_INET)

  •  err = bind(lis->fd, (struct sockaddr *)&lis->address, sizeof(lis->address));
    

    +#ifdef HAVE_DOMAINSOCKETS

  • else if (lis->address.sin_family == AF_LOCAL)

  • {

  •  struct sockaddr_un laddr;
    
  •  mode_t        mask;
    
  •  unlink(_(char *_)&lis->address.sin_addr);
    
  •  bzero(&laddr, sizeof(laddr));
    
  •  laddr.sun_family = AF_LOCAL;
    
  •  strlcpy(laddr.sun_path, _(char *_)&lis->address.sin_addr, sizeof(laddr.sun_path));
    
  •  mask = umask(0);
    
  •  err = bind(lis->fd, (struct sockaddr *)&laddr, SUN_LEN(&laddr));
    
  •  umask(mask);
    
  • }
    +#endif /* HAVE_DOMAINSOCKETS */

  • else

  •  err = -2;
    
  • if (err < 0)
    {
    LogMessage(L_ERROR, "StartListening: Unable to bind socket - %s.", strerror(errno));
    exit(errno);
    @@ -248,14 +279,21 @@
    PauseListening();

for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)

  • {
    #ifdef WIN32
    closesocket(lis->fd);
    #else
    close(lis->fd);
    #endif /* WIN32 */

+#ifdef HAVE_DOMAINSOCKETS

  • if (lis->address.sin_family == AF_LOCAL)

  •  unlink(*(char **)&lis->address.sin_addr);
    

    +#endif /* HAVE_DOMAINSOCKETS */
    }
    +}

    /*

  • * End of "$Id$".

  • * End of "$Id: listen.c,v 1.7 2003/09/05 01:14:51 jlovell Exp $".
    /
    --- scheduler/printers.c
    +++ scheduler/printers.c
    @@ -86,6 +86,8 @@
    printer_t * /
    O - New printer /
    AddPrinter(const char *name) /
    I - Name of printer */
    {

  • int i, /* Looping var */

  •   port;       /\* Port number to use _/
    

    printer_t *p, /_ New printer /
    *current, /
    Current printer in list /
    *prev; /
    Previous printer in list */
    @@ -114,8 +116,16 @@
    SetString(&p->name, name);
    SetString(&p->info, name);
    SetString(&p->hostname, ServerName);
    +

  • port = 0;

  • for (i = 0; i < NumListeners && Listeners[i].address.sin_family != AF_INET; i++)

  • ;

  • if (i < NumListeners)
  • port = ntohs(Listeners[i].address.sin_port);

SetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName,

  •         ntohs(Listeners[0].address.sin_port), name);
    
  •         port, name);
    

    SetStringf(&p->device_uri, "file:/dev/null");

    p->state = IPP_PRINTER_STOPPED;

@michaelrsweet michaelrsweet added the enhancement New feature or request label Mar 17, 2016
@michaelrsweet michaelrsweet added this to the Stable milestone Mar 17, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant