*
* HTTP routines for CUPS.
*
- * Copyright 2007-2011 by Apple Inc.
+ * Copyright 2007-2012 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* This file contains Kerberos support code, copyright 2006 by
*
* Contents:
*
- * httpAddCredential() - Allocates and adds a single credential to an
- * array.
- * _httpBIOMethods() - Get the OpenSSL BIO methods for HTTP
- * connections.
- * httpBlocking() - Set blocking/non-blocking behavior on a
- * connection.
- * httpCheck() - Check to see if there is a pending response
- * from the server.
- * httpClearCookie() - Clear the cookie value(s).
- * httpClearFields() - Clear HTTP request fields.
- * httpClose() - Close an HTTP connection.
- * httpConnect() - Connect to a HTTP server.
- * httpConnectEncrypt() - Connect to a HTTP server using encryption.
- * httpCopyCredentials() - Copy the credentials associated with an
- * encrypted connection.
- * _httpConvertCredentials() - Convert credentials to the internal format.
- * _httpCreate() - Create an unconnected HTTP connection.
- * httpDelete() - Send a DELETE request to the server.
- * _httpDisconnect() - Disconnect a HTTP connection.
- * httpEncryption() - Set the required encryption on the link.
- * httpError() - Get the last error on a connection.
- * httpFlush() - Flush data from a HTTP connection.
- * httpFlushWrite() - Flush data in write buffer.
- * _httpFreeCredentials() - Free internal credentials.
- * httpFreeCredentials() - Free an array of credentials.
- * httpGet() - Send a GET request to the server.
- * httpGetAuthString() - Get the current authorization string.
- * httpGetBlocking() - Get the blocking/non-block state of a
- * connection.
- * httpGetCookie() - Get any cookie data from the response.
- * httpGetFd() - Get the file descriptor associated with a
- * connection.
- * httpGetField() - Get a field value from a request/response.
- * httpGetLength() - Get the amount of data remaining from the
- * content-length or transfer-encoding fields.
- * httpGetLength2() - Get the amount of data remaining from the
- * content-length or transfer-encoding fields.
- * httpGetStatus() - Get the status of the last HTTP request.
- * httpGetSubField() - Get a sub-field value.
- * httpGetSubField2() - Get a sub-field value.
- * httpGets() - Get a line of text from a HTTP connection.
- * httpHead() - Send a HEAD request to the server.
- * httpInitialize() - Initialize the HTTP interface library and set
- * the default HTTP proxy (if any).
- * httpOptions() - Send an OPTIONS request to the server.
- * _httpPeek() - Peek at data from a HTTP connection.
- * httpPost() - Send a POST request to the server.
- * httpPrintf() - Print a formatted string to a HTTP connection.
- * httpPut() - Send a PUT request to the server.
- * httpRead() - Read data from a HTTP connection.
- * httpRead2() - Read data from a HTTP connection.
- * _httpReadCDSA() - Read function for the CDSA library.
- * _httpReadGNUTLS() - Read function for the GNU TLS library.
- * httpReconnect() - Reconnect to a HTTP server.
- * httpSetAuthString() - Set the current authorization string.
- * httpSetCredentials() - Set the credentials associated with an
- * encrypted connection.
- * httpSetCookie() - Set the cookie value(s).
- * httpSetExpect() - Set the Expect: header in a request.
- * httpSetField() - Set the value of an HTTP header.
- * httpSetLength() - Set the content-length and content-encoding.
- * _httpSetTimeout() - Set read/write timeouts and an optional
- * callback.
- * httpTrace() - Send an TRACE request to the server.
- * _httpUpdate() - Update the current HTTP status for incoming
- * data.
- * httpUpdate() - Update the current HTTP state for incoming
- * data.
- * _httpWait() - Wait for data available on a connection (no
- * flush).
- * httpWait() - Wait for data available on a connection.
- * httpWrite() - Write data to a HTTP connection.
- * httpWrite2() - Write data to a HTTP connection.
- * _httpWriteCDSA() - Write function for the CDSA library.
- * _httpWriteGNUTLS() - Write function for the GNU TLS library.
- * http_bio_ctrl() - Control the HTTP connection.
- * http_bio_free() - Free OpenSSL data.
- * http_bio_new() - Initialize an OpenSSL BIO structure.
- * http_bio_puts() - Send a string for OpenSSL.
- * http_bio_read() - Read data for OpenSSL.
- * http_bio_write() - Write data for OpenSSL.
- * http_debug_hex() - Do a hex dump of a buffer.
- * http_field() - Return the field index for a field name.
- * http_read_ssl() - Read from a SSL/TLS connection.
- * http_locking_cb() - Lock/unlock a thread's mutex.
- * http_send() - Send a request with all fields and the trailing
- * blank line.
- * http_set_credentials() - Set the SSL/TLS credentials.
- * http_setup_ssl() - Set up SSL/TLS support on a connection.
- * http_shutdown_ssl() - Shut down SSL/TLS on a connection.
- * http_threadid_cb() - Return the current thread ID.
- * http_upgrade() - Force upgrade to TLS encryption.
- * http_write() - Write a buffer to a HTTP connection.
- * http_write_chunk() - Write a chunked buffer.
- * http_write_ssl() - Write to a SSL/TLS connection.
+ * httpAddCredential() - Allocates and adds a single credential to an
+ * array.
+ * _httpBIOMethods() - Get the OpenSSL BIO methods for HTTP
+ * connections.
+ * httpBlocking() - Set blocking/non-blocking behavior on a
+ * connection.
+ * httpCheck() - Check to see if there is a pending response
+ * from the server.
+ * httpClearCookie() - Clear the cookie value(s).
+ * httpClearFields() - Clear HTTP request fields.
+ * httpClose() - Close an HTTP connection.
+ * httpConnect() - Connect to a HTTP server.
+ * httpConnectEncrypt() - Connect to a HTTP server using encryption.
+ * httpCopyCredentials() - Copy the credentials associated with an
+ * encrypted connection.
+ * _httpCreate() - Create an unconnected HTTP connection.
+ * _httpCreateCredentials() - Create credentials in the internal format.
+ * httpDelete() - Send a DELETE request to the server.
+ * _httpDisconnect() - Disconnect a HTTP connection.
+ * httpEncryption() - Set the required encryption on the link.
+ * httpError() - Get the last error on a connection.
+ * httpFlush() - Flush data from a HTTP connection.
+ * httpFlushWrite() - Flush data in write buffer.
+ * _httpFreeCredentials() - Free internal credentials.
+ * httpFreeCredentials() - Free an array of credentials.
+ * httpGet() - Send a GET request to the server.
+ * httpGetAuthString() - Get the current authorization string.
+ * httpGetBlocking() - Get the blocking/non-block state of a
+ * connection.
+ * httpGetCookie() - Get any cookie data from the response.
+ * httpGetFd() - Get the file descriptor associated with a
+ * connection.
+ * httpGetField() - Get a field value from a request/response.
+ * httpGetLength() - Get the amount of data remaining from the
+ * content-length or transfer-encoding fields.
+ * httpGetLength2() - Get the amount of data remaining from the
+ * content-length or transfer-encoding fields.
+ * httpGets() - Get a line of text from a HTTP connection.
+ * httpGetState() - Get the current state of the HTTP request.
+ * httpGetStatus() - Get the status of the last HTTP request.
+ * httpGetSubField() - Get a sub-field value.
+ * httpGetSubField2() - Get a sub-field value.
+ * httpGetVersion() - Get the HTTP version at the other end.
+ * httpHead() - Send a HEAD request to the server.
+ * httpInitialize() - Initialize the HTTP interface library and set
+ * the default HTTP proxy (if any).
+ * httpOptions() - Send an OPTIONS request to the server.
+ * _httpPeek() - Peek at data from a HTTP connection.
+ * httpPost() - Send a POST request to the server.
+ * httpPrintf() - Print a formatted string to a HTTP connection.
+ * httpPut() - Send a PUT request to the server.
+ * httpRead() - Read data from a HTTP connection.
+ * httpRead2() - Read data from a HTTP connection.
+ * _httpReadCDSA() - Read function for the CDSA library.
+ * _httpReadGNUTLS() - Read function for the GNU TLS library.
+ * httpReconnect() - Reconnect to a HTTP server.
+ * httpReconnect2() - Reconnect to a HTTP server with timeout and
+ * optional cancel.
+ * httpSetAuthString() - Set the current authorization string.
+ * httpSetCredentials() - Set the credentials associated with an
+ * encrypted connection.
+ * httpSetCookie() - Set the cookie value(s).
+ * httpSetExpect() - Set the Expect: header in a request.
+ * httpSetField() - Set the value of an HTTP header.
+ * httpSetLength() - Set the content-length and content-encoding.
+ * httpSetTimeout() - Set read/write timeouts and an optional
+ * callback.
+ * httpTrace() - Send an TRACE request to the server.
+ * _httpUpdate() - Update the current HTTP status for incoming
+ * data.
+ * httpUpdate() - Update the current HTTP state for incoming
+ * data.
+ * _httpWait() - Wait for data available on a connection (no
+ * flush).
+ * httpWait() - Wait for data available on a connection.
+ * httpWrite() - Write data to a HTTP connection.
+ * httpWrite2() - Write data to a HTTP connection.
+ * _httpWriteCDSA() - Write function for the CDSA library.
+ * _httpWriteGNUTLS() - Write function for the GNU TLS library.
+ * http_bio_ctrl() - Control the HTTP connection.
+ * http_bio_free() - Free OpenSSL data.
+ * http_bio_new() - Initialize an OpenSSL BIO structure.
+ * http_bio_puts() - Send a string for OpenSSL.
+ * http_bio_read() - Read data for OpenSSL.
+ * http_bio_write() - Write data for OpenSSL.
+ * http_debug_hex() - Do a hex dump of a buffer.
+ * http_field() - Return the field index for a field name.
+ * http_read_ssl() - Read from a SSL/TLS connection.
+ * http_send() - Send a request with all fields and the trailing
+ * blank line.
+ * http_set_credentials() - Set the SSL/TLS credentials.
+ * http_set_timeout() - Set the socket timeout values.
+ * http_set_wait() - Set the default wait value for reads.
+ * http_setup_ssl() - Set up SSL/TLS support on a connection.
+ * http_shutdown_ssl() - Shut down SSL/TLS on a connection.
+ * http_upgrade() - Force upgrade to TLS encryption.
+ * http_write() - Write a buffer to a HTTP connection.
+ * http_write_chunk() - Write a chunked buffer.
+ * http_write_ssl() - Write to a SSL/TLS connection.
*/
/*
#include "cups-private.h"
#include <fcntl.h>
+#include <math.h>
#ifdef WIN32
# include <tchar.h>
#else
# include <sys/resource.h>
#endif /* WIN32 */
#ifdef HAVE_POLL
-# include <sys/poll.h>
+# include <poll.h>
#endif /* HAVE_POLL */
-/*
- * Some operating systems have done away with the Fxxxx constants for
- * the fcntl() call; this works around that "feature"...
- */
-
-#ifndef FNONBLK
-# define FNONBLK O_NONBLOCK
-#endif /* !FNONBLK */
-
-
/*
* Local functions...
*/
# if defined(HAVE_CDSASSL) && defined(HAVE_SECCERTIFICATECOPYDATA)
static int http_set_credentials(http_t *http);
# endif /* HAVE_CDSASSL ** HAVE_SECCERTIFICATECOPYDATA */
+#endif /* HAVE_SSL */
+static void http_set_timeout(int fd, double timeout);
+static void http_set_wait(http_t *http);
+#ifdef HAVE_SSL
static int http_setup_ssl(http_t *http);
static void http_shutdown_ssl(http_t *http);
static int http_upgrade(http_t *http);
static int http_write_ssl(http_t *http, const char *buf, int len);
-
-# ifdef HAVE_GNUTLS
-# ifdef HAVE_PTHREAD_H
-GCRY_THREAD_OPTION_PTHREAD_IMPL;
-# endif /* HAVE_PTHREAD_H */
-
-# elif defined(HAVE_LIBSSL)
-static _cups_mutex_t *http_locks; /* OpenSSL lock mutexes */
-
-static void http_locking_cb(int mode, int type, const char *file,
- int line);
-static unsigned long http_threadid_cb(void);
-# endif /* HAVE_GNUTLS */
#endif /* HAVE_SSL */
*
* Use @code cupsArrayNew(NULL, NULL)@ to create a credentials array.
*
- * @since CUPS 1.5@
+ * @since CUPS 1.5/Mac OS X 10.7@
*/
int /* O - 0 on success, -1 on error */
int b) /* I - 1 = blocking, 0 = non-blocking */
{
if (http)
+ {
http->blocking = b;
+ http_set_wait(http);
+ }
}
* 'httpCopyCredentials()' - Copy the credentials associated with an encrypted
* connection.
*
- * @since CUPS 1.5@
+ * @since CUPS 1.5/Mac OS X 10.7@
*/
int /* O - Status of call (0 = success) */
}
-/*
- * '_httpConvertCredentials()' - Convert credentials to the internal format.
- */
-
-http_tls_credentials_t /* O - Internal credentials */
-_httpConvertCredentials(
- cups_array_t *credentials) /* I - Array of credentials */
-{
- if (!credentials)
- return (NULL);
-
-# ifdef HAVE_LIBSSL
- return (NULL);
-
-# elif defined(HAVE_GNUTLS)
- return (NULL);
-
-# elif defined(HAVE_CDSASSL) && defined(HAVE_SECCERTIFICATECOPYDATA)
- CFMutableArrayRef peerCerts; /* Peer credentials reference */
- SecCertificateRef secCert; /* Certificate reference */
- CFDataRef data; /* Credential data reference */
- http_credential_t *credential; /* Credential data */
-
-
- if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
- cupsArrayCount(credentials),
- &kCFTypeArrayCallBacks)) == NULL)
- return (NULL);
-
- for (credential = (http_credential_t *)cupsArrayFirst(credentials);
- credential;
- credential = (http_credential_t *)cupsArrayNext(credentials))
- {
- if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
- credential->datalen)))
- {
- if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
- != NULL)
- {
- CFArrayAppendValue(peerCerts, secCert);
- CFRelease(secCert);
- }
-
- CFRelease(data);
- }
- }
-
- return (peerCerts);
-
-# elif defined(HAVE_SSPISSL)
- return (NULL);
-
-# else
- return (NULL);
-# endif /* HAVE_LIBSSL */
-}
-
-
/*
* '_httpCreate()' - Create an unconnected HTTP connection.
*/
if ((http = calloc(sizeof(http_t), 1)) == NULL)
{
+ _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno), 0);
httpAddrFreeList(addrlist);
return (NULL);
}
else
http->encryption = encryption;
+ http_set_wait(http);
+
/*
* Return the new structure...
*/
}
+/*
+ * '_httpCreateCredentials()' - Create credentials in the internal format.
+ */
+
+http_tls_credentials_t /* O - Internal credentials */
+_httpCreateCredentials(
+ cups_array_t *credentials) /* I - Array of credentials */
+{
+ if (!credentials)
+ return (NULL);
+
+# ifdef HAVE_LIBSSL
+ return (NULL);
+
+# elif defined(HAVE_GNUTLS)
+ return (NULL);
+
+# elif defined(HAVE_CDSASSL) && defined(HAVE_SECCERTIFICATECOPYDATA)
+ CFMutableArrayRef peerCerts; /* Peer credentials reference */
+ SecCertificateRef secCert; /* Certificate reference */
+ CFDataRef data; /* Credential data reference */
+ http_credential_t *credential; /* Credential data */
+
+
+ if ((peerCerts = CFArrayCreateMutable(kCFAllocatorDefault,
+ cupsArrayCount(credentials),
+ &kCFTypeArrayCallBacks)) == NULL)
+ return (NULL);
+
+ for (credential = (http_credential_t *)cupsArrayFirst(credentials);
+ credential;
+ credential = (http_credential_t *)cupsArrayNext(credentials))
+ {
+ if ((data = CFDataCreate(kCFAllocatorDefault, credential->data,
+ credential->datalen)))
+ {
+ if ((secCert = SecCertificateCreateWithData(kCFAllocatorDefault, data))
+ != NULL)
+ {
+ CFArrayAppendValue(peerCerts, secCert);
+ CFRelease(secCert);
+ }
+
+ CFRelease(data);
+ }
+ }
+
+ return (peerCerts);
+
+# elif defined(HAVE_SSPISSL)
+ return (NULL);
+
+# else
+ return (NULL);
+# endif /* HAVE_LIBSSL */
+}
+
+
/*
* 'httpDelete()' - Send a DELETE request to the server.
*/
if (!http)
return (-1);
- if (!strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked"))
+ if (!_cups_strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked"))
{
DEBUG_puts("4httpGetLength2: chunked request!");
/*
- * 'httpGetStatus()' - Get the status of the last HTTP request.
- *
- * @since CUPS 1.2/Mac OS X 10.5@
- */
-
-http_status_t /* O - HTTP status */
-httpGetStatus(http_t *http) /* I - Connection to server */
-{
- return (http ? http->status : HTTP_ERROR);
-}
-
-
-/*
- * 'httpGetSubField()' - Get a sub-field value.
- *
- * @deprecated@
+ * 'httpGets()' - Get a line of text from a HTTP connection.
*/
-char * /* O - Value or NULL */
-httpGetSubField(http_t *http, /* I - Connection to server */
- http_field_t field, /* I - Field index */
- const char *name, /* I - Name of sub-field */
- char *value) /* O - Value string */
+char * /* O - Line or NULL */
+httpGets(char *line, /* I - Line to read into */
+ int length, /* I - Max length of buffer */
+ http_t *http) /* I - Connection to server */
{
- return (httpGetSubField2(http, field, name, value, HTTP_MAX_VALUE));
-}
-
-
-/*
- * 'httpGetSubField2()' - Get a sub-field value.
- *
- * @since CUPS 1.2/Mac OS X 10.5@
- */
+ char *lineptr, /* Pointer into line */
+ *lineend, /* End of line */
+ *bufptr, /* Pointer into input buffer */
+ *bufend; /* Pointer to end of buffer */
+ int bytes, /* Number of bytes read */
+ eol; /* End-of-line? */
-char * /* O - Value or NULL */
-httpGetSubField2(http_t *http, /* I - Connection to server */
- http_field_t field, /* I - Field index */
- const char *name, /* I - Name of sub-field */
- char *value, /* O - Value string */
- int valuelen) /* I - Size of value buffer */
-{
- const char *fptr; /* Pointer into field */
- char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */
- *ptr, /* Pointer into string buffer */
- *end; /* End of value buffer */
- DEBUG_printf(("2httpGetSubField2(http=%p, field=%d, name=\"%s\", value=%p, "
- "valuelen=%d)", http, field, name, value, valuelen));
+ DEBUG_printf(("2httpGets(line=%p, length=%d, http=%p)", line, length, http));
- if (!http || !name || !value || valuelen < 2 ||
- field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX)
+ if (http == NULL || line == NULL)
return (NULL);
- end = value + valuelen - 1;
+ /*
+ * Read a line from the buffer...
+ */
- for (fptr = http->fields[field]; *fptr;)
+ http->error = 0;
+ lineptr = line;
+ lineend = line + length - 1;
+ eol = 0;
+
+ while (lineptr < lineend)
{
/*
- * Skip leading whitespace...
+ * Pre-load the buffer as needed...
*/
- while (_cups_isspace(*fptr))
- fptr ++;
+#ifdef WIN32
+ WSASetLastError(0);
+#else
+ errno = 0;
+#endif /* WIN32 */
- if (*fptr == ',')
+ while (http->used == 0)
{
- fptr ++;
- continue;
- }
+ /*
+ * No newline; see if there is more data to be read...
+ */
- /*
- * Get the sub-field name...
- */
+ while (!_httpWait(http, http->wait_value, 1))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
- for (ptr = temp;
- *fptr && *fptr != '=' && !_cups_isspace(*fptr) &&
- ptr < (temp + sizeof(temp) - 1);
- *ptr++ = *fptr++);
+ DEBUG_puts("3httpGets: Timed out!");
+#ifdef WIN32
+ http->error = WSAETIMEDOUT;
+#else
+ http->error = ETIMEDOUT;
+#endif /* WIN32 */
+ return (NULL);
+ }
- *ptr = '\0';
+#ifdef HAVE_SSL
+ if (http->tls)
+ bytes = http_read_ssl(http, http->buffer + http->used,
+ HTTP_MAX_BUFFER - http->used);
+ else
+#endif /* HAVE_SSL */
+ bytes = recv(http->fd, http->buffer + http->used,
+ HTTP_MAX_BUFFER - http->used, 0);
- DEBUG_printf(("4httpGetSubField2: name=\"%s\"", temp));
+ DEBUG_printf(("4httpGets: read %d bytes...", bytes));
- /*
- * Skip trailing chars up to the '='...
- */
+#ifdef DEBUG
+ http_debug_hex("httpGets", http->buffer + http->used, bytes);
+#endif /* DEBUG */
- while (_cups_isspace(*fptr))
- fptr ++;
+ if (bytes < 0)
+ {
+ /*
+ * Nope, can't get a line this time...
+ */
- if (!*fptr)
- break;
-
- if (*fptr != '=')
- continue;
-
- /*
- * Skip = and leading whitespace...
- */
-
- fptr ++;
-
- while (_cups_isspace(*fptr))
- fptr ++;
-
- if (*fptr == '\"')
- {
- /*
- * Read quoted string...
- */
-
- for (ptr = value, fptr ++;
- *fptr && *fptr != '\"' && ptr < end;
- *ptr++ = *fptr++);
-
- *ptr = '\0';
-
- while (*fptr && *fptr != '\"')
- fptr ++;
-
- if (*fptr)
- fptr ++;
- }
- else
- {
- /*
- * Read unquoted string...
- */
-
- for (ptr = value;
- *fptr && !_cups_isspace(*fptr) && *fptr != ',' && ptr < end;
- *ptr++ = *fptr++);
-
- *ptr = '\0';
-
- while (*fptr && !_cups_isspace(*fptr) && *fptr != ',')
- fptr ++;
- }
-
- DEBUG_printf(("4httpGetSubField2: value=\"%s\"", value));
-
- /*
- * See if this is the one...
- */
-
- if (!strcmp(name, temp))
- {
- DEBUG_printf(("3httpGetSubField2: Returning \"%s\"", value));
- return (value);
- }
- }
-
- value[0] = '\0';
-
- DEBUG_puts("3httpGetSubField2: Returning NULL");
-
- return (NULL);
-}
-
-
-/*
- * 'httpGets()' - Get a line of text from a HTTP connection.
- */
-
-char * /* O - Line or NULL */
-httpGets(char *line, /* I - Line to read into */
- int length, /* I - Max length of buffer */
- http_t *http) /* I - Connection to server */
-{
- char *lineptr, /* Pointer into line */
- *lineend, /* End of line */
- *bufptr, /* Pointer into input buffer */
- *bufend; /* Pointer to end of buffer */
- int bytes, /* Number of bytes read */
- eol; /* End-of-line? */
-
-
- DEBUG_printf(("2httpGets(line=%p, length=%d, http=%p)", line, length, http));
-
- if (http == NULL || line == NULL)
- return (NULL);
-
- /*
- * Read a line from the buffer...
- */
-
- http->error = 0;
- lineptr = line;
- lineend = line + length - 1;
- eol = 0;
-
- while (lineptr < lineend)
- {
- /*
- * Pre-load the buffer as needed...
- */
-
-#ifdef WIN32
- WSASetLastError(0);
-#else
- errno = 0;
-#endif /* WIN32 */
-
- while (http->used == 0)
- {
- /*
- * No newline; see if there is more data to be read...
- */
-
- if (!_httpWait(http, http->blocking ? 30000 : 10000, 1))
- {
- DEBUG_puts("3httpGets: Timed out!");
-#ifdef WIN32
- http->error = WSAETIMEDOUT;
-#else
- http->error = ETIMEDOUT;
-#endif /* WIN32 */
- return (NULL);
- }
-
-#ifdef HAVE_SSL
- if (http->tls)
- bytes = http_read_ssl(http, http->buffer + http->used,
- HTTP_MAX_BUFFER - http->used);
- else
-#endif /* HAVE_SSL */
- bytes = recv(http->fd, http->buffer + http->used,
- HTTP_MAX_BUFFER - http->used, 0);
-
- DEBUG_printf(("4httpGets: read %d bytes...", bytes));
-
- if (bytes < 0)
- {
- /*
- * Nope, can't get a line this time...
- */
-
-#ifdef WIN32
- DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError()));
+#ifdef WIN32
+ DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError()));
if (WSAGetLastError() == WSAEINTR)
continue;
}
+/*
+ * 'httpGetState()' - Get the current state of the HTTP request.
+ */
+
+http_state_t /* O - HTTP state */
+httpGetState(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->state : HTTP_ERROR);
+}
+
+
+/*
+ * 'httpGetStatus()' - Get the status of the last HTTP request.
+ *
+ * @since CUPS 1.2/Mac OS X 10.5@
+ */
+
+http_status_t /* O - HTTP status */
+httpGetStatus(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->status : HTTP_ERROR);
+}
+
+
+/*
+ * 'httpGetSubField()' - Get a sub-field value.
+ *
+ * @deprecated@
+ */
+
+char * /* O - Value or NULL */
+httpGetSubField(http_t *http, /* I - Connection to server */
+ http_field_t field, /* I - Field index */
+ const char *name, /* I - Name of sub-field */
+ char *value) /* O - Value string */
+{
+ return (httpGetSubField2(http, field, name, value, HTTP_MAX_VALUE));
+}
+
+
+/*
+ * 'httpGetSubField2()' - Get a sub-field value.
+ *
+ * @since CUPS 1.2/Mac OS X 10.5@
+ */
+
+char * /* O - Value or NULL */
+httpGetSubField2(http_t *http, /* I - Connection to server */
+ http_field_t field, /* I - Field index */
+ const char *name, /* I - Name of sub-field */
+ char *value, /* O - Value string */
+ int valuelen) /* I - Size of value buffer */
+{
+ const char *fptr; /* Pointer into field */
+ char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */
+ *ptr, /* Pointer into string buffer */
+ *end; /* End of value buffer */
+
+ DEBUG_printf(("2httpGetSubField2(http=%p, field=%d, name=\"%s\", value=%p, "
+ "valuelen=%d)", http, field, name, value, valuelen));
+
+ if (!http || !name || !value || valuelen < 2 ||
+ field <= HTTP_FIELD_UNKNOWN || field >= HTTP_FIELD_MAX)
+ return (NULL);
+
+ end = value + valuelen - 1;
+
+ for (fptr = http->fields[field]; *fptr;)
+ {
+ /*
+ * Skip leading whitespace...
+ */
+
+ while (_cups_isspace(*fptr))
+ fptr ++;
+
+ if (*fptr == ',')
+ {
+ fptr ++;
+ continue;
+ }
+
+ /*
+ * Get the sub-field name...
+ */
+
+ for (ptr = temp;
+ *fptr && *fptr != '=' && !_cups_isspace(*fptr) &&
+ ptr < (temp + sizeof(temp) - 1);
+ *ptr++ = *fptr++);
+
+ *ptr = '\0';
+
+ DEBUG_printf(("4httpGetSubField2: name=\"%s\"", temp));
+
+ /*
+ * Skip trailing chars up to the '='...
+ */
+
+ while (_cups_isspace(*fptr))
+ fptr ++;
+
+ if (!*fptr)
+ break;
+
+ if (*fptr != '=')
+ continue;
+
+ /*
+ * Skip = and leading whitespace...
+ */
+
+ fptr ++;
+
+ while (_cups_isspace(*fptr))
+ fptr ++;
+
+ if (*fptr == '\"')
+ {
+ /*
+ * Read quoted string...
+ */
+
+ for (ptr = value, fptr ++;
+ *fptr && *fptr != '\"' && ptr < end;
+ *ptr++ = *fptr++);
+
+ *ptr = '\0';
+
+ while (*fptr && *fptr != '\"')
+ fptr ++;
+
+ if (*fptr)
+ fptr ++;
+ }
+ else
+ {
+ /*
+ * Read unquoted string...
+ */
+
+ for (ptr = value;
+ *fptr && !_cups_isspace(*fptr) && *fptr != ',' && ptr < end;
+ *ptr++ = *fptr++);
+
+ *ptr = '\0';
+
+ while (*fptr && !_cups_isspace(*fptr) && *fptr != ',')
+ fptr ++;
+ }
+
+ DEBUG_printf(("4httpGetSubField2: value=\"%s\"", value));
+
+ /*
+ * See if this is the one...
+ */
+
+ if (!strcmp(name, temp))
+ {
+ DEBUG_printf(("3httpGetSubField2: Returning \"%s\"", value));
+ return (value);
+ }
+ }
+
+ value[0] = '\0';
+
+ DEBUG_puts("3httpGetSubField2: Returning NULL");
+
+ return (NULL);
+}
+
+
+/*
+ * 'httpGetVersion()' - Get the HTTP version at the other end.
+ */
+
+http_version_t /* O - Version number */
+httpGetVersion(http_t *http) /* I - Connection to server */
+{
+ return (http ? http->version : HTTP_1_0);
+}
+
+
/*
* 'httpHead()' - Send a HEAD request to the server.
*/
#endif /* WIN32 */
#ifdef HAVE_GNUTLS
- /*
- * Make sure we handle threading properly...
- */
-
-# ifdef HAVE_PTHREAD_H
- gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
-# endif /* HAVE_PTHREAD_H */
-
/*
* Initialize GNU TLS...
*/
SSL_load_error_strings();
SSL_library_init();
- /*
- * Set the threading callbacks...
- */
-
- http_locks = calloc(CRYPTO_num_locks(), sizeof(_cups_mutex_t));
-# ifdef HAVE_PTHREAD_H
- for (i = 0; i < CRYPTO_num_locks(); i ++)
- pthread_mutex_init(http_locks + i, NULL);
-# endif /* HAVE_PTHREAD_H */
-
- CRYPTO_set_id_callback(http_threadid_cb);
- CRYPTO_set_locking_callback(http_locking_cb);
-
/*
* Using the current time is a dubious random seed, but on some systems
* it is the best we can do (on others, this seed isn't even used...)
* Buffer small reads for better performance...
*/
- if (!http->blocking && !httpWait(http, 10000))
- return (0);
+ if (!http->blocking)
+ {
+ while (!httpWait(http, http->wait_value))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ return (0);
+ }
+ }
if (http->data_remaining > sizeof(http->buffer))
bytes = sizeof(http->buffer);
* Buffer small reads for better performance...
*/
- if (!http->blocking && !httpWait(http, 10000))
- return (0);
+ ssize_t buflen; /* Length of read for buffer */
+
+ if (!http->blocking)
+ {
+ while (!httpWait(http, http->wait_value))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ return (0);
+ }
+ }
if (http->data_remaining > sizeof(http->buffer))
- bytes = sizeof(http->buffer);
+ buflen = sizeof(http->buffer);
else
- bytes = http->data_remaining;
+ buflen = http->data_remaining;
+
+ DEBUG_printf(("2httpRead2: Reading %d bytes into buffer.", (int)buflen));
+ do
+ {
#ifdef HAVE_SSL
- if (http->tls)
- bytes = http_read_ssl(http, http->buffer, bytes);
- else
+ if (http->tls)
+ bytes = http_read_ssl(http, http->buffer, buflen);
+ else
#endif /* HAVE_SSL */
- {
- DEBUG_printf(("2httpRead2: reading %d bytes from socket into buffer...",
- (int)bytes));
-
- bytes = recv(http->fd, http->buffer, bytes, 0);
+ bytes = recv(http->fd, http->buffer, buflen, 0);
- DEBUG_printf(("2httpRead2: read %d bytes from socket into buffer...",
- (int)bytes));
- }
-
- if (bytes > 0)
- http->used = bytes;
- else if (bytes < 0)
- {
-#ifdef WIN32
- if (WSAGetLastError() != WSAEINTR)
- {
- http->error = WSAGetLastError();
- return (-1);
- }
- else if (WSAGetLastError() == WSAEWOULDBLOCK)
+ if (bytes < 0)
{
- if (!http->timeout_cb || !(*http->timeout_cb)(http, http->timeout_data))
+#ifdef WIN32
+ if (WSAGetLastError() != WSAEINTR)
{
- http->error = WSAEWOULDBLOCK;
+ http->error = WSAGetLastError();
return (-1);
}
- }
+ else if (WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+ if (!http->timeout_cb ||
+ !(*http->timeout_cb)(http, http->timeout_data))
+ {
+ http->error = WSAEWOULDBLOCK;
+ return (-1);
+ }
+ }
#else
- if (errno == EWOULDBLOCK || errno == EAGAIN)
- {
- if (http->timeout_cb && !(*http->timeout_cb)(http, http->timeout_data))
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
{
- http->error = errno;
- return (-1);
+ if (http->timeout_cb && !(*http->timeout_cb)(http, http->timeout_data))
+ {
+ http->error = errno;
+ return (-1);
+ }
+ else if (!http->timeout_cb && errno != EAGAIN)
+ {
+ http->error = errno;
+ return (-1);
+ }
}
- else if (!http->timeout_cb && errno != EAGAIN)
+ else if (errno != EINTR)
{
http->error = errno;
return (-1);
}
- }
- else if (errno != EINTR)
- {
- http->error = errno;
- return (-1);
- }
#endif /* WIN32 */
+ }
}
- else
- {
- http->error = EPIPE;
- return (0);
- }
+ while (bytes < 0);
+
+ DEBUG_printf(("2httpRead2: Read " CUPS_LLFMT " bytes into buffer.",
+ CUPS_LLCAST bytes));
+#ifdef DEBUG
+ http_debug_hex("httpRead2", http->buffer, (int)bytes);
+#endif /* DEBUG */
+
+ http->used = bytes;
}
if (http->used > 0)
#ifdef HAVE_SSL
else if (http->tls)
{
- if (!http->blocking && !httpWait(http, 10000))
- return (0);
+ if (!http->blocking)
+ {
+ while (!httpWait(http, http->wait_value))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ return (0);
+ }
+ }
- bytes = (ssize_t)http_read_ssl(http, buffer, (int)length);
+ while ((bytes = (ssize_t)http_read_ssl(http, buffer, (int)length)) < 0)
+ {
+#ifdef WIN32
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+ if (!http->timeout_cb || !(*http->timeout_cb)(http, http->timeout_data))
+ break;
+ }
+ else if (WSAGetLastError() != WSAEINTR)
+ break;
+#else
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ {
+ if (http->timeout_cb && !(*http->timeout_cb)(http, http->timeout_data))
+ break;
+ else if (!http->timeout_cb && errno != EAGAIN)
+ break;
+ }
+ else if (errno != EINTR)
+ break;
+#endif /* WIN32 */
+ }
}
#endif /* HAVE_SSL */
else
{
- if (!http->blocking && !httpWait(http, 10000))
- return (0);
+ if (!http->blocking)
+ {
+ while (!httpWait(http, http->wait_value))
+ {
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
+ return (0);
+ }
+ }
DEBUG_printf(("2httpRead2: reading " CUPS_LLFMT " bytes from socket...",
CUPS_LLCAST length));
DEBUG_printf(("2httpRead2: read " CUPS_LLFMT " bytes from socket...",
CUPS_LLCAST bytes));
+#ifdef DEBUG
+ http_debug_hex("httpRead2", buffer, (int)bytes);
+#endif /* DEBUG */
}
if (bytes > 0)
}
}
-#ifdef DEBUG
- http_debug_hex("httpRead2", buffer, (int)bytes);
-#endif /* DEBUG */
-
return (bytes);
}
* Make sure we have data before we read...
*/
- if (!_httpWait(http, 10000, 0))
+ while (!_httpWait(http, http->wait_value, 0))
{
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
http->error = ETIMEDOUT;
return (-1);
}
size_t length) /* I - Number of bytes to read */
{
http_t *http; /* HTTP connection */
+ ssize_t bytes; /* Bytes read */
+
+ DEBUG_printf(("6_httpReadGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
http = (http_t *)ptr;
* Make sure we have data before we read...
*/
- if (!_httpWait(http, 10000, 0))
+ while (!_httpWait(http, http->wait_value, 0))
{
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
http->error = ETIMEDOUT;
return (-1);
}
}
- return (recv(http->fd, data, length, 0));
+ bytes = recv(http->fd, data, length, 0);
+ DEBUG_printf(("6_httpReadGNUTLS: bytes=%d", (int)bytes));
+ return (bytes);
}
#endif /* HAVE_SSL && HAVE_GNUTLS */
int /* O - 0 on success, non-zero on failure */
httpReconnect(http_t *http) /* I - Connection to server */
+{
+ DEBUG_printf(("httpReconnect(http=%p)", http));
+
+ return (httpReconnect2(http, 30000, NULL));
+}
+
+
+/*
+ * 'httpReconnect2()' - Reconnect to a HTTP server with timeout and optional
+ * cancel.
+ */
+
+int /* O - 0 on success, non-zero on failure */
+httpReconnect2(http_t *http, /* I - Connection to server */
+ int msec, /* I - Timeout in milliseconds */
+ int *cancel) /* I - Pointer to "cancel" variable */
{
http_addrlist_t *addr; /* Connected address */
#ifdef DEBUG
#endif /* DEBUG */
- DEBUG_printf(("httpReconnect(http=%p)", http));
+ DEBUG_printf(("httpReconnect2(http=%p, msec=%d, cancel=%p)", http, msec,
+ cancel));
if (!http)
+ {
+ _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL), 0);
return (-1);
+ }
#ifdef HAVE_SSL
if (http->tls)
{
- DEBUG_puts("2httpReconnect: Shutting down SSL/TLS...");
+ DEBUG_puts("2httpReconnect2: Shutting down SSL/TLS...");
http_shutdown_ssl(http);
}
#endif /* HAVE_SSL */
if (http->fd >= 0)
{
- DEBUG_printf(("2httpReconnect: Closing socket %d...", http->fd));
+ DEBUG_printf(("2httpReconnect2: Closing socket %d...", http->fd));
#ifdef WIN32
closesocket(http->fd);
#ifdef DEBUG
for (current = http->addrlist; current; current = current->next)
- DEBUG_printf(("2httpReconnect: Address %s:%d",
+ DEBUG_printf(("2httpReconnect2: Address %s:%d",
httpAddrString(&(current->addr), temp, sizeof(temp)),
_httpAddrPort(&(current->addr))));
#endif /* DEBUG */
- if ((addr = httpAddrConnect(http->addrlist, &(http->fd))) == NULL)
+ if ((addr = httpAddrConnect2(http->addrlist, &(http->fd), msec,
+ cancel)) == NULL)
{
/*
* Unable to connect...
#endif /* WIN32 */
http->status = HTTP_ERROR;
- DEBUG_printf(("1httpReconnect: httpAddrConnect failed: %s",
+ DEBUG_printf(("1httpReconnect2: httpAddrConnect failed: %s",
strerror(http->error)));
return (-1);
}
- DEBUG_printf(("2httpReconnect: New socket=%d", http->fd));
+ DEBUG_printf(("2httpReconnect2: New socket=%d", http->fd));
- if (http->timeout_value.tv_sec > 0)
- {
-#ifdef WIN32
- DWORD timeout_value = http->timeout_value.tv_sec * 1000 +
- http->timeout_value.tv_usec / 1000;
- /* Timeout in milliseconds */
-
- setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout_value,
- sizeof(timeout_value));
- setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout_value,
- sizeof(timeout_value));
-#else
- setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, &(http->timeout_value),
- sizeof(http->timeout_value));
- setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, &(http->timeout_value),
- sizeof(http->timeout_value));
-#endif /* WIN32 */
- }
+ if (http->timeout_value > 0)
+ http_set_timeout(http->fd, http->timeout_value);
http->hostaddr = &(addr->addr);
http->error = 0;
http->status = HTTP_CONTINUE;
+ http->state = HTTP_WAITING;
#ifdef HAVE_SSL
if (http->encryption == HTTP_ENCRYPT_ALWAYS)
return (http_upgrade(http));
#endif /* HAVE_SSL */
- DEBUG_printf(("1httpReconnect: Connected to %s:%d...",
+ DEBUG_printf(("1httpReconnect2: Connected to %s:%d...",
httpAddrString(http->hostaddr, temp, sizeof(temp)),
_httpAddrPort(http->hostaddr)));
* 'httpSetCredentials()' - Set the credentials associated with an encrypted
* connection.
*
- * @since CUPS 1.5@
+ * @since CUPS 1.5/Mac OS X 10.7@
*/
int /* O - Status of call (0 = success) */
_httpFreeCredentials(http->tls_credentials);
- http->tls_credentials = _httpConvertCredentials(credentials);
+ http->tls_credentials = _httpCreateCredentials(credentials);
return (http->tls_credentials ? 0 : -1);
}
/*
- * '_httpSetTimeout()' - Set read/write timeouts and an optional callback.
+ * 'httpSetTimeout()' - Set read/write timeouts and an optional callback.
*
* The optional timeout callback receives both the HTTP connection and a user
- * data pointer and must return 1 to continue or 0 to error out.
+ * data pointer and must return 1 to continue or 0 to error (time) out.
+ *
+ * @since CUPS 1.5/Mac OS X 10.7@
*/
void
-_httpSetTimeout(
- http_t *http, /* I - Connection to server */
- double timeout, /* I - Number of seconds for timeout,
+httpSetTimeout(
+ http_t *http, /* I - Connection to server */
+ double timeout, /* I - Number of seconds for timeout,
must be greater than 0 */
- _http_timeout_cb_t cb, /* I - Callback function or NULL */
- void *user_data) /* I - User data pointer */
+ http_timeout_cb_t cb, /* I - Callback function or NULL */
+ void *user_data) /* I - User data pointer */
{
if (!http || timeout <= 0.0)
return;
- http->timeout_cb = cb;
- http->timeout_data = user_data;
- http->timeout_value.tv_sec = (int)timeout;
- http->timeout_value.tv_usec = (int)(timeout * 1000000) % 1000000;
+ http->timeout_cb = cb;
+ http->timeout_data = user_data;
+ http->timeout_value = timeout;
if (http->fd >= 0)
- {
-#ifdef WIN32
- DWORD timeout_value = http->timeout_value.tv_sec * 1000 +
- http->timeout_value.tv_usec / 1000;
- /* Timeout in milliseconds */
-
- setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout_value,
- sizeof(timeout_value));
- setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout_value,
- sizeof(timeout_value));
-#else
- setsockopt(http->fd, SOL_SOCKET, SO_RCVTIMEO, &(http->timeout_value),
- sizeof(http->timeout_value));
- setsockopt(http->fd, SOL_SOCKET, SO_SNDTIMEO, &(http->timeout_value),
- sizeof(http->timeout_value));
-#endif /* WIN32 */
- }
+ http_set_timeout(http->fd, timeout);
+
+ http_set_wait(http);
}
* Be tolerants of servers that send unknown attribute fields...
*/
- if (!strcasecmp(line, "expect"))
+ if (!_cups_strcasecmp(line, "expect"))
{
/*
* "Expect: 100-continue" or similar...
http->expect = (http_status_t)atoi(value);
}
- else if (!strcasecmp(line, "cookie"))
+ else if (!_cups_strcasecmp(line, "cookie"))
{
/*
* "Cookie: name=value[; name=value ...]" - replaces previous cookies...
const void *data, /* I - Data buffer */
size_t length) /* I - Number of bytes to write */
{
- return (send(((http_t *)ptr)->fd, data, length, 0));
+ ssize_t bytes; /* Bytes written */
+
+
+ DEBUG_printf(("6_httpWriteGNUTLS(ptr=%p, data=%p, length=%d)", ptr, data,
+ (int)length));
+#ifdef DEBUG
+ http_debug_hex("_httpWriteGNUTLS", data, (int)length);
+#endif /* DEBUG */
+
+ bytes = send(((http_t *)ptr)->fd, data, length, 0);
+ DEBUG_printf(("_httpWriteGNUTLS: bytes=%d", (int)bytes));
+
+ return (bytes);
}
#endif /* HAVE_SSL && HAVE_GNUTLS */
* Make sure we have data before we read...
*/
- if (!_httpWait(http, 10000, 0))
+ while (!_httpWait(http, http->wait_value, 0))
{
+ if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+ continue;
+
#ifdef WIN32
http->error = WSAETIMEDOUT;
#else
if (_cups_debug_fd < 0 || _cups_debug_level < 6)
return;
- DEBUG_printf(("6%s: %d bytes:\n", prefix, bytes));
+ DEBUG_printf(("6%s: %d bytes:", prefix, bytes));
snprintf(line, sizeof(line), "6%s: ", prefix);
start = line + strlen(line);
for (i = 0; i < HTTP_FIELD_MAX; i ++)
- if (strcasecmp(name, http_fields[i]) == 0)
+ if (_cups_strcasecmp(name, http_fields[i]) == 0)
return ((http_field_t)i);
return (HTTP_FIELD_UNKNOWN);
error = SSLRead(http->tls, buf, len, &processed);
-
+ DEBUG_printf(("6http_read_ssl: error=%d, processed=%d", (int)error,
+ (int)processed));
switch (error)
{
case 0 :
else
{
result = -1;
- errno = EINTR;
+ errno = EINTR;
}
break;
else
{
result = -1;
- errno = EPIPE;
+ errno = EPIPE;
}
break;
}
return (result);
+
# elif defined(HAVE_SSPISSL)
return _sspiRead((_sspi_struct_t*) http->tls, buf, len);
# endif /* HAVE_LIBSSL */
#endif /* HAVE_SSL */
-#ifdef HAVE_LIBSSL
-/*
- * 'http_locking_cb()' - Lock/unlock a thread's mutex.
- */
-
-static void
-http_locking_cb(int mode, /* I - Lock mode */
- int type, /* I - Lock type */
- const char *file, /* I - Source file */
- int line) /* I - Line number */
-{
- if (mode & CRYPTO_LOCK)
- _cupsMutexLock(http_locks + type);
- else
- _cupsMutexUnlock(http_locks + type);
-}
-#endif /* HAVE_LIBSSL */
-
-
/*
* 'http_send()' - Send a request with all fields and the trailing blank line.
*/
return (error);
}
# endif /* HAVE_CDSASSL && HAVE_SECCERTIFICATECOPYDATA */
+#endif /* HAVE_SSL */
+
+
+/*
+ * 'http_set_timeout()' - Set the socket timeout values.
+ */
+
+static void
+http_set_timeout(int fd, /* I - File descriptor */
+ double timeout) /* I - Timeout in seconds */
+{
+#ifdef WIN32
+ DWORD tv = (DWORD)(timeout * 1000);
+ /* Timeout in milliseconds */
+
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
+
+#else
+ struct timeval tv; /* Timeout in secs and usecs */
+
+ tv.tv_sec = (int)timeout;
+ tv.tv_usec = (int)(1000000 * fmod(timeout, 1.0));
+
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+#endif /* WIN32 */
+}
+
+
+/*
+ * 'http_set_wait()' - Set the default wait value for reads.
+ */
+
+static void
+http_set_wait(http_t *http) /* I - Connection to server */
+{
+ if (http->blocking)
+ {
+ http->wait_value = (int)(http->timeout_value * 1000);
+
+ if (http->wait_value <= 0)
+ http->wait_value = 60000;
+ }
+ else
+ http->wait_value = 10000;
+}
+#ifdef HAVE_SSL
/*
* 'http_setup_ssl()' - Set up SSL/TLS support on a connection.
*/
-static int /* O - Status of connection */
+static int /* O - 0 on success, -1 on failure */
http_setup_ssl(http_t *http) /* I - Connection to server */
{
_cups_globals_t *cg = _cupsGlobals();
/* Pointer to library globals */
int any_root; /* Allow any root */
+ char hostname[256], /* Hostname */
+ *hostptr; /* Pointer into hostname */
# ifdef HAVE_LIBSSL
- SSL_CTX *context; /* Context for encryption */
- BIO *bio; /* BIO data */
+ SSL_CTX *context; /* Context for encryption */
+ BIO *bio; /* BIO data */
+ const char *message = NULL;/* Error message */
# elif defined(HAVE_GNUTLS)
+ int status; /* Status of handshake */
gnutls_certificate_client_credentials *credentials;
/* TLS credentials */
# elif defined(HAVE_CDSASSL)
- OSStatus error; /* Error code */
- const char *message = NULL; /* Error message */
- char *hostname; /* Hostname */
+ OSStatus error; /* Error code */
+ const char *message = NULL;/* Error message */
# ifdef HAVE_SECCERTIFICATECOPYDATA
- cups_array_t *credentials; /* Credentials array */
- cups_array_t *names; /* CUPS distinguished names */
- CFArrayRef dn_array; /* CF distinguished names array */
- CFIndex count; /* Number of credentials */
- CFDataRef data; /* Certificate data */
- int i; /* Looping var */
- http_credential_t
- *credential; /* Credential data */
+ cups_array_t *credentials; /* Credentials array */
+ cups_array_t *names; /* CUPS distinguished names */
+ CFArrayRef dn_array; /* CF distinguished names array */
+ CFIndex count; /* Number of credentials */
+ CFDataRef data; /* Certificate data */
+ int i; /* Looping var */
+ http_credential_t *credential; /* Credential data */
# endif /* HAVE_SECCERTIFICATECOPYDATA */
# elif defined(HAVE_SSPISSL)
- TCHAR username[256]; /* Username returned from GetUserName() */
- TCHAR commonName[256]; /* Common name for certificate */
- DWORD dwSize; /* 32 bit size */
+ TCHAR username[256]; /* Username returned from GetUserName() */
+ TCHAR commonName[256];/* Common name for certificate */
+ DWORD dwSize; /* 32 bit size */
# endif /* HAVE_LIBSSL */
*/
if (httpAddrLocalhost(http->hostaddr))
+ {
any_root = 1;
+ strlcpy(hostname, "localhost", sizeof(hostname));
+ }
else
+ {
+ /*
+ * Otherwise use the system-wide setting and make sure the hostname we have
+ * does not end in a trailing dot.
+ */
+
any_root = cg->any_root;
+ strlcpy(hostname, http->hostname, sizeof(hostname));
+ if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
+ *hostptr == '.')
+ *hostptr = '\0';
+ }
+
# ifdef HAVE_LIBSSL
+ (void)any_root;
+
context = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
http->tls = SSL_new(context);
- SSL_set_bio(http->tls_credentials, bio, bio);
+ SSL_set_bio(http->tls, bio, bio);
+
+ SSL_set_tlsext_host_name(http->tls, hostname);
if (SSL_connect(http->tls) != 1)
{
-# ifdef DEBUG
unsigned long error; /* Error code */
while ((error = ERR_get_error()) != 0)
- DEBUG_printf(("8http_setup_ssl: %s", ERR_error_string(error, NULL)));
-# endif /* DEBUG */
+ {
+ message = ERR_error_string(error, NULL);
+ DEBUG_printf(("8http_setup_ssl: %s", message));
+ }
SSL_CTX_free(context);
SSL_free(http->tls);
# endif /* WIN32 */
http->status = HTTP_ERROR;
- return (HTTP_ERROR);
+ if (!message)
+ message = _("Unable to establish a secure connection to host.");
+
+ _cupsSetError(IPP_PKI_ERROR, message, 1);
+
+ return (-1);
}
# elif defined(HAVE_GNUTLS)
+ (void)any_root;
+
credentials = (gnutls_certificate_client_credentials *)
malloc(sizeof(gnutls_certificate_client_credentials));
if (credentials == NULL)
{
- http->error = errno;
+ DEBUG_printf(("8http_setup_ssl: Unable to allocate credentials: %s",
+ strerror(errno)));
+ http->error = errno;
http->status = HTTP_ERROR;
+ _cupsSetHTTPError(HTTP_ERROR);
return (-1);
}
gnutls_init(&http->tls, GNUTLS_CLIENT);
gnutls_set_default_priority(http->tls);
+ gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname,
+ strlen(hostname));
gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr)http);
gnutls_transport_set_pull_function(http->tls, _httpReadGNUTLS);
gnutls_transport_set_push_function(http->tls, _httpWriteGNUTLS);
- if ((gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
+ while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
{
- http->error = errno;
- http->status = HTTP_ERROR;
+ DEBUG_printf(("8http_setup_ssl: gnutls_handshake returned %d (%s)",
+ status, gnutls_strerror(status)));
- gnutls_deinit(http->tls);
- gnutls_certificate_free_credentials(*credentials);
- free(credentials);
- http->tls = NULL;
+ if (gnutls_error_is_fatal(status))
+ {
+ http->error = EIO;
+ http->status = HTTP_ERROR;
- return (-1);
+ _cupsSetError(IPP_PKI_ERROR, gnutls_strerror(status), 0);
+
+ gnutls_deinit(http->tls);
+ gnutls_certificate_free_credentials(*credentials);
+ free(credentials);
+ http->tls = NULL;
+
+ return (-1);
+ }
}
http->tls_credentials = credentials;
# elif defined(HAVE_CDSASSL)
if ((error = SSLNewContext(false, &http->tls)))
{
- http->error = error;
+ http->error = errno;
http->status = HTTP_ERROR;
+ _cupsSetHTTPError(HTTP_ERROR);
return (-1);
}
DEBUG_printf(("4http_setup_ssl: SSLSetIOFuncs, error=%d", (int)error));
}
- if (!error)
- {
- error = SSLSetProtocolVersionEnabled(http->tls, kSSLProtocol2, false);
- DEBUG_printf(("4http_setup_ssl: SSLSetProtocolVersionEnabled, error=%d",
- (int)error));
- }
-
if (!error)
{
error = SSLSetAllowsAnyRoot(http->tls, any_root);
cg->expired_root, (int)error));
}
+# ifdef HAVE_SSLSETPROTOCOLVERSIONMAX
+ if (!error)
+ {
+ error = SSLSetProtocolVersionMax(http->tls, kTLSProtocol1);
+ DEBUG_printf(("4http_setup_ssl: SSLSetProtocolVersionMax(kTLSProtocol1), "
+ "error=%d", (int)error));
+ }
+# endif /* HAVE_SSLSETPROTOCOLVERSIONMAX */
+
+ /*
+ * In general, don't verify certificates since things like the common name
+ * often do not match...
+ */
+
+ if (!error)
+ {
+ error = SSLSetEnableCertVerify(http->tls, false);
+ DEBUG_printf(("4http_setup_ssl: SSLSetEnableCertVerify, error=%d",
+ (int)error));
+ }
+
# ifdef HAVE_SECCERTIFICATECOPYDATA
if (!error)
{
if (!error && cg->server_cert_cb != NULL)
{
- error = SSLSetEnableCertVerify(http->tls, false);
- DEBUG_printf(("4http_setup_ssl: SSLSetEnableCertVerify, error=%d",
- (int)error));
-
- if (!error)
- {
- error = SSLSetSessionOption(http->tls,
- kSSLSessionOptionBreakOnServerAuth, true);
- DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnServerAuth, "
- "error=%d", (int)error));
- }
+ error = SSLSetSessionOption(http->tls,
+ kSSLSessionOptionBreakOnServerAuth, true);
+ DEBUG_printf(("4http_setup_ssl: kSSLSessionOptionBreakOnServerAuth, "
+ "error=%d", (int)error));
}
# endif /* HAVE_SECCERTIFICATECOPYDATA */
+ /*
+ * Let the server know which hostname/domain we are trying to connect to
+ * in case it wants to serve up a certificate with a matching common name.
+ */
+
if (!error)
{
- hostname = httpAddrLocalhost(http->hostaddr) ? "localhost" : http->hostname;
- error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
+ error = SSLSetPeerDomainName(http->tls, hostname, strlen(hostname));
DEBUG_printf(("4http_setup_ssl: SSLSetPeerDomainName, error=%d",
(int)error));
{
error = SSLHandshake(http->tls);
- DEBUG_printf(("4_httpWait: SSLHandshake returned %d.", (int)error));
+ DEBUG_printf(("4http_setup_ssl: SSLHandshake returned %d.", (int)error));
switch (error)
{
httpFreeCredentials(credentials);
}
- DEBUG_printf(("4_httpWait: Server certificate callback returned "
- "%d.", (int)error));
+ DEBUG_printf(("4http_setup_ssl: Server certificate callback "
+ "returned %d.", (int)error));
}
break;
error = (cg->client_cert_cb)(http, http->tls, names,
cg->client_cert_data);
- DEBUG_printf(("4_httpWait: Client certificate callback "
+ DEBUG_printf(("4http_setup_ssl: Client certificate callback "
"returned %d.", (int)error));
}
http->tls = _sspiAlloc();
if (!http->tls)
+ {
+ _cupsSetHTTPError(HTTP_ERROR);
return (-1);
+ }
http->tls->sock = http->fd;
dwSize = sizeof(username) / sizeof(TCHAR);
{
_sspiFree(http->tls_credentials);
http->tls_credentials = NULL;
+
+ http->error = EIO;
+ http->status = HTTP_ERROR;
+
+ _cupsSetError(IPP_PKI_ERROR,
+ _("Unable to establish a secure connection to host."), 1);
+
return (-1);
}
- _sspiSetAllowsAnyRoot(http->tls_credentials, TRUE);
+ _sspiSetAllowsAnyRoot(http->tls_credentials, any_root);
_sspiSetAllowsExpiredCerts(http->tls_credentials, TRUE);
- if (!_sspiConnect(http->tls_credentials, http->hostname))
+ if (!_sspiConnect(http->tls_credentials, hostname))
{
_sspiFree(http->tls_credentials);
http->tls_credentials = NULL;
+
+ http->error = EIO;
+ http->status = HTTP_ERROR;
+
+ _cupsSetError(IPP_PKI_ERROR,
+ _("Unable to establish a secure connection to host."), 1);
+
return (-1);
}
# endif /* HAVE_CDSASSL */
return (0);
}
-#endif /* HAVE_SSL */
-#ifdef HAVE_SSL
/*
* 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection.
*/
# ifdef HAVE_LIBSSL
SSL_CTX *context; /* Context for encryption */
- context = SSL_get_SSL_CTX(http->tls_credentials);
+ context = SSL_get_SSL_CTX(http->tls);
- SSL_shutdown(http->tls_credentials);
+ SSL_shutdown(http->tls);
SSL_CTX_free(context);
- SSL_free(http->tls_credentials);
+ SSL_free(http->tls);
# elif defined(HAVE_GNUTLS)
gnutls_certificate_client_credentials *credentials;
#endif /* HAVE_SSL */
-#ifdef HAVE_LIBSSL
-/*
- * 'http_threadid_cb()' - Return the current thread ID.
- */
-
-static unsigned long /* O - Thread ID */
-http_threadid_cb(void)
-{
-# ifdef HAVE_PTHREAD_H
- return ((unsigned long)pthread_self());
-# else
- return (0);
-# endif /* HAVE_PTHREAD_H */
-}
-#endif /* HAVE_LIBSSL */
-
-
#ifdef HAVE_SSL
/*
* 'http_upgrade()' - Force upgrade to TLS encryption.
while (length > 0)
{
+ DEBUG_printf(("3http_write: About to write %d bytes.", (int)length));
+
+ if (http->timeout_cb)
+ {
+#ifdef HAVE_POLL
+ struct pollfd pfd; /* Polled file descriptor */
+#else
+ fd_set output_set; /* Output ready for write? */
+ struct timeval timeout; /* Timeout value */
+#endif /* HAVE_POLL */
+ int nfds; /* Result from select()/poll() */
+
+ do
+ {
+#ifdef HAVE_POLL
+ pfd.fd = http->fd;
+ pfd.events = POLLOUT;
+
+ while ((nfds = poll(&pfd, 1, http->wait_value)) < 0 &&
+ (errno == EINTR || errno == EAGAIN));
+
+#else
+ do
+ {
+ FD_ZERO(&output_set);
+ FD_SET(http->fd, &output_set);
+
+ timeout.tv_sec = http->wait_value / 1000;
+ timeout.tv_usec = 1000 * (http->wait_value % 1000);
+
+ nfds = select(http->fd + 1, NULL, &output_set, NULL, &timeout);
+ }
+# ifdef WIN32
+ while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
+ WSAGetLastError() == WSAEWOULDBLOCK));
+# else
+ while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
+# endif /* WIN32 */
+#endif /* HAVE_POLL */
+
+ if (nfds < 0)
+ {
+ http->error = errno;
+ return (-1);
+ }
+ else if (nfds == 0 && !(*http->timeout_cb)(http, http->timeout_data))
+ {
+#ifdef WIN32
+ http->error = WSAEWOULDBLOCK;
+#else
+ http->error = EWOULDBLOCK;
+#endif /* WIN32 */
+ return (-1);
+ }
+ }
+ while (nfds <= 0);
+ }
+
#ifdef HAVE_SSL
if (http->tls)
bytes = http_write_ssl(http, buffer, length);
#endif /* HAVE_SSL */
bytes = send(http->fd, buffer, length, 0);
+ DEBUG_printf(("3http_write: Write of %d bytes returned %d.", (int)length,
+ (int)bytes));
+
if (bytes < 0)
{
#ifdef WIN32