static int /* O - 0 on success or -1 on error */ try_connect(http_addr_t *addr, /* I - Socket address */ const char *addrname, /* I - Hostname or IP address */ int port) /* I - Port number */ { int fd; /* Socket */ int status; /* Connection status */ debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(), port == 515 ? "lpd" : "socket", addrname, port); if ((fd = socket(httpAddrFamily(addr), SOCK_STREAM, 0)) < 0) { fprintf(stderr, "ERROR: Unable to create socket: %s\n", strerror(errno)); return (-1); } _httpAddrSetPort(addr, port); alarm(1); status = connect(fd, (void *)addr, (socklen_t)httpAddrLength(addr)); close(fd); alarm(0); return (status); }
int /* O - 1 on success, 0 on error */ _cupsSNMPWrite( int fd, /* I - SNMP socket */ http_addr_t *address, /* I - Address to send to */ int version, /* I - SNMP version */ const char *community, /* I - Community name */ cups_asn1_t request_type, /* I - Request type */ const unsigned request_id, /* I - Request ID */ const int *oid) /* I - OID */ { int i; /* Looping var */ cups_snmp_t packet; /* SNMP message packet */ unsigned char buffer[CUPS_SNMP_MAX_PACKET]; /* SNMP message buffer */ int bytes; /* Size of message */ http_addr_t temp; /* Copy of address */ /* * Range check input... */ DEBUG_printf(("4_cupsSNMPWrite(fd=%d, address=%p, version=%d, " "community=\"%s\", request_type=%d, request_id=%u, oid=%p)", fd, address, version, community, request_type, request_id, oid)); if (fd < 0 || !address || version != CUPS_SNMP_VERSION_1 || !community || (request_type != CUPS_ASN1_GET_REQUEST && request_type != CUPS_ASN1_GET_NEXT_REQUEST) || request_id < 1 || !oid) { DEBUG_puts("5_cupsSNMPWrite: Returning 0 (bad arguments)"); return (0); } /* * Create the SNMP message... */ memset(&packet, 0, sizeof(packet)); packet.version = version; packet.request_type = request_type; packet.request_id = request_id; packet.object_type = CUPS_ASN1_NULL_VALUE; strlcpy(packet.community, community, sizeof(packet.community)); for (i = 0; oid[i] >= 0 && i < (CUPS_SNMP_MAX_OID - 1); i ++) packet.object_name[i] = oid[i]; packet.object_name[i] = -1; if (oid[i] >= 0) { DEBUG_puts("5_cupsSNMPWrite: Returning 0 (OID too big)"); errno = E2BIG; return (0); } bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet); if (bytes < 0) { DEBUG_puts("5_cupsSNMPWrite: Returning 0 (request too big)"); errno = E2BIG; return (0); } asn1_debug("DEBUG: OUT ", buffer, bytes, 0); /* * Send the message... */ temp = *address; _httpAddrSetPort(&temp, CUPS_SNMP_PORT); return (sendto(fd, buffer, bytes, 0, (void *)&temp, httpAddrLength(&temp)) == bytes); }
/* A non-blocking test whether it is possible to connect to a CUPS server specified * inside of GtkCupsConnectionTest structure. * - you need to check it more then once. * The connection is closed after a successful connection. */ GtkCupsConnectionState gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test) { GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE; http_addrlist_t *iter; gint error_code; gint flags; gint code; if (test == NULL) return GTK_CUPS_CONNECTION_NOT_AVAILABLE; if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE) { test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE; return GTK_CUPS_CONNECTION_AVAILABLE; } else { if (test->socket == -1) { if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL) iter = test->last_wrong_addr->next; else { test->last_wrong_addr = NULL; iter = test->addrlist; } while (iter) { test->socket = socket (iter->addr.addr.sa_family, SOCK_STREAM, 0); if (test->socket >= 0) { flags = fcntl (test->socket, F_GETFL); if (flags != -1) flags |= O_NONBLOCK; fcntl (test->socket, F_SETFL, flags); test->current_addr = iter; break; } iter = iter->next; } } if (test->socket >= 0) { code = connect (test->socket, &test->current_addr->addr.addr, httpAddrLength (&test->current_addr->addr)); error_code = errno; if (code == 0 || error_code == EISCONN) { close (test->socket); test->socket = -1; test->current_addr = NULL; result = GTK_CUPS_CONNECTION_AVAILABLE; } else { if (error_code == EALREADY || error_code == EINPROGRESS) result = GTK_CUPS_CONNECTION_IN_PROGRESS; else { close (test->socket); test->socket = -1; test->last_wrong_addr = test->current_addr; result = GTK_CUPS_CONNECTION_NOT_AVAILABLE; } } } return result; } }
static int /* O - Zero on success, non-zero on failure */ lpd_queue(const char *hostname, /* I - Host to connect to */ http_addrlist_t *addrlist, /* I - List of host addresses */ const char *printer, /* I - Printer/queue name */ int print_fd, /* I - File to print */ int snmp_fd, /* I - SNMP socket */ int mode, /* I - Print mode */ const char *user, /* I - Requesting user */ const char *title, /* I - Job title */ int copies, /* I - Number of copies */ int banner, /* I - Print LPD banner? */ int format, /* I - Format specifier */ int order, /* I - Order of data/control files */ int reserve, /* I - Reserve ports? */ int manual_copies,/* I - Do copies by hand... */ int timeout, /* I - Timeout... */ int contimeout, /* I - Connection timeout */ const char *orighost) /* I - job-originating-host-name */ { char localhost[255]; /* Local host name */ int error; /* Error number */ struct stat filestats; /* File statistics */ int lport; /* LPD connection local port */ int fd; /* LPD socket */ char control[10240], /* LPD control 'file' */ *cptr; /* Pointer into control file string */ char status; /* Status byte from command */ int delay; /* Delay for retries... */ char addrname[256]; /* Address name */ http_addrlist_t *addr; /* Socket address */ int have_supplies; /* Printer supports supply levels? */ int copy; /* Copies written */ time_t start_time; /* Time of first connect */ size_t nbytes; /* Number of bytes written */ off_t tbytes; /* Total bytes written */ char buffer[32768]; /* Output buffer */ #ifdef WIN32 DWORD tv; /* Timeout in milliseconds */ #else struct timeval tv; /* Timeout in secs and usecs */ #endif /* WIN32 */ /* * Remember when we started trying to connect to the printer... */ start_time = time(NULL); /* * Loop forever trying to print the file... */ while (!abort_job) { /* * First try to reserve a port for this connection... */ fprintf(stderr, "DEBUG: Connecting to %s:%d for printer %s\n", hostname, httpAddrPort(&(addrlist->addr)), printer); _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer.")); for (lport = reserve == RESERVE_RFC1179 ? 732 : 1024, addr = addrlist, delay = 5;; addr = addr->next) { /* * Stop if this job has been canceled... */ if (abort_job) return (CUPS_BACKEND_FAILED); /* * Choose the next priviledged port... */ if (!addr) addr = addrlist; lport --; if (lport < 721 && reserve == RESERVE_RFC1179) lport = 731; else if (lport < 1) lport = 1023; #ifdef HAVE_GETEUID if (geteuid() || !reserve) #else if (getuid() || !reserve) #endif /* HAVE_GETEUID */ { /* * Just create a regular socket... */ if ((fd = socket(addr->addr.addr.sa_family, SOCK_STREAM, 0)) < 0) { perror("DEBUG: Unable to create socket"); sleep(1); continue; } lport = 0; } else { /* * We're running as root and want to comply with RFC 1179. Reserve a * priviledged lport between 721 and 731... */ if ((fd = rresvport_af(&lport, addr->addr.addr.sa_family)) < 0) { perror("DEBUG: Unable to reserve port"); sleep(1); continue; } } /* * Connect to the printer or server... */ if (abort_job) { close(fd); return (CUPS_BACKEND_FAILED); } if (!connect(fd, &(addr->addr.addr), httpAddrLength(&(addr->addr)))) break; error = errno; close(fd); if (addr->next) continue; if (getenv("CLASS") != NULL) { /* * If the CLASS environment variable is set, the job was submitted * to a class and not to a specific queue. In this case, we want * to abort immediately so that the job can be requeued on the next * available printer in the class. */ _cupsLangPrintFilter(stderr, "INFO", _("Unable to contact printer, queuing on next " "printer in class.")); /* * Sleep 5 seconds to keep the job from requeuing too rapidly... */ sleep(5); return (CUPS_BACKEND_FAILED); } fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(error)); if (error == ECONNREFUSED || error == EHOSTDOWN || error == EHOSTUNREACH) { if (contimeout && (time(NULL) - start_time) > contimeout) { _cupsLangPrintFilter(stderr, "ERROR", _("The printer is not responding.")); return (CUPS_BACKEND_FAILED); } switch (error) { case EHOSTDOWN : _cupsLangPrintFilter(stderr, "WARNING", _("The printer may not exist or " "is unavailable at this time.")); break; case EHOSTUNREACH : _cupsLangPrintFilter(stderr, "WARNING", _("The printer is unreachable at " "this time.")); break; case ECONNREFUSED : default : _cupsLangPrintFilter(stderr, "WARNING", _("The printer is in use.")); break; } sleep(delay); if (delay < 30) delay += 5; } else if (error == EADDRINUSE) { /* * Try on another port... */ sleep(1); } else { _cupsLangPrintFilter(stderr, "ERROR", _("The printer is not responding.")); sleep(30); } } /* * Set the timeout... */ #ifdef WIN32 tv = (DWORD)(timeout * 1000); setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); #else tv.tv_sec = timeout; tv.tv_usec = 0; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); #endif /* WIN32 */ fputs("STATE: -connecting-to-device\n", stderr); _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer.")); fprintf(stderr, "DEBUG: Connected to %s:%d (local port %d)...\n", httpAddrString(&(addr->addr), addrname, sizeof(addrname)), httpAddrPort(&(addr->addr)), lport); /* * See if the printer supports SNMP... */ if (snmp_fd >= 0) have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr), NULL, NULL); else have_supplies = 0; /* * Check for side-channel requests... */ backendCheckSideChannel(snmp_fd, &(addrlist->addr)); /* * Next, open the print file and figure out its size... */ if (print_fd) { /* * Use the size from the print file... */ if (fstat(print_fd, &filestats)) { close(fd); perror("DEBUG: unable to stat print file"); return (CUPS_BACKEND_FAILED); } filestats.st_size *= manual_copies; } else { /* * Use a "very large value" for the size so that the printer will * keep printing until we close the connection... */ #ifdef _LARGEFILE_SOURCE filestats.st_size = (size_t)(999999999999.0); #else filestats.st_size = 2147483647; #endif /* _LARGEFILE_SOURCE */ } /* * Send a job header to the printer, specifying no banner page and * literal output... */ if (lpd_command(fd, "\002%s\n", printer)) /* Receive print job(s) */ { close(fd); return (CUPS_BACKEND_FAILED); } if (orighost && _cups_strcasecmp(orighost, "localhost")) strlcpy(localhost, orighost, sizeof(localhost)); else httpGetHostname(NULL, localhost, sizeof(localhost)); snprintf(control, sizeof(control), "H%.31s\n" /* RFC 1179, Section 7.2 - host name <= 31 chars */ "P%.31s\n" /* RFC 1179, Section 7.2 - user name <= 31 chars */ "J%.99s\n", /* RFC 1179, Section 7.2 - job name <= 99 chars */ localhost, user, title); cptr = control + strlen(control); if (banner) { snprintf(cptr, sizeof(control) - (cptr - control), "C%.31s\n" /* RFC 1179, Section 7.2 - class name <= 31 chars */ "L%s\n", localhost, user); cptr += strlen(cptr); } while (copies > 0) { snprintf(cptr, sizeof(control) - (cptr - control), "%cdfA%03d%.15s\n", format, (int)getpid() % 1000, localhost); cptr += strlen(cptr); copies --; } snprintf(cptr, sizeof(control) - (cptr - control), "UdfA%03d%.15s\n" "N%.131s\n", /* RFC 1179, Section 7.2 - sourcefile name <= 131 chars */ (int)getpid() % 1000, localhost, title); fprintf(stderr, "DEBUG: Control file is:\n%s", control); if (order == ORDER_CONTROL_DATA) { /* * Check for side-channel requests... */ backendCheckSideChannel(snmp_fd, &(addr->addr)); /* * Send the control file... */ if (lpd_command(fd, "\002%d cfA%03.3d%.15s\n", strlen(control), (int)getpid() % 1000, localhost)) { close(fd); return (CUPS_BACKEND_FAILED); } fprintf(stderr, "DEBUG: Sending control file (%u bytes)\n", (unsigned)strlen(control)); if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1)) { status = errno; perror("DEBUG: Unable to write control file"); } else { if (read(fd, &status, 1) < 1) { _cupsLangPrintFilter(stderr, "WARNING", _("The printer did not respond.")); status = errno; } } if (status != 0) _cupsLangPrintFilter(stderr, "ERROR", _("Remote host did not accept control file (%d)."), status); else _cupsLangPrintFilter(stderr, "INFO", _("Control file sent successfully.")); } else status = 0; if (status == 0) { /* * Check for side-channel requests... */ backendCheckSideChannel(snmp_fd, &(addr->addr)); /* * Send the print file... */ if (lpd_command(fd, "\003" CUPS_LLFMT " dfA%03.3d%.15s\n", CUPS_LLCAST filestats.st_size, (int)getpid() % 1000, localhost)) { close(fd); return (CUPS_BACKEND_FAILED); } fprintf(stderr, "DEBUG: Sending data file (" CUPS_LLFMT " bytes)\n", CUPS_LLCAST filestats.st_size); tbytes = 0; for (copy = 0; copy < manual_copies; copy ++) { lseek(print_fd, 0, SEEK_SET); while ((nbytes = read(print_fd, buffer, sizeof(buffer))) > 0) { _cupsLangPrintFilter(stderr, "INFO", _("Spooling job, %.0f%% complete."), 100.0 * tbytes / filestats.st_size); if (lpd_write(fd, buffer, nbytes) < nbytes) { perror("DEBUG: Unable to send print file to printer"); break; } else tbytes += nbytes; } } if (mode == MODE_STANDARD) { if (tbytes < filestats.st_size) status = errno; else if (lpd_write(fd, "", 1) < 1) { perror("DEBUG: Unable to send trailing nul to printer"); status = errno; } else { /* * Read the status byte from the printer; if we can't read the byte * back now, we should set status to "errno", however at this point * we know the printer got the whole file and we don't necessarily * want to requeue it over and over... */ if (recv(fd, &status, 1, 0) < 1) { _cupsLangPrintFilter(stderr, "WARNING", _("The printer did not respond.")); status = 0; } } } else status = 0; if (status != 0) _cupsLangPrintFilter(stderr, "ERROR", _("Remote host did not accept data file (%d)."), status); else _cupsLangPrintFilter(stderr, "INFO", _("Data file sent successfully.")); } if (status == 0 && order == ORDER_DATA_CONTROL) { /* * Check for side-channel requests... */ backendCheckSideChannel(snmp_fd, &(addr->addr)); /* * Send control file... */ if (lpd_command(fd, "\002%d cfA%03.3d%.15s\n", strlen(control), (int)getpid() % 1000, localhost)) { close(fd); return (CUPS_BACKEND_FAILED); } fprintf(stderr, "DEBUG: Sending control file (%lu bytes)\n", (unsigned long)strlen(control)); if (lpd_write(fd, control, strlen(control) + 1) < (strlen(control) + 1)) { status = errno; perror("DEBUG: Unable to write control file"); } else { if (read(fd, &status, 1) < 1) { _cupsLangPrintFilter(stderr, "WARNING", _("The printer did not respond.")); status = errno; } } if (status != 0) _cupsLangPrintFilter(stderr, "ERROR", _("Remote host did not accept control file (%d)."), status); else _cupsLangPrintFilter(stderr, "INFO", _("Control file sent successfully.")); } /* * Collect the final supply levels as needed... */ if (have_supplies) backendSNMPSupplies(snmp_fd, &(addr->addr), NULL, NULL); /* * Close the socket connection and input file... */ close(fd); if (status == 0) return (CUPS_BACKEND_OK); /* * Waiting for a retry... */ sleep(30); } /* * If we get here, then the job has been canceled... */ return (CUPS_BACKEND_FAILED); }
static int /* O - Socket or -1 on error */ cups_rresvport(int *port, /* IO - Port number to bind to */ int family) /* I - Address family */ { http_addr_t addr; /* Socket address */ int fd; /* Socket file descriptor */ /* * Try to create an IPv4 socket... */ if ((fd = socket(family, SOCK_STREAM, 0)) < 0) return (-1); /* * Initialize the address buffer... */ memset(&addr, 0, sizeof(addr)); addr.addr.sa_family = (sa_family_t)family; /* * Try to bind the socket to a reserved port... */ while (*port > 511) { /* * Set the port number... */ _httpAddrSetPort(&addr, *port); /* * Try binding the port to the socket; return if all is OK... */ if (!bind(fd, (struct sockaddr *)&addr, (socklen_t)httpAddrLength(&addr))) return (fd); /* * Stop if we have any error other than "address already in use"... */ if (errno != EADDRINUSE) { httpAddrClose(NULL, fd); return (-1); } /* * Try the next port... */ (*port)--; } /* * Wasn't able to bind to a reserved port, so close the socket and return * -1... */ #ifdef WIN32 closesocket(fd); #else close(fd); #endif /* WIN32 */ return (-1); }
void cupsdStartListening(void) { int status; /* Bind result */ int p, /* Port number */ val; /* Parameter value */ cupsd_listener_t *lis; /* Current listening socket */ char s[256]; /* String addresss */ const char *have_domain; /* Have a domain socket? */ static const char * const encryptions[] = { /* Encryption values */ "IfRequested", "Never", "Required", "Always" }; cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartListening: %d Listeners", cupsArrayCount(Listeners)); /* * Setup socket listeners... */ for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners), LocalPort = 0, have_domain = NULL; lis; lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) { httpAddrString(&(lis->address), s, sizeof(s)); p = httpAddrPort(&(lis->address)); /* * If needed, create a socket for listening... */ if (lis->fd == -1) { /* * Create a socket for listening... */ lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0); if (lis->fd == -1) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open listen socket for address %s:%d - %s.", s, p, strerror(errno)); #ifdef AF_INET6 /* * IPv6 is often disabled while DNS returns IPv6 addresses... */ if (lis->address.addr.sa_family != AF_INET6 && (FatalErrors & CUPSD_FATAL_LISTEN)) cupsdEndProcess(getpid(), 0); #else if (FatalErrors & CUPSD_FATAL_LISTEN) cupsdEndProcess(getpid(), 0); #endif /* AF_INET6 */ continue; } /* * Set things up to reuse the local address for this port. */ val = 1; #ifdef __sun setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); #else setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); #endif /* __sun */ /* * Bind to the port we found... */ #ifdef AF_INET6 if (lis->address.addr.sa_family == AF_INET6) { # ifdef IPV6_V6ONLY /* * Accept only IPv6 connections on this socket, to avoid * potential security issues and to make all platforms behave * the same. */ val = 1; # ifdef __sun setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val)); # else setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); # endif /* __sun */ # endif /* IPV6_V6ONLY */ status = bind(lis->fd, (struct sockaddr *)&(lis->address), httpAddrLength(&(lis->address))); } else #endif /* AF_INET6 */ #ifdef AF_LOCAL if (lis->address.addr.sa_family == AF_LOCAL) { mode_t mask; /* Umask setting */ /* * Remove any existing domain socket file... */ unlink(lis->address.un.sun_path); /* * Save the current umask and set it to 0 so that all users can access * the domain socket... */ mask = umask(0); /* * Bind the domain socket... */ status = bind(lis->fd, (struct sockaddr *)&(lis->address), httpAddrLength(&(lis->address))); /* * Restore the umask... */ umask(mask); } else #endif /* AF_LOCAL */ status = bind(lis->fd, (struct sockaddr *)&(lis->address), sizeof(lis->address.ipv4)); if (status < 0) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to bind socket for address %s:%d - %s.", s, p, strerror(errno)); close(lis->fd); lis->fd = -1; if (FatalErrors & CUPSD_FATAL_LISTEN) cupsdEndProcess(getpid(), 0); continue; } /* * Listen for new clients. */ if (listen(lis->fd, ListenBackLog) < 0) { cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to listen for clients on address %s:%d - %s.", s, p, strerror(errno)); close(lis->fd); lis->fd = -1; if (FatalErrors & CUPSD_FATAL_LISTEN) cupsdEndProcess(getpid(), 0); continue; } } fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); if (p) cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d on fd %d...", s, p, lis->fd); else { cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s on fd %d...", s, lis->fd); if (chmod(s, 0140777)) cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to change permisssions on domain socket " "\"%s\" - %s", s, strerror(errno)); } /* * Save the first port that is bound to the local loopback or * "any" address... */ if ((!LocalPort || LocalEncryption == HTTP_ENCRYPT_ALWAYS) && p > 0 && (httpAddrLocalhost(&(lis->address)) || httpAddrAny(&(lis->address)))) { LocalPort = p; LocalEncryption = lis->encryption; } #ifdef AF_LOCAL if (lis->address.addr.sa_family == AF_LOCAL && !have_domain) have_domain = lis->address.un.sun_path; #endif /* AF_LOCAL */ } /* * Make sure that we are listening on localhost! */ if (!LocalPort && !have_domain) { cupsdLogMessage(CUPSD_LOG_EMERG, "No Listen or Port lines were found to allow access via " "localhost!"); if (FatalErrors & (CUPSD_FATAL_CONFIG | CUPSD_FATAL_LISTEN)) cupsdEndProcess(getpid(), 0); } /* * Set the CUPS_SERVER, IPP_PORT, and CUPS_ENCRYPTION variables based on * the listeners... */ if (have_domain) { /* * Use domain sockets for the local connection... */ cupsdSetEnv("CUPS_SERVER", have_domain); LocalEncryption = HTTP_ENCRYPT_IF_REQUESTED; } else { /* * Use the default local loopback address for the server... */ cupsdSetEnv("CUPS_SERVER", "localhost"); } cupsdSetEnv("CUPS_ENCRYPTION", encryptions[LocalEncryption]); if (LocalPort) cupsdSetEnvf("IPP_PORT", "%d", LocalPort); /* * Resume listening for connections... */ cupsdResumeListening(); }
http_addrlist_t * /* O - Connected address or NULL on failure */ httpAddrConnect2( http_addrlist_t *addrlist, /* I - List of potential addresses */ int *sock, /* O - Socket */ int msec, /* I - Timeout in milliseconds */ int *cancel) /* I - Pointer to "cancel" variable */ { int val; /* Socket option value */ #ifndef WIN32 int flags; /* Socket flags */ #endif /* !WIN32 */ int remaining; /* Remaining timeout */ int i, /* Looping var */ nfds, /* Number of file descriptors */ fds[100], /* Socket file descriptors */ result; /* Result from select() or poll() */ http_addrlist_t *addrs[100]; /* Addresses */ #ifndef HAVE_POLL int max_fd = -1; /* Highest file descriptor */ #endif /* !HAVE_POLL */ #ifdef O_NONBLOCK # ifdef HAVE_POLL struct pollfd pfds[100]; /* Polled file descriptors */ # else fd_set input_set, /* select() input set */ output_set, /* select() output set */ error_set; /* select() error set */ struct timeval timeout; /* Timeout */ # endif /* HAVE_POLL */ #endif /* O_NONBLOCK */ #ifdef DEBUG socklen_t len; /* Length of value */ http_addr_t peer; /* Peer address */ char temp[256]; /* Temporary address string */ #endif /* DEBUG */ DEBUG_printf(("httpAddrConnect2(addrlist=%p, sock=%p, msec=%d, cancel=%p)", (void *)addrlist, (void *)sock, msec, (void *)cancel)); if (!sock) { errno = EINVAL; _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0); return (NULL); } if (cancel && *cancel) return (NULL); if (msec <= 0) msec = INT_MAX; /* * Loop through each address until we connect or run out of addresses... */ nfds = 0; remaining = msec; while (remaining > 0) { if (cancel && *cancel) { while (nfds > 0) { nfds --; httpAddrClose(NULL, fds[nfds]); } return (NULL); } if (addrlist && nfds < (int)(sizeof(fds) / sizeof(fds[0]))) { /* * Create the socket... */ DEBUG_printf(("2httpAddrConnect2: Trying %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)))); if ((fds[nfds] = (int)socket(httpAddrFamily(&(addrlist->addr)), SOCK_STREAM, 0)) < 0) { /* * Don't abort yet, as this could just be an issue with the local * system not being configured with IPv4/IPv6/domain socket enabled. * * Just skip this address... */ addrlist = addrlist->next; continue; } /* * Set options... */ val = 1; setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEADDR, CUPS_SOCAST &val, sizeof(val)); #ifdef SO_REUSEPORT val = 1; setsockopt(fds[nfds], SOL_SOCKET, SO_REUSEPORT, CUPS_SOCAST &val, sizeof(val)); #endif /* SO_REUSEPORT */ #ifdef SO_NOSIGPIPE val = 1; setsockopt(fds[nfds], SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val)); #endif /* SO_NOSIGPIPE */ /* * Using TCP_NODELAY improves responsiveness, especially on systems * with a slow loopback interface... */ val = 1; setsockopt(fds[nfds], IPPROTO_TCP, TCP_NODELAY, CUPS_SOCAST &val, sizeof(val)); #ifdef FD_CLOEXEC /* * Close this socket when starting another process... */ fcntl(fds[nfds], F_SETFD, FD_CLOEXEC); #endif /* FD_CLOEXEC */ #ifdef O_NONBLOCK /* * Do an asynchronous connect by setting the socket non-blocking... */ DEBUG_printf(("httpAddrConnect2: Setting non-blocking connect()")); flags = fcntl(fds[nfds], F_GETFL, 0); fcntl(fds[nfds], F_SETFL, flags | O_NONBLOCK); #endif /* O_NONBLOCK */ /* * Then connect... */ if (!connect(fds[nfds], &(addrlist->addr.addr), (socklen_t)httpAddrLength(&(addrlist->addr)))) { DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)))); #ifdef O_NONBLOCK fcntl(fds[nfds], F_SETFL, flags); #endif /* O_NONBLOCK */ *sock = fds[nfds]; while (nfds > 0) { nfds --; httpAddrClose(NULL, fds[nfds]); } return (addrlist); } #ifdef WIN32 if (WSAGetLastError() != WSAEINPROGRESS && WSAGetLastError() != WSAEWOULDBLOCK) #else if (errno != EINPROGRESS && errno != EWOULDBLOCK) #endif /* WIN32 */ { DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)), strerror(errno))); httpAddrClose(NULL, fds[nfds]); addrlist = addrlist->next; continue; } #ifndef WIN32 fcntl(fds[nfds], F_SETFL, flags); #endif /* !WIN32 */ #ifndef HAVE_POLL if (fds[nfds] > max_fd) max_fd = fds[nfds]; #endif /* !HAVE_POLL */ addrs[nfds] = addrlist; nfds ++; addrlist = addrlist->next; } if (!addrlist && nfds == 0) break; /* * See if we can connect to any of the addresses so far... */ #ifdef O_NONBLOCK DEBUG_puts("1httpAddrConnect2: Finishing async connect()"); do { if (cancel && *cancel) { /* * Close this socket and return... */ DEBUG_puts("1httpAddrConnect2: Canceled connect()"); while (nfds > 0) { nfds --; httpAddrClose(NULL, fds[nfds]); } *sock = -1; return (NULL); } # ifdef HAVE_POLL for (i = 0; i < nfds; i ++) { pfds[i].fd = fds[i]; pfds[i].events = POLLIN | POLLOUT; } result = poll(pfds, (nfds_t)nfds, addrlist ? 100 : remaining > 250 ? 250 : remaining); DEBUG_printf(("1httpAddrConnect2: poll() returned %d (%d)", result, errno)); # else FD_ZERO(&input_set); for (i = 0; i < nfds; i ++) FD_SET(fds[i], &input_set); output_set = input_set; error_set = input_set; timeout.tv_sec = 0; timeout.tv_usec = (addrlist ? 100 : remaining > 250 ? 250 : remaining) * 1000; result = select(max_fd + 1, &input_set, &output_set, &error_set, &timeout); DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result, errno)); # endif /* HAVE_POLL */ } # ifdef WIN32 while (result < 0 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK)); # else while (result < 0 && (errno == EINTR || errno == EAGAIN)); # endif /* WIN32 */ if (result > 0) { http_addrlist_t *connaddr = NULL; /* Connected address, if any */ for (i = 0; i < nfds; i ++) { # ifdef HAVE_POLL DEBUG_printf(("pfds[%d].revents=%x\n", i, pfds[i].revents)); if (pfds[i].revents && !(pfds[i].revents & (POLLERR | POLLHUP))) # else if (FD_ISSET(fds[i], &input_set) && !FD_ISSET(fds[i], &error_set)) # endif /* HAVE_POLL */ { *sock = fds[i]; connaddr = addrs[i]; # ifdef DEBUG len = sizeof(peer); if (!getpeername(fds[i], (struct sockaddr *)&peer, &len)) DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer, temp, sizeof(temp)), httpAddrPort(&peer))); # endif /* DEBUG */ } # ifdef HAVE_POLL else if (pfds[i].revents & (POLLERR | POLLHUP)) # else else if (FD_ISSET(fds[i], &error_set)) # endif /* HAVE_POLL */ { /* * Error on socket, remove from the "pool"... */ httpAddrClose(NULL, fds[i]); nfds --; if (i < nfds) { memmove(fds + i, fds + i + 1, (size_t)(nfds - i) * (sizeof(fds[0]))); memmove(addrs + i, addrs + i + 1, (size_t)(nfds - i) * (sizeof(addrs[0]))); } i --; } } if (connaddr) return (connaddr); } #endif /* O_NONBLOCK */ if (addrlist) remaining -= 100; else remaining -= 250; }