1diff --git a/cups/http.h b/cups/http.h 2index 6c45301e..ac166970 100644 3--- a/cups/http.h 4+++ b/cups/http.h 5@@ -556,10 +556,11 @@ extern int httpSetCredentials(http_t *http, cups_array_t *certs) _CUPS_API_1_5; 6 extern void httpSetTimeout(http_t *http, double timeout, http_timeout_cb_t cb, void *user_data) _CUPS_API_1_5; 7 8 /**** New in CUPS 1.6/macOS 10.8 ****/ 9-extern http_addrlist_t *httpAddrConnect2(http_addrlist_t *addrlist, int *sock, int msec, int *cancel) _CUPS_API_1_6; 10+extern http_addrlist_t *httpAddrConnect2(http_addrlist_t *addrlist, int *sock, int msec, int *cancel, const char *nic) _CUPS_API_1_6; 11 extern http_state_t httpGetState(http_t *http) _CUPS_API_1_6; 12 extern http_version_t httpGetVersion(http_t *http) _CUPS_API_1_6; 13 extern int httpReconnect2(http_t *http, int msec, int *cancel) _CUPS_API_1_6; 14+extern int httpReconnect3(http_t *http, int msec, int *cancel, const char *nic) _CUPS_API_1_6; 15 16 17 /**** New in CUPS 1.7/macOS 10.9 ****/ 18@@ -569,6 +570,7 @@ extern int httpAddrListen(http_addr_t *addr, int port) _CUPS_API_1_7; 19 extern int httpAddrPort(http_addr_t *addr) _CUPS_API_1_7; 20 extern char *httpAssembleUUID(const char *server, int port, const char *name, int number, char *buffer, size_t bufsize) _CUPS_API_1_7; 21 extern http_t *httpConnect2(const char *host, int port, http_addrlist_t *addrlist, int family, http_encryption_t encryption, int blocking, int msec, int *cancel) _CUPS_API_1_7; 22+extern http_t *httpConnect3(const char *host, int port, http_addrlist_t *addrlist, int family, http_encryption_t encryption, int blocking, int msec, int *cancel, const char *nic) _CUPS_API_1_7; 23 extern const char *httpGetContentEncoding(http_t *http) _CUPS_API_1_7; 24 extern http_status_t httpGetExpect(http_t *http) _CUPS_API_1_7; 25 extern ssize_t httpPeek(http_t *http, char *buffer, size_t length) _CUPS_API_1_7; 26diff --git a/cups/http.c b/cups/http.c 27index 66dd3b07..e98c0550 100644 28--- a/cups/http.c 29+++ b/cups/http.c 30@@ -476,6 +476,53 @@ httpConnect2( 31 return (NULL); 32 } 33 34+/* 35+ * 'httpConnect3()' - Connect to a HTTP server. 36+ * 37+ * @since CUPS 1.7/macOS 10.9@ 38+ */ 39+ 40+http_t * /* O - New HTTP connection */ 41+httpConnect3( 42+ const char *host, /* I - Host to connect to */ 43+ int port, /* I - Port number */ 44+ http_addrlist_t *addrlist, /* I - List of addresses or @code NULL@ to lookup */ 45+ int family, /* I - Address family to use or @code AF_UNSPEC@ for any */ 46+ http_encryption_t encryption, /* I - Type of encryption to use */ 47+ int blocking, /* I - 1 for blocking connection, 0 for non-blocking */ 48+ int msec, /* I - Connection timeout in milliseconds, 0 means don't connect */ 49+ int *cancel, /* I - Pointer to "cancel" variable */ 50+ const char *nic) 51+{ 52+ http_t *http; /* New HTTP connection */ 53+ 54+ 55+ DEBUG_printf(("httpConnect2(host=\"%s\", port=%d, addrlist=%p, family=%d, encryption=%d, blocking=%d, msec=%d, cancel=%p)", host, port, (void *)addrlist, family, encryption, blocking, msec, (void *)cancel)); 56+ 57+ /* 58+ * Create the HTTP structure... 59+ */ 60+ 61+ if ((http = http_create(host, port, addrlist, family, encryption, blocking, 62+ _HTTP_MODE_CLIENT)) == NULL) 63+ return (NULL); 64+ 65+ /* 66+ * Optionally connect to the remote system... 67+ */ 68+ 69+ if (msec == 0 || !httpReconnect3(http, msec, cancel, nic)) 70+ return (http); 71+ 72+ /* 73+ * Could not connect to any known address - bail out! 74+ */ 75+ 76+ httpClose(http); 77+ 78+ return (NULL); 79+} 80+ 81 82 /* 83 * 'httpConnectEncrypt()' - Connect to a HTTP server using encryption. 84@@ -2397,7 +2444,133 @@ httpReconnect2(http_t *http, /* I - HTTP connection */ 85 httpAddrPort(&(current->addr)))); 86 #endif /* DEBUG */ 87 88- if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec, cancel)) == NULL) 89+ if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec, cancel, NULL)) == NULL) 90+ { 91+ /* 92+ * Unable to connect... 93+ */ 94+ 95+#ifdef _WIN32 96+ http->error = WSAGetLastError(); 97+#else 98+ http->error = errno; 99+#endif /* _WIN32 */ 100+ http->status = HTTP_STATUS_ERROR; 101+ 102+ DEBUG_printf(("1httpReconnect2: httpAddrConnect failed: %s", 103+ strerror(http->error))); 104+ 105+ return (-1); 106+ } 107+ 108+ DEBUG_printf(("2httpReconnect2: New socket=%d", http->fd)); 109+ 110+ if (http->timeout_value > 0) 111+ http_set_timeout(http->fd, http->timeout_value); 112+ 113+ http->hostaddr = &(addr->addr); 114+ http->error = 0; 115+ 116+#ifdef HAVE_TLS 117+ if (http->encryption == HTTP_ENCRYPTION_ALWAYS) 118+ { 119+ /* 120+ * Always do encryption via SSL. 121+ */ 122+ 123+ if (_httpTLSStart(http) != 0) 124+ { 125+ httpAddrClose(NULL, http->fd); 126+ http->fd = -1; 127+ 128+ return (-1); 129+ } 130+ } 131+ else if (http->encryption == HTTP_ENCRYPTION_REQUIRED && !http->tls_upgrade) 132+ return (http_tls_upgrade(http)); 133+#endif /* HAVE_TLS */ 134+ 135+ DEBUG_printf(("1httpReconnect2: Connected to %s:%d...", 136+ httpAddrString(http->hostaddr, temp, sizeof(temp)), 137+ httpAddrPort(http->hostaddr))); 138+ 139+ return (0); 140+} 141+ 142+/* 143+ * 'httpReconnect3()' - Reconnect to a HTTP server with timeout and optional 144+ * cancel. 145+ */ 146+ 147+int /* O - 0 on success, non-zero on failure */ 148+httpReconnect3(http_t *http, /* I - HTTP connection */ 149+ int msec, /* I - Timeout in milliseconds */ 150+ int *cancel, /* I - Pointer to "cancel" variable */ 151+ const char *nic) 152+{ 153+ http_addrlist_t *addr; /* Connected address */ 154+#ifdef DEBUG 155+ http_addrlist_t *current; /* Current address */ 156+ char temp[256]; /* Temporary address string */ 157+#endif /* DEBUG */ 158+ 159+ 160+ DEBUG_printf(("httpReconnect2(http=%p, msec=%d, cancel=%p)", (void *)http, msec, (void *)cancel)); 161+ 162+ if (!http) 163+ { 164+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); 165+ return (-1); 166+ } 167+ 168+#ifdef HAVE_TLS 169+ if (http->tls) 170+ { 171+ DEBUG_puts("2httpReconnect2: Shutting down SSL/TLS..."); 172+ _httpTLSStop(http); 173+ } 174+#endif /* HAVE_TLS */ 175+ 176+ /* 177+ * Close any previously open socket... 178+ */ 179+ 180+ if (http->fd >= 0) 181+ { 182+ DEBUG_printf(("2httpReconnect2: Closing socket %d...", http->fd)); 183+ 184+ httpAddrClose(NULL, http->fd); 185+ 186+ http->fd = -1; 187+ } 188+ 189+ /* 190+ * Reset all state (except fields, which may be reused)... 191+ */ 192+ 193+ http->state = HTTP_STATE_WAITING; 194+ http->version = HTTP_VERSION_1_1; 195+ http->keep_alive = HTTP_KEEPALIVE_OFF; 196+ memset(&http->_hostaddr, 0, sizeof(http->_hostaddr)); 197+ http->data_encoding = HTTP_ENCODING_FIELDS; 198+ http->_data_remaining = 0; 199+ http->used = 0; 200+ http->data_remaining = 0; 201+ http->hostaddr = NULL; 202+ http->wused = 0; 203+ 204+ /* 205+ * Connect to the server... 206+ */ 207+ 208+#ifdef DEBUG 209+ for (current = http->addrlist; current; current = current->next) 210+ DEBUG_printf(("2httpReconnect2: Address %s:%d", 211+ httpAddrString(&(current->addr), temp, sizeof(temp)), 212+ httpAddrPort(&(current->addr)))); 213+#endif /* DEBUG */ 214+ 215+ if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec, cancel, NULL)) == NULL) 216 { 217 /* 218 * Unable to connect... 219diff --git a/cups/http-addrlist.c b/cups/http-addrlist.c 220index 6e73464c..36b410e8 100644 221--- a/cups/http-addrlist.c 222+++ b/cups/http-addrlist.c 223@@ -39,7 +39,7 @@ httpAddrConnect( 224 { 225 DEBUG_printf(("httpAddrConnect(addrlist=%p, sock=%p)", (void *)addrlist, (void *)sock)); 226 227- return (httpAddrConnect2(addrlist, sock, 30000, NULL)); 228+ return (httpAddrConnect2(addrlist, sock, 30000, NULL, NULL)); 229 } 230 231 232@@ -55,7 +55,8 @@ httpAddrConnect2( 233 http_addrlist_t *addrlist, /* I - List of potential addresses */ 234 int *sock, /* O - Socket */ 235 int msec, /* I - Timeout in milliseconds */ 236- int *cancel) /* I - Pointer to "cancel" variable */ 237+ int *cancel, /* I - Pointer to "cancel" variable */ 238+ const char *nic) 239 { 240 int val; /* Socket option value */ 241 #ifndef _WIN32 242@@ -164,6 +165,11 @@ httpAddrConnect2( 243 setsockopt(fds[nfds], SOL_SOCKET, SO_NOSIGPIPE, CUPS_SOCAST &val, sizeof(val)); 244 #endif /* SO_NOSIGPIPE */ 245 246+ if (nic != NULL) { 247+ val = 1; 248+ setsockopt(fds[nfds], SOL_SOCKET, SO_BINDTODEVICE, nic, sizeof(nic)); 249+ } 250+ 251 /* 252 * Using TCP_NODELAY improves responsiveness, especially on systems 253 * with a slow loopback interface... 254diff --git a/backend/ipp.c b/backend/ipp.c 255index c4b34668..60b867a0 100644 256--- a/backend/ipp.c 257+++ b/backend/ipp.c 258@@ -64,6 +64,7 @@ typedef struct _cups_monitor_s /**** Monitoring data ****/ 259 ipp_jstate_t job_state; /* Current job state */ 260 ipp_pstate_t printer_state; /* Current printer state */ 261 int retryable; /* Is this a job that should be retried? */ 262+ const char *nic; 263 } _cups_monitor_t; 264 265 266@@ -273,6 +274,7 @@ main(int argc, /* I - Number of command-line args */ 267 ppd_file_t *ppd = NULL; /* PPD file */ 268 _ppd_cache_t *pc = NULL; /* PPD cache and mapping data */ 269 fd_set input; /* Input set for select() */ 270+ const char *nic = NULL; 271 272 273 /* 274@@ -651,6 +653,10 @@ main(int argc, /* I - Number of command-line args */ 275 password = getenv("AUTH_PASSWORD"); 276 } 277 278+ num_options = cupsParseOptions(argv[5], 0, &options); 279+ nic = cupsGetOption("nic", num_options, options); 280+ fprintf(stderr, "DEBUG: nic %s\n", nic); 281+ 282 /* 283 * Try finding the remote server... 284 */ 285@@ -659,8 +665,11 @@ main(int argc, /* I - Number of command-line args */ 286 287 addrlist = backendLookup(hostname, port, &job_canceled); 288 289- http = httpConnect2(hostname, port, addrlist, AF_UNSPEC, cupsEncryption(), 1, 290- 0, NULL); 291+ if (nic != NULL) { 292+ http = httpConnect3(hostname, port, addrlist, AF_UNSPEC, cupsEncryption(), 1, 0, NULL, nic); 293+ } else { 294+ http = httpConnect2(hostname, port, addrlist, AF_UNSPEC, cupsEncryption(), 1, 0, NULL); 295+ } 296 httpSetTimeout(http, 30.0, timeout_cb, NULL); 297 298 /* 299@@ -701,7 +710,7 @@ main(int argc, /* I - Number of command-line args */ 300 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port); 301 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer.")); 302 303- if (httpReconnect2(http, 30000, NULL)) 304+ if (nic != NULL ? httpReconnect3(http, 30000, NULL, nic) : httpReconnect2(http, 30000, NULL)) 305 { 306 int error = errno; /* Connection error */ 307 308@@ -986,7 +995,7 @@ main(int argc, /* I - Number of command-line args */ 309 version = 10; 310 } 311 312- httpReconnect2(http, 30000, NULL); 313+ nic != NULL ? httpReconnect3(http, 30000, NULL, nic) : httpReconnect2(http, 30000, NULL); 314 } 315 else if (ipp_status == IPP_STATUS_ERROR_NOT_FOUND) 316 { 317@@ -1018,7 +1027,7 @@ main(int argc, /* I - Number of command-line args */ 318 _("Unable to get printer status.")); 319 sleep(10); 320 321- httpReconnect2(http, 30000, NULL); 322+ nic != NULL ? httpReconnect3(http, 30000, NULL, nic) : httpReconnect2(http, 30000, NULL); 323 } 324 325 ippDelete(supported); 326@@ -1450,6 +1459,7 @@ main(int argc, /* I - Number of command-line args */ 327 monitor.job_state = IPP_JSTATE_PENDING; 328 monitor.printer_state = IPP_PSTATE_IDLE; 329 monitor.retryable = argc == 6 && document_format && strcmp(document_format, "image/pwg-raster") && strcmp(document_format, "image/urf"); 330+ monitor.nic = nic; 331 332 fprintf(stderr, "DEBUG: retryable=%d\n", monitor.retryable); 333 334@@ -2063,7 +2073,7 @@ main(int argc, /* I - Number of command-line args */ 335 * Do the request... 336 */ 337 338- httpReconnect2(http, 30000, NULL); 339+ nic != NULL ? httpReconnect3(http, 30000, NULL, nic) : httpReconnect2(http, 30000, NULL); 340 response = cupsDoRequest(http, request, resource); 341 ipp_status = cupsLastError(); 342 343@@ -2457,8 +2467,13 @@ monitor_printer( 344 * Make a copy of the printer connection... 345 */ 346 347- http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, 348- monitor->encryption, 1, 0, NULL); 349+ if (monitor->nic != NULL) { 350+ http = httpConnect3(monitor->hostname, monitor->port, NULL, AF_UNSPEC, 351+ monitor->encryption, 1, 0, NULL, monitor->nic); 352+ } else { 353+ http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, 354+ monitor->encryption, 1, 0, NULL); 355+ } 356 httpSetTimeout(http, 30.0, timeout_cb, NULL); 357 if (username[0]) 358 cupsSetUser(username); 359@@ -2480,7 +2495,7 @@ monitor_printer( 360 */ 361 362 if (httpGetFd(http) < 0) 363- httpReconnect2(http, 30000, NULL); 364+ monitor->nic != NULL ? httpReconnect3(http, 30000, NULL, monitor->nic) : httpReconnect2(http, 30000, NULL); 365 366 if (httpGetFd(http) >= 0) 367 { 368@@ -2702,7 +2717,7 @@ monitor_printer( 369 if (job_canceled > 0 && monitor->job_id > 0) 370 { 371 if (httpGetFd(http) < 0) 372- httpReconnect2(http, 30000, NULL); 373+ monitor->nic != NULL ? httpReconnect3(http, 30000, NULL, monitor->nic) : httpReconnect2(http, 30000, NULL); 374 375 if (httpGetFd(http) >= 0) 376 { 377diff --git a/tools/ippeveprinter.c b/tools/ippeveprinter.c 378index 2da5ed52..39274b6f 100644 379--- a/tools/ippeveprinter.c 380+++ b/tools/ippeveprinter.c 381@@ -6842,7 +6842,7 @@ process_job(ippeve_job_t *job) /* I - Job */ 382 383 if ((addrlist = httpAddrGetList(host, AF_UNSPEC, service)) == NULL) 384 fprintf(stderr, "[Job %d] Unable to find \"%s\": %s\n", job->id, host, cupsLastErrorString()); 385- else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel))) 386+ else if (!httpAddrConnect2(addrlist, &mystdout, 30000, &(job->cancel), NULL)) 387 fprintf(stderr, "[Job %d] Unable to connect to \"%s\": %s\n", job->id, host, cupsLastErrorString()); 388 389 httpAddrFreeList(addrlist); 390