]>
Commit | Line | Data |
---|---|---|
3840d6ba | 1 | /* |
6269b3e2 | 2 | * "$Id: http.c,v 1.138 2004/06/29 01:02:45 mike Exp $" |
3840d6ba | 3 | * |
1c1de3d7 | 4 | * HTTP routines for the Common UNIX Printing System (CUPS). |
fd8b1cf8 | 5 | * |
71649dc9 | 6 | * Copyright 1997-2004 by Easy Software Products, all rights reserved. |
fd8b1cf8 | 7 | * |
b38fb7fc | 8 | * These coded instructions, statements, and computer programs are the |
fd8b1cf8 | 9 | * property of Easy Software Products and are protected by Federal |
10 | * copyright law. Distribution and use rights are outlined in the file | |
11 | * "LICENSE.txt" which should have been included with this file. If this | |
12 | * file is missing or damaged please contact Easy Software Products | |
13 | * at: | |
14 | * | |
15 | * Attn: CUPS Licensing Information | |
16 | * Easy Software Products | |
8784b6a6 | 17 | * 44141 Airport View Drive, Suite 204 |
fd8b1cf8 | 18 | * Hollywood, Maryland 20636-3111 USA |
19 | * | |
20 | * Voice: (301) 373-9603 | |
21 | * EMail: cups-info@cups.org | |
22 | * WWW: http://www.cups.org | |
3840d6ba | 23 | * |
34410ef2 | 24 | * This file is subject to the Apple OS-Developed Software exception. |
25 | * | |
3840d6ba | 26 | * Contents: |
27 | * | |
a1793153 | 28 | * httpInitialize() - Initialize the HTTP interface library and set the |
29 | * default HTTP proxy (if any). | |
30 | * httpCheck() - Check to see if there is a pending response from | |
31 | * the server. | |
4a8a506d | 32 | * httpClearCookie() - Clear the cookie value(s). |
a1793153 | 33 | * httpClose() - Close an HTTP connection... |
34 | * httpConnect() - Connect to a HTTP server. | |
35 | * httpConnectEncrypt() - Connect to a HTTP server using encryption. | |
36 | * httpEncryption() - Set the required encryption on the link. | |
37 | * httpReconnect() - Reconnect to a HTTP server... | |
83a2ea50 | 38 | * httpGetSubField() - Get a sub-field value. |
a1793153 | 39 | * httpSetField() - Set the value of an HTTP header. |
40 | * httpDelete() - Send a DELETE request to the server. | |
41 | * httpGet() - Send a GET request to the server. | |
42 | * httpHead() - Send a HEAD request to the server. | |
43 | * httpOptions() - Send an OPTIONS request to the server. | |
44 | * httpPost() - Send a POST request to the server. | |
45 | * httpPut() - Send a PUT request to the server. | |
46 | * httpTrace() - Send an TRACE request to the server. | |
47 | * httpFlush() - Flush data from a HTTP connection. | |
48 | * httpRead() - Read data from a HTTP connection. | |
4a8a506d | 49 | * httpSetCookie() - Set the cookie value(s)... |
aa73fd19 | 50 | * httpWait() - Wait for data available on a connection. |
a1793153 | 51 | * httpWrite() - Write data to a HTTP connection. |
52 | * httpGets() - Get a line of text from a HTTP connection. | |
53 | * httpPrintf() - Print a formatted string to a HTTP connection. | |
a1793153 | 54 | * httpGetDateString() - Get a formatted date/time string from a time value. |
55 | * httpGetDateTime() - Get a time value from a formatted date/time string. | |
56 | * httpUpdate() - Update the current HTTP state for incoming data. | |
57 | * httpDecode64() - Base64-decode a string. | |
58 | * httpEncode64() - Base64-encode a string. | |
59 | * httpGetLength() - Get the amount of data remaining from the | |
60 | * content-length or transfer-encoding fields. | |
61 | * http_field() - Return the field index for a field name. | |
62 | * http_send() - Send a request with all fields and the trailing | |
63 | * blank line. | |
94cf7749 | 64 | * http_wait() - Wait for data available on a connection. |
a1793153 | 65 | * http_upgrade() - Force upgrade to TLS encryption. |
2dac9564 | 66 | * http_setup_ssl() - Set up SSL/TLS on a connection. |
67 | * http_shutdown_ssl() - Shut down SSL/TLS on a connection. | |
68 | * http_read_ssl() - Read from a SSL/TLS connection. | |
69 | * http_write_ssl() - Write to a SSL/TLS connection. | |
6fe7f729 | 70 | * CDSAReadFunc() - Read function for CDSA decryption code. |
71 | * CDSAWriteFunc() - Write function for CDSA encryption code. | |
3840d6ba | 72 | */ |
73 | ||
74 | /* | |
75 | * Include necessary headers... | |
76 | */ | |
77 | ||
bdbf4417 | 78 | #include "http-private.h" |
79 | ||
3b960317 | 80 | #include <stdio.h> |
81 | #include <stdlib.h> | |
3840d6ba | 82 | #include <stdarg.h> |
3b960317 | 83 | #include <ctype.h> |
84 | #include "string.h" | |
3b960317 | 85 | #include <fcntl.h> |
4e8f6c6a | 86 | #include <errno.h> |
992cf15a | 87 | |
3b960317 | 88 | #include "http.h" |
4a73831b | 89 | #include "debug.h" |
f4cafe91 | 90 | |
3620d932 | 91 | #ifndef WIN32 |
5356dc5a | 92 | # include <signal.h> |
379ed99b | 93 | # include <sys/time.h> |
94 | # include <sys/resource.h> | |
3620d932 | 95 | #endif /* !WIN32 */ |
5356dc5a | 96 | |
a75c006a | 97 | |
42d48bd2 | 98 | /* |
99 | * Some operating systems have done away with the Fxxxx constants for | |
100 | * the fcntl() call; this works around that "feature"... | |
101 | */ | |
102 | ||
103 | #ifndef FNONBLK | |
104 | # define FNONBLK O_NONBLOCK | |
105 | #endif /* !FNONBLK */ | |
106 | ||
992cf15a | 107 | |
108 | /* | |
3a193f5e | 109 | * Local functions... |
992cf15a | 110 | */ |
111 | ||
063e1ac7 | 112 | static http_field_t http_field(const char *name); |
113 | static int http_send(http_t *http, http_state_t request, | |
114 | const char *uri); | |
94cf7749 | 115 | static int http_wait(http_t *http, int msec); |
2dac9564 | 116 | #ifdef HAVE_SSL |
2a0ef17a | 117 | static int http_upgrade(http_t *http); |
2dac9564 | 118 | static int http_setup_ssl(http_t *http); |
119 | static void http_shutdown_ssl(http_t *http); | |
120 | static int http_read_ssl(http_t *http, char *buf, int len); | |
121 | static int http_write_ssl(http_t *http, const char *buf, int len); | |
6fe7f729 | 122 | # ifdef HAVE_CDSASSL |
123 | static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data, size_t *dataLength); | |
124 | static OSStatus CDSAWriteFunc(SSLConnectionRef connection, const void *data, size_t *dataLength); | |
125 | # endif /* HAVE_CDSASSL */ | |
2dac9564 | 126 | #endif /* HAVE_SSL */ |
3840d6ba | 127 | |
128 | ||
129 | /* | |
3a193f5e | 130 | * Local globals... |
3840d6ba | 131 | */ |
132 | ||
19baf4b4 | 133 | static const char * const http_fields[] = |
3a193f5e | 134 | { |
3a193f5e | 135 | "Accept-Language", |
0542e38e | 136 | "Accept-Ranges", |
3a193f5e | 137 | "Authorization", |
3a193f5e | 138 | "Connection", |
3a193f5e | 139 | "Content-Encoding", |
140 | "Content-Language", | |
141 | "Content-Length", | |
142 | "Content-Location", | |
143 | "Content-MD5", | |
144 | "Content-Range", | |
145 | "Content-Type", | |
0d1f75a3 | 146 | "Content-Version", |
3a193f5e | 147 | "Date", |
3a193f5e | 148 | "Host", |
3a193f5e | 149 | "If-Modified-Since", |
3a193f5e | 150 | "If-Unmodified-since", |
0d1f75a3 | 151 | "Keep-Alive", |
3a193f5e | 152 | "Last-Modified", |
0d1f75a3 | 153 | "Link", |
3a193f5e | 154 | "Location", |
3a193f5e | 155 | "Range", |
156 | "Referer", | |
157 | "Retry-After", | |
3a193f5e | 158 | "Transfer-Encoding", |
159 | "Upgrade", | |
160 | "User-Agent", | |
3a193f5e | 161 | "WWW-Authenticate" |
162 | }; | |
19baf4b4 | 163 | static const char * const days[7] = |
3a193f5e | 164 | { |
165 | "Sun", | |
166 | "Mon", | |
167 | "Tue", | |
168 | "Wed", | |
169 | "Thu", | |
170 | "Fri", | |
171 | "Sat" | |
172 | }; | |
19baf4b4 | 173 | static const char * const months[12] = |
3a193f5e | 174 | { |
175 | "Jan", | |
176 | "Feb", | |
177 | "Mar", | |
178 | "Apr", | |
179 | "May", | |
180 | "Jun", | |
181 | "Jul", | |
182 | "Aug", | |
183 | "Sep", | |
184 | "Oct", | |
185 | "Nov", | |
186 | "Dec" | |
187 | }; | |
3840d6ba | 188 | |
189 | ||
9cba63f2 | 190 | /* |
191 | * 'httpInitialize()' - Initialize the HTTP interface library and set the | |
192 | * default HTTP proxy (if any). | |
193 | */ | |
194 | ||
195 | void | |
0542e38e | 196 | httpInitialize(void) |
9cba63f2 | 197 | { |
a75c006a | 198 | #ifdef HAVE_LIBSSL |
6cb0970f | 199 | # ifndef WIN32 |
a75c006a | 200 | struct timeval curtime; /* Current time in microseconds */ |
4e71ebd8 | 201 | # endif /* !WIN32 */ |
1c6682dd | 202 | int i; /* Looping var */ |
203 | unsigned char data[1024]; /* Seed data */ | |
a75c006a | 204 | #endif /* HAVE_LIBSSL */ |
205 | ||
3620d932 | 206 | #ifdef WIN32 |
7d34f475 | 207 | WSADATA winsockdata; /* WinSock data */ |
208 | static int initialized = 0; /* Has WinSock been initialized? */ | |
50146867 | 209 | |
210 | ||
211 | if (!initialized) | |
212 | WSAStartup(MAKEWORD(1,1), &winsockdata); | |
ca7e65c8 | 213 | #elif defined(HAVE_SIGSET) |
214 | sigset(SIGPIPE, SIG_IGN); | |
215 | #elif defined(HAVE_SIGACTION) | |
7d34f475 | 216 | struct sigaction action; /* POSIX sigaction data */ |
2bffb563 | 217 | |
218 | ||
219 | /* | |
220 | * Ignore SIGPIPE signals... | |
221 | */ | |
222 | ||
223 | memset(&action, 0, sizeof(action)); | |
224 | action.sa_handler = SIG_IGN; | |
225 | sigaction(SIGPIPE, &action, NULL); | |
ca7e65c8 | 226 | #else |
227 | signal(SIGPIPE, SIG_IGN); | |
3620d932 | 228 | #endif /* WIN32 */ |
a75c006a | 229 | |
2dac9564 | 230 | #ifdef HAVE_GNUTLS |
231 | gnutls_global_init(); | |
232 | #endif /* HAVE_GNUTLS */ | |
233 | ||
a75c006a | 234 | #ifdef HAVE_LIBSSL |
1c6682dd | 235 | SSL_load_error_strings(); |
a75c006a | 236 | SSL_library_init(); |
237 | ||
238 | /* | |
239 | * Using the current time is a dubious random seed, but on some systems | |
240 | * it is the best we can do (on others, this seed isn't even used...) | |
241 | */ | |
242 | ||
6cb0970f | 243 | #ifdef WIN32 |
244 | #else | |
a75c006a | 245 | gettimeofday(&curtime, NULL); |
1c6682dd | 246 | srand(curtime.tv_sec + curtime.tv_usec); |
4e71ebd8 | 247 | #endif /* WIN32 */ |
1c6682dd | 248 | |
249 | for (i = 0; i < sizeof(data); i ++) | |
250 | data[i] = rand(); /* Yes, this is a poor source of random data... */ | |
251 | ||
252 | RAND_seed(&data, sizeof(data)); | |
a75c006a | 253 | #endif /* HAVE_LIBSSL */ |
50146867 | 254 | } |
255 | ||
256 | ||
c1918ec5 | 257 | /* |
258 | * 'httpCheck()' - Check to see if there is a pending response from the server. | |
259 | */ | |
260 | ||
261 | int /* O - 0 = no data, 1 = data available */ | |
262 | httpCheck(http_t *http) /* I - HTTP connection */ | |
263 | { | |
aa73fd19 | 264 | return (httpWait(http, 0)); |
c1918ec5 | 265 | } |
266 | ||
267 | ||
4a8a506d | 268 | /* |
269 | * 'httpClearCookie()' - Clear the cookie value(s). | |
270 | */ | |
271 | ||
272 | void | |
273 | httpClearCookie(http_t *http) /* I - Connection */ | |
274 | { | |
275 | if (!http) | |
276 | return; | |
277 | ||
278 | if (http->cookie) | |
279 | { | |
280 | free(http->cookie); | |
281 | http->cookie = NULL; | |
282 | } | |
283 | } | |
284 | ||
285 | ||
50146867 | 286 | /* |
287 | * 'httpClose()' - Close an HTTP connection... | |
288 | */ | |
289 | ||
290 | void | |
291 | httpClose(http_t *http) /* I - Connection to close */ | |
292 | { | |
6269b3e2 | 293 | DEBUG_printf(("httpClose(http=%p)\n", http)); |
294 | ||
2a0ef17a | 295 | if (!http) |
50146867 | 296 | return; |
297 | ||
aa73fd19 | 298 | if (http->input_set) |
299 | free(http->input_set); | |
300 | ||
4a8a506d | 301 | if (http->cookie) |
302 | free(http->cookie); | |
303 | ||
2dac9564 | 304 | #ifdef HAVE_SSL |
2a0ef17a | 305 | if (http->tls) |
2dac9564 | 306 | http_shutdown_ssl(http); |
307 | #endif /* HAVE_SSL */ | |
2a0ef17a | 308 | |
50146867 | 309 | #ifdef WIN32 |
310 | closesocket(http->fd); | |
311 | #else | |
312 | close(http->fd); | |
313 | #endif /* WIN32 */ | |
314 | ||
315 | free(http); | |
9cba63f2 | 316 | } |
317 | ||
318 | ||
3840d6ba | 319 | /* |
50146867 | 320 | * 'httpConnect()' - Connect to a HTTP server. |
3840d6ba | 321 | */ |
322 | ||
50146867 | 323 | http_t * /* O - New HTTP connection */ |
063e1ac7 | 324 | httpConnect(const char *host, /* I - Host to connect to */ |
325 | int port) /* I - Port number */ | |
a1793153 | 326 | { |
327 | http_encryption_t encrypt;/* Type of encryption to use */ | |
328 | ||
329 | ||
330 | /* | |
331 | * Set the default encryption status... | |
332 | */ | |
333 | ||
334 | if (port == 443) | |
335 | encrypt = HTTP_ENCRYPT_ALWAYS; | |
336 | else | |
337 | encrypt = HTTP_ENCRYPT_IF_REQUESTED; | |
338 | ||
339 | return (httpConnectEncrypt(host, port, encrypt)); | |
340 | } | |
341 | ||
342 | ||
343 | /* | |
344 | * 'httpConnectEncrypt()' - Connect to a HTTP server using encryption. | |
345 | */ | |
346 | ||
347 | http_t * /* O - New HTTP connection */ | |
348 | httpConnectEncrypt(const char *host, /* I - Host to connect to */ | |
349 | int port, /* I - Port number */ | |
350 | http_encryption_t encrypt) | |
351 | /* I - Type of encryption to use */ | |
3840d6ba | 352 | { |
0e0ac99a | 353 | int i; /* Looping var */ |
50146867 | 354 | http_t *http; /* New HTTP connection */ |
355 | struct hostent *hostaddr; /* Host address data */ | |
356 | ||
357 | ||
6269b3e2 | 358 | DEBUG_printf(("httpConnectEncrypt(host=\"%s\", port=%d, encrypt=%d)\n", |
359 | host ? host : "(null)", port, encrypt)); | |
360 | ||
361 | if (!host) | |
db8c0a8c | 362 | return (NULL); |
363 | ||
0b83a6c8 | 364 | httpInitialize(); |
365 | ||
50146867 | 366 | /* |
367 | * Lookup the host... | |
368 | */ | |
369 | ||
0e0ac99a | 370 | if ((hostaddr = httpGetHostByName(host)) == NULL) |
627f4602 | 371 | { |
372 | /* | |
373 | * This hack to make users that don't have a localhost entry in | |
374 | * their hosts file or DNS happy... | |
375 | */ | |
376 | ||
377 | if (strcasecmp(host, "localhost") != 0) | |
378 | return (NULL); | |
0e0ac99a | 379 | else if ((hostaddr = httpGetHostByName("127.0.0.1")) == NULL) |
627f4602 | 380 | return (NULL); |
381 | } | |
3840d6ba | 382 | |
7e9dc5e9 | 383 | /* |
384 | * Verify that it is an IPv4 address (IPv6 support will come in CUPS 1.2...) | |
385 | */ | |
386 | ||
387 | if (hostaddr->h_addrtype != AF_INET || hostaddr->h_length != 4) | |
388 | return (NULL); | |
389 | ||
3840d6ba | 390 | /* |
3a193f5e | 391 | * Allocate memory for the structure... |
3840d6ba | 392 | */ |
393 | ||
3a193f5e | 394 | http = calloc(sizeof(http_t), 1); |
395 | if (http == NULL) | |
396 | return (NULL); | |
3840d6ba | 397 | |
a8374e1e | 398 | http->version = HTTP_1_1; |
0542e38e | 399 | http->blocking = 1; |
3a193f5e | 400 | http->activity = time(NULL); |
7e9dc5e9 | 401 | http->fd = -1; |
3840d6ba | 402 | |
403 | /* | |
50146867 | 404 | * Copy the hostname and port and then "reconnect"... |
3840d6ba | 405 | */ |
406 | ||
17438bf4 | 407 | strlcpy(http->hostname, host, sizeof(http->hostname)); |
50146867 | 408 | http->hostaddr.sin_family = hostaddr->h_addrtype; |
2456b740 | 409 | #ifdef WIN32 |
410 | http->hostaddr.sin_port = htons((u_short)port); | |
411 | #else | |
0542e38e | 412 | http->hostaddr.sin_port = htons(port); |
2456b740 | 413 | #endif /* WIN32 */ |
a75c006a | 414 | |
415 | /* | |
a1793153 | 416 | * Set the encryption status... |
a75c006a | 417 | */ |
418 | ||
9075313d | 419 | if (port == 443) /* Always use encryption for https */ |
420 | http->encryption = HTTP_ENCRYPT_ALWAYS; | |
421 | else | |
422 | http->encryption = encrypt; | |
a75c006a | 423 | |
424 | /* | |
0e0ac99a | 425 | * Loop through the addresses we have until one of them connects... |
a75c006a | 426 | */ |
427 | ||
17438bf4 | 428 | strlcpy(http->hostname, host, sizeof(http->hostname)); |
90c2431b | 429 | |
0e0ac99a | 430 | for (i = 0; hostaddr->h_addr_list[i]; i ++) |
3840d6ba | 431 | { |
0e0ac99a | 432 | /* |
433 | * Load the address... | |
434 | */ | |
435 | ||
90c2431b | 436 | memcpy((char *)&(http->hostaddr.sin_addr), hostaddr->h_addr_list[i], |
437 | hostaddr->h_length); | |
0e0ac99a | 438 | |
439 | /* | |
440 | * Connect to the remote system... | |
441 | */ | |
442 | ||
443 | if (!httpReconnect(http)) | |
444 | return (http); | |
3840d6ba | 445 | } |
0e0ac99a | 446 | |
447 | /* | |
448 | * Could not connect to any known address - bail out! | |
449 | */ | |
450 | ||
451 | free(http); | |
452 | return (NULL); | |
3a193f5e | 453 | } |
3840d6ba | 454 | |
3840d6ba | 455 | |
b38fb7fc | 456 | /* |
457 | * 'httpEncryption()' - Set the required encryption on the link. | |
458 | */ | |
459 | ||
460 | int /* O - -1 on error, 0 on success */ | |
461 | httpEncryption(http_t *http, /* I - HTTP data */ | |
462 | http_encryption_t e) /* I - New encryption preference */ | |
463 | { | |
f992395d | 464 | DEBUG_printf(("httpEncryption(http=%p, e=%d)\n", http, e)); |
465 | ||
2dac9564 | 466 | #ifdef HAVE_SSL |
b38fb7fc | 467 | if (!http) |
2a0ef17a | 468 | return (0); |
b38fb7fc | 469 | |
470 | http->encryption = e; | |
471 | ||
472 | if ((http->encryption == HTTP_ENCRYPT_ALWAYS && !http->tls) || | |
473 | (http->encryption == HTTP_ENCRYPT_NEVER && http->tls)) | |
474 | return (httpReconnect(http)); | |
2a0ef17a | 475 | else if (http->encryption == HTTP_ENCRYPT_REQUIRED && !http->tls) |
476 | return (http_upgrade(http)); | |
477 | else | |
478 | return (0); | |
479 | #else | |
480 | if (e == HTTP_ENCRYPT_ALWAYS || e == HTTP_ENCRYPT_REQUIRED) | |
481 | return (-1); | |
b38fb7fc | 482 | else |
483 | return (0); | |
2dac9564 | 484 | #endif /* HAVE_SSL */ |
b38fb7fc | 485 | } |
486 | ||
487 | ||
3a193f5e | 488 | /* |
50146867 | 489 | * 'httpReconnect()' - Reconnect to a HTTP server... |
3a193f5e | 490 | */ |
3840d6ba | 491 | |
50146867 | 492 | int /* O - 0 on success, non-zero on failure */ |
493 | httpReconnect(http_t *http) /* I - HTTP data */ | |
3a193f5e | 494 | { |
a75c006a | 495 | int val; /* Socket option value */ |
3840d6ba | 496 | |
f992395d | 497 | |
498 | DEBUG_printf(("httpReconnect(http=%p)\n", http)); | |
499 | ||
500 | if (!http) | |
501 | return (-1); | |
502 | ||
2dac9564 | 503 | #ifdef HAVE_SSL |
a75c006a | 504 | if (http->tls) |
2dac9564 | 505 | http_shutdown_ssl(http); |
506 | #endif /* HAVE_SSL */ | |
a75c006a | 507 | |
50146867 | 508 | /* |
509 | * Close any previously open socket... | |
510 | */ | |
511 | ||
7e9dc5e9 | 512 | if (http->fd >= 0) |
3a193f5e | 513 | #ifdef WIN32 |
50146867 | 514 | closesocket(http->fd); |
3a193f5e | 515 | #else |
50146867 | 516 | close(http->fd); |
3a193f5e | 517 | #endif /* WIN32 */ |
3840d6ba | 518 | |
50146867 | 519 | /* |
520 | * Create the socket and set options to allow reuse. | |
521 | */ | |
3840d6ba | 522 | |
900b10ea | 523 | if ((http->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
0bf5ae82 | 524 | { |
3620d932 | 525 | #ifdef WIN32 |
977acbd3 | 526 | http->error = WSAGetLastError(); |
527 | #else | |
f44afac9 | 528 | http->error = errno; |
3620d932 | 529 | #endif /* WIN32 */ |
f44afac9 | 530 | http->status = HTTP_ERROR; |
50146867 | 531 | return (-1); |
0bf5ae82 | 532 | } |
fd8b1cf8 | 533 | |
50146867 | 534 | #ifdef FD_CLOEXEC |
535 | fcntl(http->fd, F_SETFD, FD_CLOEXEC); /* Close this socket when starting * | |
536 | * other processes... */ | |
537 | #endif /* FD_CLOEXEC */ | |
fd8b1cf8 | 538 | |
50146867 | 539 | val = 1; |
2456b740 | 540 | setsockopt(http->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); |
3840d6ba | 541 | |
50146867 | 542 | #ifdef SO_REUSEPORT |
543 | val = 1; | |
544 | setsockopt(http->fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); | |
545 | #endif /* SO_REUSEPORT */ | |
3840d6ba | 546 | |
08e230aa | 547 | /* |
548 | * Using TCP_NODELAY improves responsiveness, especially on systems | |
549 | * with a slow loopback interface... Since we write large buffers | |
550 | * when sending print files and requests, there shouldn't be any | |
551 | * performance penalty for this... | |
552 | */ | |
553 | ||
554 | val = 1; | |
111840f5 | 555 | #ifdef WIN32 |
556 | setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); | |
557 | #else | |
08e230aa | 558 | setsockopt(http->fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); |
111840f5 | 559 | #endif // WIN32 |
08e230aa | 560 | |
50146867 | 561 | /* |
562 | * Connect to the server... | |
563 | */ | |
564 | ||
565 | if (connect(http->fd, (struct sockaddr *)&(http->hostaddr), | |
566 | sizeof(http->hostaddr)) < 0) | |
567 | { | |
3620d932 | 568 | #ifdef WIN32 |
977acbd3 | 569 | http->error = WSAGetLastError(); |
570 | #else | |
f44afac9 | 571 | http->error = errno; |
3620d932 | 572 | #endif /* WIN32 */ |
f44afac9 | 573 | http->status = HTTP_ERROR; |
0bf5ae82 | 574 | |
50146867 | 575 | #ifdef WIN32 |
576 | closesocket(http->fd); | |
577 | #else | |
578 | close(http->fd); | |
579 | #endif | |
580 | ||
7e9dc5e9 | 581 | http->fd = -1; |
582 | ||
50146867 | 583 | return (-1); |
e8fda7b9 | 584 | } |
f44afac9 | 585 | |
586 | http->error = 0; | |
587 | http->status = HTTP_CONTINUE; | |
50146867 | 588 | |
2dac9564 | 589 | #ifdef HAVE_SSL |
a75c006a | 590 | if (http->encryption == HTTP_ENCRYPT_ALWAYS) |
591 | { | |
592 | /* | |
2a0ef17a | 593 | * Always do encryption via SSL. |
a75c006a | 594 | */ |
595 | ||
2dac9564 | 596 | if (http_setup_ssl(http) != 0) |
a75c006a | 597 | { |
a75c006a | 598 | #ifdef WIN32 |
599 | closesocket(http->fd); | |
600 | #else | |
601 | close(http->fd); | |
2dac9564 | 602 | #endif /* WIN32 */ |
a75c006a | 603 | |
604 | return (-1); | |
605 | } | |
a75c006a | 606 | } |
2a0ef17a | 607 | else if (http->encryption == HTTP_ENCRYPT_REQUIRED) |
608 | return (http_upgrade(http)); | |
2dac9564 | 609 | #endif /* HAVE_SSL */ |
a75c006a | 610 | |
50146867 | 611 | return (0); |
3a193f5e | 612 | } |
613 | ||
614 | ||
83e740a5 | 615 | /* |
616 | * 'httpGetSubField()' - Get a sub-field value. | |
617 | */ | |
618 | ||
619 | char * /* O - Value or NULL */ | |
620 | httpGetSubField(http_t *http, /* I - HTTP data */ | |
621 | http_field_t field, /* I - Field index */ | |
622 | const char *name, /* I - Name of sub-field */ | |
623 | char *value) /* O - Value string */ | |
624 | { | |
625 | const char *fptr; /* Pointer into field */ | |
626 | char temp[HTTP_MAX_VALUE], /* Temporary buffer for name */ | |
627 | *ptr; /* Pointer into string buffer */ | |
628 | ||
629 | ||
7d34f475 | 630 | DEBUG_printf(("httpGetSubField(http=%p, field=%d, name=\"%s\", value=%p)\n", |
631 | http, field, name, value)); | |
632 | ||
83e740a5 | 633 | if (http == NULL || |
634 | field < HTTP_FIELD_ACCEPT_LANGUAGE || | |
635 | field > HTTP_FIELD_WWW_AUTHENTICATE || | |
636 | name == NULL || value == NULL) | |
637 | return (NULL); | |
638 | ||
639 | for (fptr = http->fields[field]; *fptr;) | |
640 | { | |
641 | /* | |
642 | * Skip leading whitespace... | |
643 | */ | |
644 | ||
27366073 | 645 | while (isspace(*fptr & 255)) |
83e740a5 | 646 | fptr ++; |
647 | ||
648 | if (*fptr == ',') | |
649 | { | |
650 | fptr ++; | |
651 | continue; | |
652 | } | |
653 | ||
654 | /* | |
655 | * Get the sub-field name... | |
656 | */ | |
657 | ||
658 | for (ptr = temp; | |
27366073 | 659 | *fptr && *fptr != '=' && !isspace(*fptr & 255) && ptr < (temp + sizeof(temp) - 1); |
83e740a5 | 660 | *ptr++ = *fptr++); |
661 | ||
662 | *ptr = '\0'; | |
663 | ||
f992395d | 664 | DEBUG_printf(("httpGetSubField: name=\"%s\"\n", temp)); |
83a2ea50 | 665 | |
83e740a5 | 666 | /* |
667 | * Skip trailing chars up to the '='... | |
668 | */ | |
669 | ||
27366073 | 670 | while (isspace(*fptr & 255)) |
83e740a5 | 671 | fptr ++; |
672 | ||
673 | if (!*fptr) | |
674 | break; | |
675 | ||
83a2ea50 | 676 | if (*fptr != '=') |
677 | continue; | |
678 | ||
83e740a5 | 679 | /* |
680 | * Skip = and leading whitespace... | |
681 | */ | |
682 | ||
683 | fptr ++; | |
684 | ||
27366073 | 685 | while (isspace(*fptr & 255)) |
83e740a5 | 686 | fptr ++; |
687 | ||
688 | if (*fptr == '\"') | |
689 | { | |
690 | /* | |
691 | * Read quoted string... | |
692 | */ | |
693 | ||
694 | for (ptr = value, fptr ++; | |
695 | *fptr && *fptr != '\"' && ptr < (value + HTTP_MAX_VALUE - 1); | |
696 | *ptr++ = *fptr++); | |
697 | ||
698 | *ptr = '\0'; | |
699 | ||
700 | while (*fptr && *fptr != '\"') | |
701 | fptr ++; | |
702 | ||
703 | if (*fptr) | |
704 | fptr ++; | |
705 | } | |
706 | else | |
707 | { | |
708 | /* | |
709 | * Read unquoted string... | |
710 | */ | |
711 | ||
712 | for (ptr = value; | |
27366073 | 713 | *fptr && !isspace(*fptr & 255) && *fptr != ',' && ptr < (value + HTTP_MAX_VALUE - 1); |
83e740a5 | 714 | *ptr++ = *fptr++); |
715 | ||
716 | *ptr = '\0'; | |
717 | ||
27366073 | 718 | while (*fptr && !isspace(*fptr & 255) && *fptr != ',') |
83e740a5 | 719 | fptr ++; |
720 | } | |
721 | ||
f992395d | 722 | DEBUG_printf(("httpGetSubField: value=\"%s\"\n", value)); |
83a2ea50 | 723 | |
83e740a5 | 724 | /* |
725 | * See if this is the one... | |
726 | */ | |
727 | ||
728 | if (strcmp(name, temp) == 0) | |
729 | return (value); | |
730 | } | |
731 | ||
732 | value[0] = '\0'; | |
733 | ||
734 | return (NULL); | |
735 | } | |
736 | ||
737 | ||
50146867 | 738 | /* |
739 | * 'httpSetField()' - Set the value of an HTTP header. | |
740 | */ | |
3a193f5e | 741 | |
50146867 | 742 | void |
743 | httpSetField(http_t *http, /* I - HTTP data */ | |
744 | http_field_t field, /* I - Field index */ | |
063e1ac7 | 745 | const char *value) /* I - Value */ |
3a193f5e | 746 | { |
83e740a5 | 747 | if (http == NULL || |
748 | field < HTTP_FIELD_ACCEPT_LANGUAGE || | |
749 | field > HTTP_FIELD_WWW_AUTHENTICATE || | |
750 | value == NULL) | |
751 | return; | |
752 | ||
17438bf4 | 753 | strlcpy(http->fields[field], value, HTTP_MAX_VALUE); |
3a193f5e | 754 | } |
755 | ||
756 | ||
50146867 | 757 | /* |
758 | * 'httpDelete()' - Send a DELETE request to the server. | |
759 | */ | |
760 | ||
761 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 762 | httpDelete(http_t *http, /* I - HTTP data */ |
763 | const char *uri) /* I - URI to delete */ | |
3a193f5e | 764 | { |
a8374e1e | 765 | return (http_send(http, HTTP_DELETE, uri)); |
3a193f5e | 766 | } |
767 | ||
768 | ||
50146867 | 769 | /* |
770 | * 'httpGet()' - Send a GET request to the server. | |
771 | */ | |
772 | ||
773 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 774 | httpGet(http_t *http, /* I - HTTP data */ |
775 | const char *uri) /* I - URI to get */ | |
3a193f5e | 776 | { |
a8374e1e | 777 | return (http_send(http, HTTP_GET, uri)); |
3a193f5e | 778 | } |
779 | ||
780 | ||
50146867 | 781 | /* |
782 | * 'httpHead()' - Send a HEAD request to the server. | |
783 | */ | |
784 | ||
785 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 786 | httpHead(http_t *http, /* I - HTTP data */ |
787 | const char *uri) /* I - URI for head */ | |
3a193f5e | 788 | { |
a8374e1e | 789 | return (http_send(http, HTTP_HEAD, uri)); |
3a193f5e | 790 | } |
791 | ||
792 | ||
50146867 | 793 | /* |
794 | * 'httpOptions()' - Send an OPTIONS request to the server. | |
795 | */ | |
796 | ||
797 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 798 | httpOptions(http_t *http, /* I - HTTP data */ |
799 | const char *uri) /* I - URI for options */ | |
3a193f5e | 800 | { |
a8374e1e | 801 | return (http_send(http, HTTP_OPTIONS, uri)); |
3a193f5e | 802 | } |
803 | ||
804 | ||
50146867 | 805 | /* |
806 | * 'httpPost()' - Send a POST request to the server. | |
807 | */ | |
808 | ||
809 | int /* O - Status of call (0 = success) */ | |
063e1ac7 | 810 | httpPost(http_t *http, /* I - HTTP data */ |
811 | const char *uri) /* I - URI for post */ | |
3a193f5e | 812 | { |
4a73831b | 813 | httpGetLength(http); |
0542e38e | 814 | |
a8374e1e | 815 | return (http_send(http, HTTP_POST, uri)); |
3a193f5e | 816 | } |
817 | ||
818 | ||
819 | /* | |
50146867 | 820 | * 'httpPut()' - Send a PUT request to the server. |
3a193f5e | 821 | */ |
822 | ||
50146867 | 823 | int /* O - Status of call (0 = success) */ |
063e1ac7 | 824 | httpPut(http_t *http, /* I - HTTP data */ |
825 | const char *uri) /* I - URI to put */ | |
3a193f5e | 826 | { |
4a73831b | 827 | httpGetLength(http); |
0542e38e | 828 | |
a8374e1e | 829 | return (http_send(http, HTTP_PUT, uri)); |
3840d6ba | 830 | } |
831 | ||
832 | ||
833 | /* | |
50146867 | 834 | * 'httpTrace()' - Send an TRACE request to the server. |
3840d6ba | 835 | */ |
836 | ||
50146867 | 837 | int /* O - Status of call (0 = success) */ |
063e1ac7 | 838 | httpTrace(http_t *http, /* I - HTTP data */ |
839 | const char *uri) /* I - URI for trace */ | |
3840d6ba | 840 | { |
a8374e1e | 841 | return (http_send(http, HTTP_TRACE, uri)); |
3840d6ba | 842 | } |
843 | ||
844 | ||
69ee1496 | 845 | /* |
846 | * 'httpFlush()' - Flush data from a HTTP connection. | |
847 | */ | |
848 | ||
849 | void | |
f992395d | 850 | httpFlush(http_t *http) /* I - HTTP data */ |
69ee1496 | 851 | { |
f992395d | 852 | char buffer[8192]; /* Junk buffer */ |
69ee1496 | 853 | |
854 | ||
f992395d | 855 | DEBUG_printf(("httpFlush(http=%p), state=%d\n", http, http->state)); |
856 | ||
857 | while (httpRead(http, buffer, sizeof(buffer)) > 0); | |
69ee1496 | 858 | } |
859 | ||
860 | ||
3840d6ba | 861 | /* |
50146867 | 862 | * 'httpRead()' - Read data from a HTTP connection. |
3840d6ba | 863 | */ |
864 | ||
50146867 | 865 | int /* O - Number of bytes read */ |
866 | httpRead(http_t *http, /* I - HTTP data */ | |
867 | char *buffer, /* I - Buffer for data */ | |
868 | int length) /* I - Maximum number of bytes */ | |
3840d6ba | 869 | { |
2456b740 | 870 | int bytes; /* Bytes read */ |
50146867 | 871 | char len[32]; /* Length string */ |
3840d6ba | 872 | |
873 | ||
f992395d | 874 | DEBUG_printf(("httpRead(http=%p, buffer=%p, length=%d)\n", |
875 | http, buffer, length)); | |
4a73831b | 876 | |
50146867 | 877 | if (http == NULL || buffer == NULL) |
878 | return (-1); | |
3840d6ba | 879 | |
50146867 | 880 | http->activity = time(NULL); |
3840d6ba | 881 | |
50146867 | 882 | if (length <= 0) |
883 | return (0); | |
fd8b1cf8 | 884 | |
0542e38e | 885 | if (http->data_encoding == HTTP_ENCODE_CHUNKED && |
5e879034 | 886 | http->data_remaining <= 0) |
50146867 | 887 | { |
5e879034 | 888 | DEBUG_puts("httpRead: Getting chunk length..."); |
889 | ||
50146867 | 890 | if (httpGets(len, sizeof(len), http) == NULL) |
5e879034 | 891 | { |
892 | DEBUG_puts("httpRead: Could not get length!"); | |
50146867 | 893 | return (0); |
5e879034 | 894 | } |
3840d6ba | 895 | |
0542e38e | 896 | http->data_remaining = strtol(len, NULL, 16); |
04a7b72e | 897 | if (http->data_remaining < 0) |
898 | { | |
899 | DEBUG_puts("httpRead: Negative chunk length!"); | |
900 | return (0); | |
901 | } | |
50146867 | 902 | } |
3840d6ba | 903 | |
f992395d | 904 | DEBUG_printf(("httpRead: data_remaining=%d\n", http->data_remaining)); |
4a73831b | 905 | |
04a7b72e | 906 | if (http->data_remaining <= 0) |
3840d6ba | 907 | { |
50146867 | 908 | /* |
909 | * A zero-length chunk ends a transfer; unless we are reading POST | |
910 | * data, go idle... | |
911 | */ | |
f736c0e3 | 912 | |
5e879034 | 913 | if (http->data_encoding == HTTP_ENCODE_CHUNKED) |
4355fb2f | 914 | httpGets(len, sizeof(len), http); |
915 | ||
50146867 | 916 | if (http->state == HTTP_POST_RECV) |
917 | http->state ++; | |
918 | else | |
919 | http->state = HTTP_WAITING; | |
3840d6ba | 920 | |
f992395d | 921 | /* |
922 | * Prevent future reads for this request... | |
923 | */ | |
924 | ||
925 | http->data_encoding = HTTP_ENCODE_LENGTH; | |
926 | ||
50146867 | 927 | return (0); |
928 | } | |
0542e38e | 929 | else if (length > http->data_remaining) |
930 | length = http->data_remaining; | |
50146867 | 931 | |
c0a0a784 | 932 | if (http->used == 0 && length <= 256) |
933 | { | |
934 | /* | |
935 | * Buffer small reads for better performance... | |
936 | */ | |
937 | ||
1c9fac45 | 938 | if (!http->blocking && !httpWait(http, 1000)) |
939 | return (0); | |
940 | ||
c0a0a784 | 941 | if (http->data_remaining > sizeof(http->buffer)) |
942 | bytes = sizeof(http->buffer); | |
943 | else | |
944 | bytes = http->data_remaining; | |
945 | ||
2dac9564 | 946 | #ifdef HAVE_SSL |
c0a0a784 | 947 | if (http->tls) |
2dac9564 | 948 | bytes = http_read_ssl(http, http->buffer, bytes); |
c0a0a784 | 949 | else |
2dac9564 | 950 | #endif /* HAVE_SSL */ |
c0a0a784 | 951 | { |
952 | DEBUG_printf(("httpRead: reading %d bytes from socket into buffer...\n", | |
953 | bytes)); | |
954 | ||
955 | bytes = recv(http->fd, http->buffer, bytes, 0); | |
956 | ||
957 | DEBUG_printf(("httpRead: read %d bytes from socket into buffer...\n", | |
958 | bytes)); | |
959 | } | |
960 | ||
961 | if (bytes > 0) | |
962 | http->used = bytes; | |
963 | else if (bytes < 0) | |
964 | { | |
3620d932 | 965 | #ifdef WIN32 |
c0a0a784 | 966 | http->error = WSAGetLastError(); |
b3ee6362 | 967 | return (-1); |
c0a0a784 | 968 | #else |
b3ee6362 | 969 | if (errno != EINTR) |
970 | { | |
971 | http->error = errno; | |
972 | return (-1); | |
973 | } | |
3620d932 | 974 | #endif /* WIN32 */ |
c0a0a784 | 975 | } |
1c9fac45 | 976 | else |
706416fc | 977 | { |
978 | http->error = EPIPE; | |
c0a0a784 | 979 | return (0); |
706416fc | 980 | } |
c0a0a784 | 981 | } |
982 | ||
0542e38e | 983 | if (http->used > 0) |
50146867 | 984 | { |
0542e38e | 985 | if (length > http->used) |
986 | length = http->used; | |
3840d6ba | 987 | |
0542e38e | 988 | bytes = length; |
989 | ||
4a73831b | 990 | DEBUG_printf(("httpRead: grabbing %d bytes from input buffer...\n", bytes)); |
991 | ||
0542e38e | 992 | memcpy(buffer, http->buffer, length); |
993 | http->used -= length; | |
994 | ||
995 | if (http->used > 0) | |
1289a630 | 996 | memmove(http->buffer, http->buffer + length, http->used); |
50146867 | 997 | } |
2dac9564 | 998 | #ifdef HAVE_SSL |
a75c006a | 999 | else if (http->tls) |
13271ca6 | 1000 | { |
1001 | if (!http->blocking && !httpWait(http, 1000)) | |
1002 | return (0); | |
1003 | ||
2dac9564 | 1004 | bytes = http_read_ssl(http, buffer, length); |
13271ca6 | 1005 | } |
2dac9564 | 1006 | #endif /* HAVE_SSL */ |
0542e38e | 1007 | else |
4a73831b | 1008 | { |
706416fc | 1009 | if (!http->blocking && !httpWait(http, 1000)) |
1010 | return (0); | |
1c9fac45 | 1011 | |
4a73831b | 1012 | DEBUG_printf(("httpRead: reading %d bytes from socket...\n", length)); |
0542e38e | 1013 | bytes = recv(http->fd, buffer, length, 0); |
4a73831b | 1014 | DEBUG_printf(("httpRead: read %d bytes from socket...\n", bytes)); |
1015 | } | |
3840d6ba | 1016 | |
0542e38e | 1017 | if (bytes > 0) |
1018 | http->data_remaining -= bytes; | |
0bf5ae82 | 1019 | else if (bytes < 0) |
b3ee6362 | 1020 | { |
3620d932 | 1021 | #ifdef WIN32 |
977acbd3 | 1022 | http->error = WSAGetLastError(); |
1023 | #else | |
b3ee6362 | 1024 | if (errno == EINTR) |
1025 | bytes = 0; | |
1026 | else | |
1027 | http->error = errno; | |
3620d932 | 1028 | #endif /* WIN32 */ |
b3ee6362 | 1029 | } |
1c9fac45 | 1030 | else |
706416fc | 1031 | { |
1032 | http->error = EPIPE; | |
1c9fac45 | 1033 | return (0); |
706416fc | 1034 | } |
0542e38e | 1035 | |
4355fb2f | 1036 | if (http->data_remaining == 0) |
4a73831b | 1037 | { |
5e879034 | 1038 | if (http->data_encoding == HTTP_ENCODE_CHUNKED) |
4355fb2f | 1039 | httpGets(len, sizeof(len), http); |
1040 | ||
1041 | if (http->data_encoding != HTTP_ENCODE_CHUNKED) | |
1042 | { | |
1043 | if (http->state == HTTP_POST_RECV) | |
1044 | http->state ++; | |
1045 | else | |
1046 | http->state = HTTP_WAITING; | |
1047 | } | |
4a73831b | 1048 | } |
1049 | ||
96da13a4 | 1050 | #ifdef DEBUG |
1051 | { | |
1052 | int i, j, ch; | |
1053 | printf("httpRead: Read %d bytes:\n", bytes); | |
1054 | for (i = 0; i < bytes; i += 16) | |
1055 | { | |
1056 | printf(" "); | |
1057 | ||
1058 | for (j = 0; j < 16 && (i + j) < bytes; j ++) | |
1059 | printf(" %02X", buffer[i + j] & 255); | |
1060 | ||
1061 | while (j < 16) | |
1062 | { | |
1063 | printf(" "); | |
1064 | j ++; | |
1065 | } | |
1066 | ||
1067 | printf(" "); | |
1068 | for (j = 0; j < 16 && (i + j) < bytes; j ++) | |
1069 | { | |
1070 | ch = buffer[i + j] & 255; | |
1071 | ||
1072 | if (ch < ' ' || ch == 127) | |
1073 | ch = '.'; | |
1074 | ||
1075 | putchar(ch); | |
1076 | } | |
1077 | putchar('\n'); | |
1078 | } | |
1079 | } | |
1080 | #endif /* DEBUG */ | |
1081 | ||
0542e38e | 1082 | return (bytes); |
3840d6ba | 1083 | } |
1084 | ||
1085 | ||
4a8a506d | 1086 | /* |
1087 | * 'httpSetCookie()' - Set the cookie value(s)... | |
1088 | */ | |
1089 | ||
1090 | void | |
94cf7749 | 1091 | httpSetCookie(http_t *http, /* I - Connection */ |
1092 | const char *cookie) /* I - Cookie string */ | |
4a8a506d | 1093 | { |
1094 | if (!http) | |
1095 | return; | |
1096 | ||
1097 | if (http->cookie) | |
1098 | free(http->cookie); | |
1099 | ||
1100 | if (cookie) | |
1101 | http->cookie = strdup(cookie); | |
1102 | else | |
1103 | http->cookie = NULL; | |
1104 | } | |
1105 | ||
1106 | ||
aa73fd19 | 1107 | /* |
1108 | * 'httpWait()' - Wait for data available on a connection. | |
1109 | */ | |
1110 | ||
1111 | int /* O - 1 if data is available, 0 otherwise */ | |
1112 | httpWait(http_t *http, /* I - HTTP data */ | |
1113 | int msec) /* I - Milliseconds to wait */ | |
1114 | { | |
aa73fd19 | 1115 | /* |
1116 | * First see if there is data in the buffer... | |
1117 | */ | |
1118 | ||
1119 | if (http == NULL) | |
1120 | return (0); | |
1121 | ||
1122 | if (http->used) | |
1123 | return (1); | |
1124 | ||
1125 | /* | |
94cf7749 | 1126 | * If not, check the SSL/TLS buffers and do a select() on the connection... |
aa73fd19 | 1127 | */ |
1128 | ||
94cf7749 | 1129 | return (http_wait(http, msec)); |
aa73fd19 | 1130 | } |
1131 | ||
1132 | ||
3840d6ba | 1133 | /* |
50146867 | 1134 | * 'httpWrite()' - Write data to a HTTP connection. |
3840d6ba | 1135 | */ |
50146867 | 1136 | |
1137 | int /* O - Number of bytes written */ | |
063e1ac7 | 1138 | httpWrite(http_t *http, /* I - HTTP data */ |
1139 | const char *buffer, /* I - Buffer for data */ | |
1140 | int length) /* I - Number of bytes to write */ | |
3840d6ba | 1141 | { |
977acbd3 | 1142 | int tbytes, /* Total bytes sent */ |
1143 | bytes; /* Bytes sent */ | |
3840d6ba | 1144 | |
1145 | ||
50146867 | 1146 | if (http == NULL || buffer == NULL) |
1147 | return (-1); | |
3840d6ba | 1148 | |
50146867 | 1149 | http->activity = time(NULL); |
1150 | ||
5e879034 | 1151 | if (http->data_encoding == HTTP_ENCODE_CHUNKED) |
8d5f8b3e | 1152 | { |
4355fb2f | 1153 | if (httpPrintf(http, "%x\r\n", length) < 0) |
50146867 | 1154 | return (-1); |
3840d6ba | 1155 | |
8d5f8b3e | 1156 | if (length == 0) |
1157 | { | |
1158 | /* | |
1159 | * A zero-length chunk ends a transfer; unless we are sending POST | |
9f6d3559 | 1160 | * or PUT data, go idle... |
8d5f8b3e | 1161 | */ |
1162 | ||
1163 | DEBUG_puts("httpWrite: changing states..."); | |
1164 | ||
1165 | if (http->state == HTTP_POST_RECV) | |
1166 | http->state ++; | |
ae3c9f2d | 1167 | else if (http->state == HTTP_PUT_RECV) |
1168 | http->state = HTTP_STATUS; | |
8d5f8b3e | 1169 | else |
1170 | http->state = HTTP_WAITING; | |
1171 | ||
1172 | if (httpPrintf(http, "\r\n") < 0) | |
1173 | return (-1); | |
1174 | ||
1175 | return (0); | |
1176 | } | |
1177 | } | |
1178 | ||
50146867 | 1179 | tbytes = 0; |
4a73831b | 1180 | |
50146867 | 1181 | while (length > 0) |
1182 | { | |
2dac9564 | 1183 | #ifdef HAVE_SSL |
a75c006a | 1184 | if (http->tls) |
2dac9564 | 1185 | bytes = http_write_ssl(http, buffer, length); |
a75c006a | 1186 | else |
2dac9564 | 1187 | #endif /* HAVE_SSL */ |
50146867 | 1188 | bytes = send(http->fd, buffer, length, 0); |
a75c006a | 1189 | |
50146867 | 1190 | if (bytes < 0) |
4a73831b | 1191 | { |
b3ee6362 | 1192 | #ifdef WIN32 |
1193 | if (WSAGetLastError() != http->error) | |
1194 | { | |
1195 | http->error = WSAGetLastError(); | |
1196 | continue; | |
1197 | } | |
1198 | #else | |
1199 | if (errno == EINTR) | |
1200 | continue; | |
0f70f23d | 1201 | else if (errno != http->error && errno != ECONNRESET) |
b3ee6362 | 1202 | { |
1203 | http->error = errno; | |
1204 | continue; | |
1205 | } | |
1206 | #endif /* WIN32 */ | |
1207 | ||
4a73831b | 1208 | DEBUG_puts("httpWrite: error writing data...\n"); |
c1918ec5 | 1209 | |
50146867 | 1210 | return (-1); |
4a73831b | 1211 | } |
3840d6ba | 1212 | |
50146867 | 1213 | buffer += bytes; |
1214 | tbytes += bytes; | |
1215 | length -= bytes; | |
69ee1496 | 1216 | if (http->data_encoding == HTTP_ENCODE_LENGTH) |
1217 | http->data_remaining -= bytes; | |
1218 | } | |
1219 | ||
5e879034 | 1220 | if (http->data_encoding == HTTP_ENCODE_CHUNKED) |
4355fb2f | 1221 | if (httpPrintf(http, "\r\n") < 0) |
1222 | return (-1); | |
1223 | ||
8d5f8b3e | 1224 | if (http->data_remaining == 0 && http->data_encoding == HTTP_ENCODE_LENGTH) |
69ee1496 | 1225 | { |
1226 | /* | |
9f6d3559 | 1227 | * Finished with the transfer; unless we are sending POST or PUT |
1228 | * data, go idle... | |
69ee1496 | 1229 | */ |
1230 | ||
8d5f8b3e | 1231 | DEBUG_puts("httpWrite: changing states..."); |
1232 | ||
69ee1496 | 1233 | if (http->state == HTTP_POST_RECV) |
1234 | http->state ++; | |
9f6d3559 | 1235 | else if (http->state == HTTP_PUT_RECV) |
1236 | http->state = HTTP_STATUS; | |
69ee1496 | 1237 | else |
1238 | http->state = HTTP_WAITING; | |
50146867 | 1239 | } |
1240 | ||
96da13a4 | 1241 | #ifdef DEBUG |
1242 | { | |
1243 | int i, j, ch; | |
1244 | printf("httpWrite: wrote %d bytes: \n", tbytes); | |
1245 | for (i = 0, buffer -= tbytes; i < tbytes; i += 16) | |
1246 | { | |
1247 | printf(" "); | |
1248 | ||
1249 | for (j = 0; j < 16 && (i + j) < tbytes; j ++) | |
1250 | printf(" %02X", buffer[i + j] & 255); | |
1251 | ||
1252 | while (j < 16) | |
1253 | { | |
1254 | printf(" "); | |
1255 | j ++; | |
1256 | } | |
4a73831b | 1257 | |
96da13a4 | 1258 | printf(" "); |
1259 | for (j = 0; j < 16 && (i + j) < tbytes; j ++) | |
1260 | { | |
1261 | ch = buffer[i + j] & 255; | |
1262 | ||
1263 | if (ch < ' ' || ch == 127) | |
1264 | ch = '.'; | |
1265 | ||
1266 | putchar(ch); | |
1267 | } | |
1268 | putchar('\n'); | |
1269 | } | |
1270 | } | |
1271 | #endif /* DEBUG */ | |
50146867 | 1272 | return (tbytes); |
1273 | } | |
1274 | ||
1275 | ||
1276 | /* | |
1277 | * 'httpGets()' - Get a line of text from a HTTP connection. | |
1278 | */ | |
1279 | ||
1280 | char * /* O - Line or NULL */ | |
1281 | httpGets(char *line, /* I - Line to read into */ | |
1282 | int length, /* I - Max length of buffer */ | |
1283 | http_t *http) /* I - HTTP data */ | |
1284 | { | |
1285 | char *lineptr, /* Pointer into line */ | |
1286 | *bufptr, /* Pointer into input buffer */ | |
1287 | *bufend; /* Pointer to end of buffer */ | |
1288 | int bytes; /* Number of bytes read */ | |
1289 | ||
1290 | ||
f992395d | 1291 | DEBUG_printf(("httpGets(line=%p, length=%d, http=%p)\n", line, length, http)); |
4a73831b | 1292 | |
50146867 | 1293 | if (http == NULL || line == NULL) |
1294 | return (NULL); | |
1295 | ||
1296 | /* | |
1297 | * Pre-scan the buffer and see if there is a newline in there... | |
1298 | */ | |
1299 | ||
3620d932 | 1300 | #ifdef WIN32 |
977acbd3 | 1301 | WSASetLastError(0); |
1302 | #else | |
0bf5ae82 | 1303 | errno = 0; |
3620d932 | 1304 | #endif /* WIN32 */ |
f5174332 | 1305 | |
0542e38e | 1306 | do |
50146867 | 1307 | { |
0542e38e | 1308 | bufptr = http->buffer; |
1309 | bufend = http->buffer + http->used; | |
50146867 | 1310 | |
0542e38e | 1311 | while (bufptr < bufend) |
1312 | if (*bufptr == 0x0a) | |
1313 | break; | |
1314 | else | |
1315 | bufptr ++; | |
50146867 | 1316 | |
27d555e8 | 1317 | if (bufptr >= bufend && http->used < HTTP_MAX_BUFFER) |
50146867 | 1318 | { |
1319 | /* | |
0542e38e | 1320 | * No newline; see if there is more data to be read... |
50146867 | 1321 | */ |
1322 | ||
94cf7749 | 1323 | if (!http->blocking && !http_wait(http, 1000)) |
13271ca6 | 1324 | return (NULL); |
1325 | ||
2dac9564 | 1326 | #ifdef HAVE_SSL |
a75c006a | 1327 | if (http->tls) |
2dac9564 | 1328 | bytes = http_read_ssl(http, bufend, HTTP_MAX_BUFFER - http->used); |
a75c006a | 1329 | else |
2dac9564 | 1330 | #endif /* HAVE_SSL */ |
1c9fac45 | 1331 | bytes = recv(http->fd, bufend, HTTP_MAX_BUFFER - http->used, 0); |
a75c006a | 1332 | |
a9b9fc66 | 1333 | DEBUG_printf(("httpGets: read %d bytes...\n", bytes)); |
1334 | ||
a75c006a | 1335 | if (bytes < 0) |
0542e38e | 1336 | { |
1337 | /* | |
1338 | * Nope, can't get a line this time... | |
1339 | */ | |
1340 | ||
3620d932 | 1341 | #ifdef WIN32 |
977acbd3 | 1342 | if (WSAGetLastError() != http->error) |
1343 | { | |
1344 | http->error = WSAGetLastError(); | |
1345 | continue; | |
1346 | } | |
1347 | ||
f992395d | 1348 | DEBUG_printf(("httpGets: recv() error %d!\n", WSAGetLastError())); |
977acbd3 | 1349 | #else |
a9b9fc66 | 1350 | DEBUG_printf(("httpGets: recv() error %d!\n", errno)); |
1351 | ||
b3ee6362 | 1352 | if (errno == EINTR) |
1353 | continue; | |
1354 | else if (errno != http->error) | |
f5174332 | 1355 | { |
0bf5ae82 | 1356 | http->error = errno; |
c1918ec5 | 1357 | continue; |
f5174332 | 1358 | } |
3620d932 | 1359 | #endif /* WIN32 */ |
c1918ec5 | 1360 | |
0542e38e | 1361 | return (NULL); |
1362 | } | |
f5174332 | 1363 | else if (bytes == 0) |
0bf5ae82 | 1364 | { |
1c9fac45 | 1365 | http->error = EPIPE; |
0bf5ae82 | 1366 | |
f5174332 | 1367 | return (NULL); |
0bf5ae82 | 1368 | } |
0542e38e | 1369 | |
f5174332 | 1370 | /* |
1371 | * Yup, update the amount used and the end pointer... | |
1372 | */ | |
1373 | ||
1374 | http->used += bytes; | |
1375 | bufend += bytes; | |
1289a630 | 1376 | bufptr = bufend; |
50146867 | 1377 | } |
1378 | } | |
27d555e8 | 1379 | while (bufptr >= bufend && http->used < HTTP_MAX_BUFFER); |
50146867 | 1380 | |
1381 | http->activity = time(NULL); | |
1382 | ||
1383 | /* | |
1384 | * Read a line from the buffer... | |
1385 | */ | |
1386 | ||
1387 | lineptr = line; | |
1388 | bufptr = http->buffer; | |
1389 | bytes = 0; | |
584886cb | 1390 | length --; |
50146867 | 1391 | |
1392 | while (bufptr < bufend && bytes < length) | |
1393 | { | |
1394 | bytes ++; | |
1395 | ||
1396 | if (*bufptr == 0x0a) | |
1397 | { | |
1398 | bufptr ++; | |
584886cb | 1399 | break; |
50146867 | 1400 | } |
1401 | else if (*bufptr == 0x0d) | |
1402 | bufptr ++; | |
1403 | else | |
1404 | *lineptr++ = *bufptr++; | |
1405 | } | |
1406 | ||
584886cb | 1407 | if (bytes > 0) |
1408 | { | |
1409 | *lineptr = '\0'; | |
1410 | ||
1411 | http->used -= bytes; | |
1412 | if (http->used > 0) | |
1289a630 | 1413 | memmove(http->buffer, bufptr, http->used); |
584886cb | 1414 | |
f992395d | 1415 | DEBUG_printf(("httpGets: Returning \"%s\"\n", line)); |
584886cb | 1416 | return (line); |
1417 | } | |
1418 | ||
f992395d | 1419 | DEBUG_puts("httpGets: No new line available!"); |
c1918ec5 | 1420 | |
50146867 | 1421 | return (NULL); |
1422 | } | |
1423 | ||
1424 | ||
1425 | /* | |
1426 | * 'httpPrintf()' - Print a formatted string to a HTTP connection. | |
1427 | */ | |
1428 | ||
1429 | int /* O - Number of bytes written */ | |
1430 | httpPrintf(http_t *http, /* I - HTTP data */ | |
1431 | const char *format, /* I - printf-style format string */ | |
1432 | ...) /* I - Additional args as needed */ | |
1433 | { | |
1118e338 | 1434 | int bytes, /* Number of bytes to write */ |
1435 | nbytes, /* Number of bytes written */ | |
1436 | tbytes; /* Number of bytes all together */ | |
1437 | char buf[HTTP_MAX_BUFFER], /* Buffer for formatted string */ | |
1438 | *bufptr; /* Pointer into buffer */ | |
50146867 | 1439 | va_list ap; /* Variable argument pointer */ |
1440 | ||
1441 | ||
f992395d | 1442 | DEBUG_printf(("httpPrintf(http=%p, format=\"%s\", ...)\n", http, format)); |
1443 | ||
50146867 | 1444 | va_start(ap, format); |
04de52f8 | 1445 | bytes = vsnprintf(buf, sizeof(buf), format, ap); |
50146867 | 1446 | va_end(ap); |
1447 | ||
5356dc5a | 1448 | DEBUG_printf(("httpPrintf: %s", buf)); |
1449 | ||
1118e338 | 1450 | for (tbytes = 0, bufptr = buf; tbytes < bytes; tbytes += nbytes, bufptr += nbytes) |
a75c006a | 1451 | { |
2dac9564 | 1452 | #ifdef HAVE_SSL |
a75c006a | 1453 | if (http->tls) |
2dac9564 | 1454 | nbytes = http_write_ssl(http, bufptr, bytes - tbytes); |
a75c006a | 1455 | else |
2dac9564 | 1456 | #endif /* HAVE_SSL */ |
a75c006a | 1457 | nbytes = send(http->fd, bufptr, bytes - tbytes, 0); |
1458 | ||
1459 | if (nbytes < 0) | |
b3ee6362 | 1460 | { |
1461 | nbytes = 0; | |
1462 | ||
1463 | #ifdef WIN32 | |
1464 | if (WSAGetLastError() != http->error) | |
1465 | { | |
1466 | http->error = WSAGetLastError(); | |
1467 | continue; | |
1468 | } | |
1469 | #else | |
1470 | if (errno == EINTR) | |
1471 | continue; | |
1472 | else if (errno != http->error) | |
1473 | { | |
1474 | http->error = errno; | |
1475 | continue; | |
1476 | } | |
1477 | #endif /* WIN32 */ | |
1478 | ||
1118e338 | 1479 | return (-1); |
b3ee6362 | 1480 | } |
a75c006a | 1481 | } |
1118e338 | 1482 | |
1483 | return (bytes); | |
50146867 | 1484 | } |
1485 | ||
1486 | ||
50146867 | 1487 | /* |
1488 | * 'httpGetDateString()' - Get a formatted date/time string from a time value. | |
1489 | */ | |
1490 | ||
063e1ac7 | 1491 | const char * /* O - Date/time string */ |
50146867 | 1492 | httpGetDateString(time_t t) /* I - UNIX time */ |
1493 | { | |
1494 | struct tm *tdate; | |
1495 | static char datetime[256]; | |
1496 | ||
1497 | ||
1498 | tdate = gmtime(&t); | |
a6988fb1 | 1499 | snprintf(datetime, sizeof(datetime), "%s, %02d %s %d %02d:%02d:%02d GMT", |
1500 | days[tdate->tm_wday], tdate->tm_mday, months[tdate->tm_mon], | |
1501 | tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec); | |
50146867 | 1502 | |
1503 | return (datetime); | |
1504 | } | |
1505 | ||
1506 | ||
1507 | /* | |
1508 | * 'httpGetDateTime()' - Get a time value from a formatted date/time string. | |
1509 | */ | |
1510 | ||
1511 | time_t /* O - UNIX time */ | |
063e1ac7 | 1512 | httpGetDateTime(const char *s) /* I - Date/time string */ |
50146867 | 1513 | { |
a8374e1e | 1514 | int i; /* Looping var */ |
1515 | struct tm tdate; /* Time/date structure */ | |
1516 | char mon[16]; /* Abbreviated month name */ | |
1517 | int day, year; /* Day of month and year */ | |
1518 | int hour, min, sec; /* Time */ | |
1519 | ||
1520 | ||
04d61d56 | 1521 | if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6) |
a8374e1e | 1522 | return (0); |
1523 | ||
1524 | for (i = 0; i < 12; i ++) | |
1525 | if (strcasecmp(mon, months[i]) == 0) | |
1526 | break; | |
1527 | ||
1528 | if (i >= 12) | |
1529 | return (0); | |
1530 | ||
1531 | tdate.tm_mon = i; | |
1532 | tdate.tm_mday = day; | |
1533 | tdate.tm_year = year - 1900; | |
1534 | tdate.tm_hour = hour; | |
1535 | tdate.tm_min = min; | |
1536 | tdate.tm_sec = sec; | |
1537 | tdate.tm_isdst = 0; | |
1538 | ||
1539 | return (mktime(&tdate)); | |
50146867 | 1540 | } |
1541 | ||
1542 | ||
1543 | /* | |
1544 | * 'httpUpdate()' - Update the current HTTP state for incoming data. | |
1545 | */ | |
1546 | ||
a8374e1e | 1547 | http_status_t /* O - HTTP status */ |
50146867 | 1548 | httpUpdate(http_t *http) /* I - HTTP data */ |
1549 | { | |
a8374e1e | 1550 | char line[1024], /* Line from connection... */ |
1551 | *value; /* Pointer to value on line */ | |
1552 | http_field_t field; /* Field index */ | |
81702dd2 | 1553 | int major, minor, /* HTTP version numbers */ |
1554 | status; /* Request status */ | |
50146867 | 1555 | |
50146867 | 1556 | |
f992395d | 1557 | DEBUG_printf(("httpUpdate(http=%p), state=%d\n", http, http->state)); |
4a73831b | 1558 | |
a8374e1e | 1559 | /* |
1560 | * If we haven't issued any commands, then there is nothing to "update"... | |
1561 | */ | |
3840d6ba | 1562 | |
a8374e1e | 1563 | if (http->state == HTTP_WAITING) |
1564 | return (HTTP_CONTINUE); | |
3840d6ba | 1565 | |
1566 | /* | |
a8374e1e | 1567 | * Grab all of the lines we can from the connection... |
3840d6ba | 1568 | */ |
1569 | ||
a8374e1e | 1570 | while (httpGets(line, sizeof(line), http) != NULL) |
fd8b1cf8 | 1571 | { |
f992395d | 1572 | DEBUG_printf(("httpUpdate: Got \"%s\"\n", line)); |
9ae9d67c | 1573 | |
a8374e1e | 1574 | if (line[0] == '\0') |
fd8b1cf8 | 1575 | { |
a8374e1e | 1576 | /* |
1577 | * Blank line means the start of the data section (if any). Return | |
1578 | * the result code, too... | |
11b9b0d7 | 1579 | * |
1580 | * If we get status 100 (HTTP_CONTINUE), then we *don't* change states. | |
1581 | * Instead, we just return HTTP_CONTINUE to the caller and keep on | |
1582 | * tryin'... | |
a8374e1e | 1583 | */ |
1584 | ||
11b9b0d7 | 1585 | if (http->status == HTTP_CONTINUE) |
1586 | return (http->status); | |
1587 | ||
6269b3e2 | 1588 | if (http->status < HTTP_BAD_REQUEST) |
1589 | http->digest_tries = 0; | |
1590 | ||
2dac9564 | 1591 | #ifdef HAVE_SSL |
b38fb7fc | 1592 | if (http->status == HTTP_SWITCHING_PROTOCOLS && !http->tls) |
a75c006a | 1593 | { |
2dac9564 | 1594 | if (http_setup_ssl(http) != 0) |
a75c006a | 1595 | { |
7d34f475 | 1596 | # ifdef WIN32 |
a75c006a | 1597 | closesocket(http->fd); |
7d34f475 | 1598 | # else |
a75c006a | 1599 | close(http->fd); |
7d34f475 | 1600 | # endif /* WIN32 */ |
a75c006a | 1601 | |
1602 | return (HTTP_ERROR); | |
1603 | } | |
1604 | ||
a75c006a | 1605 | return (HTTP_CONTINUE); |
1606 | } | |
2dac9564 | 1607 | #endif /* HAVE_SSL */ |
a75c006a | 1608 | |
5356dc5a | 1609 | httpGetLength(http); |
0542e38e | 1610 | |
1611 | switch (http->state) | |
1612 | { | |
1613 | case HTTP_GET : | |
1614 | case HTTP_POST : | |
1615 | case HTTP_POST_RECV : | |
1616 | case HTTP_PUT : | |
1617 | http->state ++; | |
3fd6dd5b | 1618 | case HTTP_POST_SEND : |
0542e38e | 1619 | break; |
1620 | ||
1621 | default : | |
1622 | http->state = HTTP_WAITING; | |
1623 | break; | |
1624 | } | |
a8374e1e | 1625 | |
1626 | return (http->status); | |
fd8b1cf8 | 1627 | } |
a8374e1e | 1628 | else if (strncmp(line, "HTTP/", 5) == 0) |
fd8b1cf8 | 1629 | { |
1630 | /* | |
a8374e1e | 1631 | * Got the beginning of a response... |
fd8b1cf8 | 1632 | */ |
1633 | ||
81702dd2 | 1634 | if (sscanf(line, "HTTP/%d.%d%d", &major, &minor, &status) != 3) |
a8374e1e | 1635 | return (HTTP_ERROR); |
1636 | ||
1637 | http->version = (http_version_t)(major * 100 + minor); | |
81702dd2 | 1638 | http->status = (http_status_t)status; |
fd8b1cf8 | 1639 | } |
a8374e1e | 1640 | else if ((value = strchr(line, ':')) != NULL) |
3840d6ba | 1641 | { |
a8374e1e | 1642 | /* |
1643 | * Got a value... | |
1644 | */ | |
3a193f5e | 1645 | |
a8374e1e | 1646 | *value++ = '\0'; |
27366073 | 1647 | while (isspace(*value & 255)) |
a8374e1e | 1648 | value ++; |
1649 | ||
0d1f75a3 | 1650 | /* |
1651 | * Be tolerants of servers that send unknown attribute fields... | |
1652 | */ | |
1653 | ||
4a8a506d | 1654 | if (!strcasecmp(line, "expect")) |
1655 | { | |
1656 | /* | |
1657 | * "Expect: 100-continue" or similar... | |
1658 | */ | |
1659 | ||
1660 | http->expect = (http_status_t)atoi(value); | |
1661 | } | |
1662 | else if (!strcasecmp(line, "cookie")) | |
1663 | { | |
1664 | /* | |
1665 | * "Cookie: name=value[; name=value ...]" - replaces previous cookies... | |
1666 | */ | |
1667 | ||
1668 | httpSetCookie(http, value); | |
1669 | } | |
1670 | else if ((field = http_field(line)) == HTTP_FIELD_UNKNOWN) | |
f4cafe91 | 1671 | { |
4a73831b | 1672 | DEBUG_printf(("httpUpdate: unknown field %s seen!\n", line)); |
0d1f75a3 | 1673 | continue; |
f4cafe91 | 1674 | } |
4a8a506d | 1675 | else |
1676 | httpSetField(http, field, value); | |
3840d6ba | 1677 | } |
a8374e1e | 1678 | else |
f44afac9 | 1679 | { |
1680 | http->status = HTTP_ERROR; | |
a8374e1e | 1681 | return (HTTP_ERROR); |
f44afac9 | 1682 | } |
fd8b1cf8 | 1683 | } |
3840d6ba | 1684 | |
4e8f6c6a | 1685 | /* |
1686 | * See if there was an error... | |
1687 | */ | |
1688 | ||
b2bfd344 | 1689 | if (http->error == EPIPE && http->status > HTTP_CONTINUE) |
1690 | return (http->status); | |
1691 | ||
0bf5ae82 | 1692 | if (http->error) |
f44afac9 | 1693 | { |
a9b9fc66 | 1694 | DEBUG_printf(("httpUpdate: socket error %d - %s\n", http->error, |
1695 | strerror(http->error))); | |
f44afac9 | 1696 | http->status = HTTP_ERROR; |
4e8f6c6a | 1697 | return (HTTP_ERROR); |
f44afac9 | 1698 | } |
4e8f6c6a | 1699 | |
3840d6ba | 1700 | /* |
a8374e1e | 1701 | * If we haven't already returned, then there is nothing new... |
3840d6ba | 1702 | */ |
1703 | ||
a8374e1e | 1704 | return (HTTP_CONTINUE); |
3840d6ba | 1705 | } |
1706 | ||
1707 | ||
3a193f5e | 1708 | /* |
50146867 | 1709 | * 'httpDecode64()' - Base64-decode a string. |
3840d6ba | 1710 | */ |
1711 | ||
50146867 | 1712 | char * /* O - Decoded string */ |
063e1ac7 | 1713 | httpDecode64(char *out, /* I - String to write to */ |
1714 | const char *in) /* I - String to read from */ | |
3840d6ba | 1715 | { |
50146867 | 1716 | int pos, /* Bit position */ |
1717 | base64; /* Value of this character */ | |
1718 | char *outptr; /* Output pointer */ | |
3840d6ba | 1719 | |
3840d6ba | 1720 | |
50146867 | 1721 | for (outptr = out, pos = 0; *in != '\0'; in ++) |
fd8b1cf8 | 1722 | { |
50146867 | 1723 | /* |
1724 | * Decode this character into a number from 0 to 63... | |
1725 | */ | |
f736c0e3 | 1726 | |
50146867 | 1727 | if (*in >= 'A' && *in <= 'Z') |
1728 | base64 = *in - 'A'; | |
1729 | else if (*in >= 'a' && *in <= 'z') | |
1730 | base64 = *in - 'a' + 26; | |
1731 | else if (*in >= '0' && *in <= '9') | |
1732 | base64 = *in - '0' + 52; | |
1733 | else if (*in == '+') | |
1734 | base64 = 62; | |
1735 | else if (*in == '/') | |
1736 | base64 = 63; | |
1737 | else if (*in == '=') | |
1738 | break; | |
1739 | else | |
1740 | continue; | |
3840d6ba | 1741 | |
50146867 | 1742 | /* |
1743 | * Store the result in the appropriate chars... | |
1744 | */ | |
992cf15a | 1745 | |
1746 | switch (pos) | |
1747 | { | |
1748 | case 0 : | |
50146867 | 1749 | *outptr = base64 << 2; |
1750 | pos ++; | |
992cf15a | 1751 | break; |
50146867 | 1752 | case 1 : |
1753 | *outptr++ |= (base64 >> 4) & 3; | |
1754 | *outptr = (base64 << 4) & 255; | |
1755 | pos ++; | |
1756 | break; | |
1757 | case 2 : | |
1758 | *outptr++ |= (base64 >> 2) & 15; | |
1759 | *outptr = (base64 << 6) & 255; | |
1760 | pos ++; | |
1761 | break; | |
1762 | case 3 : | |
1763 | *outptr++ |= base64; | |
1764 | pos = 0; | |
992cf15a | 1765 | break; |
992cf15a | 1766 | } |
1767 | } | |
1768 | ||
50146867 | 1769 | *outptr = '\0'; |
3840d6ba | 1770 | |
1771 | /* | |
50146867 | 1772 | * Return the decoded string... |
3840d6ba | 1773 | */ |
1774 | ||
50146867 | 1775 | return (out); |
3840d6ba | 1776 | } |
1777 | ||
1778 | ||
f736c0e3 | 1779 | /* |
50146867 | 1780 | * 'httpEncode64()' - Base64-encode a string. |
f736c0e3 | 1781 | */ |
1782 | ||
50146867 | 1783 | char * /* O - Encoded string */ |
063e1ac7 | 1784 | httpEncode64(char *out, /* I - String to write to */ |
1785 | const char *in) /* I - String to read from */ | |
f736c0e3 | 1786 | { |
50146867 | 1787 | char *outptr; /* Output pointer */ |
19baf4b4 | 1788 | static const char base64[] = /* Base64 characters... */ |
50146867 | 1789 | { |
1790 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
1791 | "abcdefghijklmnopqrstuvwxyz" | |
1792 | "0123456789" | |
1793 | "+/" | |
1794 | }; | |
f736c0e3 | 1795 | |
f736c0e3 | 1796 | |
50146867 | 1797 | for (outptr = out; *in != '\0'; in ++) |
f736c0e3 | 1798 | { |
1799 | /* | |
50146867 | 1800 | * Encode the up to 3 characters as 4 Base64 numbers... |
f736c0e3 | 1801 | */ |
1802 | ||
50146867 | 1803 | *outptr ++ = base64[in[0] >> 2]; |
1804 | *outptr ++ = base64[((in[0] << 4) | (in[1] >> 4)) & 63]; | |
f736c0e3 | 1805 | |
50146867 | 1806 | in ++; |
1807 | if (*in == '\0') | |
f0af4f8f | 1808 | { |
4bc4b09f | 1809 | *outptr ++ = '='; |
f0af4f8f | 1810 | *outptr ++ = '='; |
50146867 | 1811 | break; |
f0af4f8f | 1812 | } |
f736c0e3 | 1813 | |
50146867 | 1814 | *outptr ++ = base64[((in[0] << 2) | (in[1] >> 6)) & 63]; |
f736c0e3 | 1815 | |
50146867 | 1816 | in ++; |
1817 | if (*in == '\0') | |
4bc4b09f | 1818 | { |
1819 | *outptr ++ = '='; | |
50146867 | 1820 | break; |
4bc4b09f | 1821 | } |
f736c0e3 | 1822 | |
50146867 | 1823 | *outptr ++ = base64[in[0] & 63]; |
f736c0e3 | 1824 | } |
f736c0e3 | 1825 | |
50146867 | 1826 | *outptr = '\0'; |
f736c0e3 | 1827 | |
50146867 | 1828 | /* |
1829 | * Return the encoded string... | |
1830 | */ | |
f736c0e3 | 1831 | |
50146867 | 1832 | return (out); |
f736c0e3 | 1833 | } |
1834 | ||
1835 | ||
3a193f5e | 1836 | /* |
4a73831b | 1837 | * 'httpGetLength()' - Get the amount of data remaining from the |
1838 | * content-length or transfer-encoding fields. | |
0542e38e | 1839 | */ |
1840 | ||
063e1ac7 | 1841 | int /* O - Content length */ |
4a73831b | 1842 | httpGetLength(http_t *http) /* I - HTTP data */ |
0542e38e | 1843 | { |
f992395d | 1844 | DEBUG_printf(("httpGetLength(http=%p), state=%d\n", http, http->state)); |
5e879034 | 1845 | |
0542e38e | 1846 | if (strcasecmp(http->fields[HTTP_FIELD_TRANSFER_ENCODING], "chunked") == 0) |
1847 | { | |
5e879034 | 1848 | DEBUG_puts("httpGetLength: chunked request!"); |
1849 | ||
0542e38e | 1850 | http->data_encoding = HTTP_ENCODE_CHUNKED; |
1851 | http->data_remaining = 0; | |
1852 | } | |
1853 | else | |
1854 | { | |
5e879034 | 1855 | http->data_encoding = HTTP_ENCODE_LENGTH; |
0542e38e | 1856 | |
1857 | /* | |
1858 | * The following is a hack for HTTP servers that don't send a | |
1859 | * content-length or transfer-encoding field... | |
1860 | * | |
1861 | * If there is no content-length then the connection must close | |
1862 | * after the transfer is complete... | |
1863 | */ | |
1864 | ||
1865 | if (http->fields[HTTP_FIELD_CONTENT_LENGTH][0] == '\0') | |
1866 | http->data_remaining = 2147483647; | |
1867 | else | |
1868 | http->data_remaining = atoi(http->fields[HTTP_FIELD_CONTENT_LENGTH]); | |
5e879034 | 1869 | |
f992395d | 1870 | DEBUG_printf(("httpGetLength: content_length=%d\n", http->data_remaining)); |
0542e38e | 1871 | } |
4a73831b | 1872 | |
1873 | return (http->data_remaining); | |
1874 | } | |
1875 | ||
1876 | ||
1877 | /* | |
1878 | * 'http_field()' - Return the field index for a field name. | |
1879 | */ | |
1880 | ||
1881 | static http_field_t /* O - Field index */ | |
063e1ac7 | 1882 | http_field(const char *name) /* I - String name */ |
4a73831b | 1883 | { |
1884 | int i; /* Looping var */ | |
1885 | ||
1886 | ||
1887 | for (i = 0; i < HTTP_FIELD_MAX; i ++) | |
1888 | if (strcasecmp(name, http_fields[i]) == 0) | |
1889 | return ((http_field_t)i); | |
1890 | ||
1891 | return (HTTP_FIELD_UNKNOWN); | |
0542e38e | 1892 | } |
1893 | ||
1894 | ||
a8374e1e | 1895 | /* |
1896 | * 'http_send()' - Send a request with all fields and the trailing blank line. | |
1897 | */ | |
1898 | ||
1899 | static int /* O - 0 on success, non-zero on error */ | |
1900 | http_send(http_t *http, /* I - HTTP data */ | |
1901 | http_state_t request, /* I - Request code */ | |
063e1ac7 | 1902 | const char *uri) /* I - URI */ |
a8374e1e | 1903 | { |
1904 | int i; /* Looping var */ | |
1905 | char *ptr, /* Pointer in buffer */ | |
1906 | buf[1024]; /* Encoded URI buffer */ | |
19baf4b4 | 1907 | static const char * const codes[] = |
1908 | { /* Request code strings */ | |
a8374e1e | 1909 | NULL, |
1910 | "OPTIONS", | |
1911 | "GET", | |
1912 | NULL, | |
1913 | "HEAD", | |
1914 | "POST", | |
1915 | NULL, | |
1916 | NULL, | |
1917 | "PUT", | |
1918 | NULL, | |
1919 | "DELETE", | |
1920 | "TRACE", | |
1921 | "CLOSE" | |
1922 | }; | |
19baf4b4 | 1923 | static const char hex[] = "0123456789ABCDEF"; |
a8374e1e | 1924 | /* Hex digits */ |
1925 | ||
1926 | ||
f992395d | 1927 | DEBUG_printf(("http_send(http=%p, request=HTTP_%s, uri=\"%s\")\n", |
1928 | http, codes[request], uri)); | |
1929 | ||
a8374e1e | 1930 | if (http == NULL || uri == NULL) |
1931 | return (-1); | |
1932 | ||
1933 | /* | |
1934 | * Encode the URI as needed... | |
1935 | */ | |
1936 | ||
d2e58bfa | 1937 | for (ptr = buf; *uri != '\0' && ptr < (buf + sizeof(buf) - 1); uri ++) |
a8374e1e | 1938 | if (*uri <= ' ' || *uri >= 127) |
1939 | { | |
d2e58bfa | 1940 | if (ptr < (buf + sizeof(buf) - 1)) |
1941 | *ptr ++ = '%'; | |
1942 | if (ptr < (buf + sizeof(buf) - 1)) | |
1943 | *ptr ++ = hex[(*uri >> 4) & 15]; | |
1944 | if (ptr < (buf + sizeof(buf) - 1)) | |
1945 | *ptr ++ = hex[*uri & 15]; | |
a8374e1e | 1946 | } |
1947 | else | |
1948 | *ptr ++ = *uri; | |
1949 | ||
1950 | *ptr = '\0'; | |
1951 | ||
f44afac9 | 1952 | /* |
1953 | * See if we had an error the last time around; if so, reconnect... | |
1954 | */ | |
1955 | ||
1956 | if (http->status == HTTP_ERROR || http->status >= HTTP_BAD_REQUEST) | |
1957 | httpReconnect(http); | |
1958 | ||
a8374e1e | 1959 | /* |
1960 | * Send the request header... | |
1961 | */ | |
1962 | ||
1963 | http->state = request; | |
5356dc5a | 1964 | if (request == HTTP_POST || request == HTTP_PUT) |
1965 | http->state ++; | |
1966 | ||
c1918ec5 | 1967 | http->status = HTTP_CONTINUE; |
1968 | ||
2dac9564 | 1969 | #ifdef HAVE_SSL |
2a0ef17a | 1970 | if (http->encryption == HTTP_ENCRYPT_REQUIRED && !http->tls) |
a75c006a | 1971 | { |
1972 | httpSetField(http, HTTP_FIELD_CONNECTION, "Upgrade"); | |
1973 | httpSetField(http, HTTP_FIELD_UPGRADE, "TLS/1.0,SSL/2.0,SSL/3.0"); | |
1974 | } | |
2dac9564 | 1975 | #endif /* HAVE_SSL */ |
a75c006a | 1976 | |
406971d9 | 1977 | if (httpPrintf(http, "%s %s HTTP/1.1\r\n", codes[request], buf) < 1) |
a8374e1e | 1978 | { |
f44afac9 | 1979 | http->status = HTTP_ERROR; |
1980 | return (-1); | |
a8374e1e | 1981 | } |
1982 | ||
1983 | for (i = 0; i < HTTP_FIELD_MAX; i ++) | |
1984 | if (http->fields[i][0] != '\0') | |
4a73831b | 1985 | { |
1986 | DEBUG_printf(("%s: %s\n", http_fields[i], http->fields[i])); | |
1987 | ||
406971d9 | 1988 | if (httpPrintf(http, "%s: %s\r\n", http_fields[i], http->fields[i]) < 1) |
f44afac9 | 1989 | { |
1990 | http->status = HTTP_ERROR; | |
1991 | return (-1); | |
1992 | } | |
4a73831b | 1993 | } |
a8374e1e | 1994 | |
406971d9 | 1995 | if (httpPrintf(http, "\r\n") < 1) |
f44afac9 | 1996 | { |
1997 | http->status = HTTP_ERROR; | |
a8374e1e | 1998 | return (-1); |
f44afac9 | 1999 | } |
a8374e1e | 2000 | |
2001 | httpClearFields(http); | |
2002 | ||
a8374e1e | 2003 | return (0); |
2004 | } | |
2005 | ||
2006 | ||
94cf7749 | 2007 | /* |
2008 | * 'http_wait()' - Wait for data available on a connection. | |
2009 | */ | |
2010 | ||
2011 | static int /* O - 1 if data is available, 0 otherwise */ | |
2012 | http_wait(http_t *http, /* I - HTTP data */ | |
2013 | int msec) /* I - Milliseconds to wait */ | |
2014 | { | |
2015 | #ifndef WIN32 | |
2016 | struct rlimit limit; /* Runtime limit */ | |
2017 | #endif /* !WIN32 */ | |
2018 | struct timeval timeout; /* Timeout */ | |
2019 | int nfds; /* Result from select() */ | |
4b271dae | 2020 | int set_size; /* Size of select set */ |
94cf7749 | 2021 | |
2022 | ||
f992395d | 2023 | DEBUG_printf(("http_wait(http=%p, msec=%d)\n", http, msec)); |
2024 | ||
94cf7749 | 2025 | /* |
2026 | * Check the SSL/TLS buffers for data first... | |
2027 | */ | |
2028 | ||
2029 | #ifdef HAVE_SSL | |
2030 | if (http->tls) | |
2031 | { | |
2032 | # ifdef HAVE_LIBSSL | |
2033 | if (SSL_pending((SSL *)(http->tls))) | |
2034 | return (1); | |
2035 | # elif defined(HAVE_GNUTLS) | |
2036 | if (gnutls_record_check_pending(((http_tls_t *)(http->tls))->session)) | |
2037 | return (1); | |
2038 | # elif defined(HAVE_CDSASSL) | |
2039 | size_t bytes; /* Bytes that are available */ | |
2040 | ||
2041 | if (!SSLGetBufferedReadSize((SSLContextRef)http->tls, &bytes) && bytes > 0) | |
2042 | return; | |
2043 | # endif /* HAVE_LIBSSL */ | |
2044 | } | |
2045 | #endif /* HAVE_SSL */ | |
2046 | ||
2047 | /* | |
2048 | * Then try doing a select() to poll the socket... | |
2049 | */ | |
2050 | ||
2051 | if (!http->input_set) | |
2052 | { | |
2053 | #ifdef WIN32 | |
2054 | /* | |
2055 | * Windows has a fixed-size select() structure, different (surprise, | |
2056 | * surprise!) from all UNIX implementations. Just allocate this | |
2057 | * fixed structure... | |
2058 | */ | |
2059 | ||
2060 | http->input_set = calloc(1, sizeof(fd_set)); | |
2061 | #else | |
2062 | /* | |
2063 | * Allocate the select() input set based upon the max number of file | |
2064 | * descriptors available for this process... | |
2065 | */ | |
2066 | ||
2067 | getrlimit(RLIMIT_NOFILE, &limit); | |
2068 | ||
60948dc7 | 2069 | set_size = (limit.rlim_cur + 31) / 8 + 4; |
4b271dae | 2070 | if (set_size < sizeof(fd_set)) |
2071 | set_size = sizeof(fd_set); | |
2072 | ||
2073 | http->input_set = calloc(1, set_size); | |
94cf7749 | 2074 | #endif /* WIN32 */ |
2075 | ||
2076 | if (!http->input_set) | |
2077 | return (0); | |
2078 | } | |
2079 | ||
2080 | FD_SET(http->fd, http->input_set); | |
2081 | ||
2082 | if (msec >= 0) | |
2083 | { | |
2084 | timeout.tv_sec = msec / 1000; | |
2085 | timeout.tv_usec = (msec % 1000) * 1000; | |
2086 | ||
2087 | nfds = select(http->fd + 1, http->input_set, NULL, NULL, &timeout); | |
2088 | } | |
2089 | else | |
2090 | nfds = select(http->fd + 1, http->input_set, NULL, NULL, NULL); | |
2091 | ||
2092 | FD_CLR(http->fd, http->input_set); | |
2093 | ||
2094 | return (nfds > 0); | |
2095 | } | |
2096 | ||
2097 | ||
2dac9564 | 2098 | #ifdef HAVE_SSL |
2a0ef17a | 2099 | /* |
2100 | * 'http_upgrade()' - Force upgrade to TLS encryption. | |
2101 | */ | |
2102 | ||
2103 | static int /* O - Status of connection */ | |
2104 | http_upgrade(http_t *http) /* I - HTTP data */ | |
2105 | { | |
1c6682dd | 2106 | int ret; /* Return value */ |
2107 | http_t myhttp; /* Local copy of HTTP data */ | |
2108 | ||
2a0ef17a | 2109 | |
1c6682dd | 2110 | DEBUG_printf(("http_upgrade(%p)\n", http)); |
2111 | ||
2112 | /* | |
2113 | * Copy the HTTP data to a local variable so we can do the OPTIONS | |
2114 | * request without interfering with the existing request data... | |
2115 | */ | |
2116 | ||
2117 | memcpy(&myhttp, http, sizeof(myhttp)); | |
2a0ef17a | 2118 | |
2119 | /* | |
2120 | * Send an OPTIONS request to the server, requiring SSL or TLS | |
2121 | * encryption on the link... | |
2122 | */ | |
2123 | ||
1c6682dd | 2124 | httpClearFields(&myhttp); |
2125 | httpSetField(&myhttp, HTTP_FIELD_CONNECTION, "upgrade"); | |
2126 | httpSetField(&myhttp, HTTP_FIELD_UPGRADE, "TLS/1.0, SSL/2.0, SSL/3.0"); | |
2127 | ||
2128 | if ((ret = httpOptions(&myhttp, "*")) == 0) | |
2129 | { | |
2130 | /* | |
2131 | * Wait for the secure connection... | |
2132 | */ | |
2133 | ||
2134 | while (httpUpdate(&myhttp) == HTTP_CONTINUE); | |
2135 | } | |
2136 | ||
2137 | httpFlush(&myhttp); | |
2a0ef17a | 2138 | |
2139 | /* | |
1c6682dd | 2140 | * Copy the HTTP data back over, if any... |
2a0ef17a | 2141 | */ |
2142 | ||
1c6682dd | 2143 | http->fd = myhttp.fd; |
2144 | http->error = myhttp.error; | |
2145 | http->activity = myhttp.activity; | |
2146 | http->status = myhttp.status; | |
2147 | http->version = myhttp.version; | |
2148 | http->keep_alive = myhttp.keep_alive; | |
2149 | http->used = myhttp.used; | |
2a0ef17a | 2150 | |
1c6682dd | 2151 | if (http->used) |
2152 | memcpy(http->buffer, myhttp.buffer, http->used); | |
2153 | ||
2154 | http->auth_type = myhttp.auth_type; | |
2155 | http->nonce_count = myhttp.nonce_count; | |
2156 | ||
2157 | memcpy(http->nonce, myhttp.nonce, sizeof(http->nonce)); | |
2a0ef17a | 2158 | |
1c6682dd | 2159 | http->tls = myhttp.tls; |
2160 | http->encryption = myhttp.encryption; | |
2161 | ||
2162 | /* | |
2163 | * See if we actually went secure... | |
2164 | */ | |
2165 | ||
2166 | if (!http->tls) | |
2a0ef17a | 2167 | { |
1c6682dd | 2168 | /* |
2169 | * Server does not support HTTP upgrade... | |
2170 | */ | |
2a0ef17a | 2171 | |
1c6682dd | 2172 | DEBUG_puts("Server does not support HTTP upgrade!"); |
2a0ef17a | 2173 | |
6fe7f729 | 2174 | # ifdef WIN32 |
2a0ef17a | 2175 | closesocket(http->fd); |
6fe7f729 | 2176 | # else |
2a0ef17a | 2177 | close(http->fd); |
6fe7f729 | 2178 | # endif |
2a0ef17a | 2179 | |
1c6682dd | 2180 | http->fd = -1; |
2181 | ||
2a0ef17a | 2182 | return (-1); |
2183 | } | |
1c6682dd | 2184 | else |
2185 | return (ret); | |
2a0ef17a | 2186 | } |
2a0ef17a | 2187 | |
2188 | ||
3840d6ba | 2189 | /* |
2dac9564 | 2190 | * 'http_setup_ssl()' - Set up SSL/TLS support on a connection. |
2191 | */ | |
2192 | ||
2193 | static int /* O - Status of connection */ | |
2194 | http_setup_ssl(http_t *http) /* I - HTTP data */ | |
2195 | { | |
6fe7f729 | 2196 | # ifdef HAVE_LIBSSL |
2dac9564 | 2197 | SSL_CTX *context; /* Context for encryption */ |
2198 | SSL *conn; /* Connection for encryption */ | |
6fe7f729 | 2199 | # elif defined(HAVE_GNUTLS) |
2dac9564 | 2200 | http_tls_t *conn; /* TLS session object */ |
6fe7f729 | 2201 | gnutls_certificate_client_credentials *credentials; |
2202 | /* TLS credentials */ | |
2203 | # elif defined(HAVE_CDSASSL) | |
2204 | SSLContextRef conn; /* Context for encryption */ | |
2205 | OSStatus error; /* Error info */ | |
2206 | # endif /* HAVE_LIBSSL */ | |
2dac9564 | 2207 | |
2dac9564 | 2208 | |
a9b9fc66 | 2209 | DEBUG_printf(("http_setup_ssl(http=%p)\n", http)); |
2210 | ||
6fe7f729 | 2211 | # ifdef HAVE_LIBSSL |
2dac9564 | 2212 | context = SSL_CTX_new(SSLv23_client_method()); |
2213 | conn = SSL_new(context); | |
2214 | ||
2215 | SSL_set_fd(conn, http->fd); | |
2216 | if (SSL_connect(conn) != 1) | |
2217 | { | |
a9b9fc66 | 2218 | # ifdef DEBUG |
2219 | unsigned long error; /* Error code */ | |
2220 | ||
2221 | while ((error = ERR_get_error()) != 0) | |
2222 | printf("http_setup_ssl: %s\n", ERR_error_string(error, NULL)); | |
2223 | # endif /* DEBUG */ | |
2224 | ||
2dac9564 | 2225 | SSL_CTX_free(context); |
2226 | SSL_free(conn); | |
2227 | ||
6fe7f729 | 2228 | # ifdef WIN32 |
2dac9564 | 2229 | http->error = WSAGetLastError(); |
6fe7f729 | 2230 | # else |
2dac9564 | 2231 | http->error = errno; |
6fe7f729 | 2232 | # endif /* WIN32 */ |
2dac9564 | 2233 | http->status = HTTP_ERROR; |
2234 | ||
2235 | return (HTTP_ERROR); | |
2236 | } | |
2237 | ||
6fe7f729 | 2238 | # elif defined(HAVE_GNUTLS) |
2dac9564 | 2239 | conn = (http_tls_t *)malloc(sizeof(http_tls_t)); |
6fe7f729 | 2240 | |
2dac9564 | 2241 | if (conn == NULL) |
2242 | { | |
2243 | http->error = errno; | |
2244 | http->status = HTTP_ERROR; | |
2245 | ||
2246 | return (-1); | |
2247 | } | |
6fe7f729 | 2248 | |
2dac9564 | 2249 | credentials = (gnutls_certificate_client_credentials *) |
6fe7f729 | 2250 | malloc(sizeof(gnutls_certificate_client_credentials)); |
2dac9564 | 2251 | if (credentials == NULL) |
2252 | { | |
2253 | free(conn); | |
2254 | ||
2255 | http->error = errno; | |
2256 | http->status = HTTP_ERROR; | |
2257 | ||
2258 | return (-1); | |
2259 | } | |
2260 | ||
2261 | gnutls_certificate_allocate_credentials(credentials); | |
2262 | ||
2263 | gnutls_init(&(conn->session), GNUTLS_CLIENT); | |
2264 | gnutls_set_default_priority(conn->session); | |
2265 | gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials); | |
2266 | gnutls_transport_set_ptr(conn->session, http->fd); | |
2267 | ||
2268 | if ((gnutls_handshake(conn->session)) != GNUTLS_E_SUCCESS) | |
2269 | { | |
2270 | http->error = errno; | |
2271 | http->status = HTTP_ERROR; | |
2272 | ||
2273 | return (-1); | |
2274 | } | |
2275 | ||
2276 | conn->credentials = credentials; | |
2277 | ||
6fe7f729 | 2278 | # elif defined(HAVE_CDSASSL) |
2279 | error = SSLNewContext(false, &conn); | |
2280 | ||
2281 | if (!error) | |
2282 | error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc); | |
2283 | ||
2284 | if (!error) | |
2285 | error = SSLSetConnection(conn, (SSLConnectionRef)http->fd); | |
2286 | ||
2287 | if (!error) | |
2288 | error = SSLSetAllowsExpiredCerts(conn, true); | |
2289 | ||
2290 | if (!error) | |
2291 | error = SSLSetAllowsAnyRoot(conn, true); | |
2292 | ||
2293 | if (!error) | |
2294 | error = SSLHandshake(conn); | |
2295 | ||
2296 | if (error != 0) | |
2297 | { | |
2298 | http->error = error; | |
2299 | http->status = HTTP_ERROR; | |
2300 | ||
2301 | SSLDisposeContext(conn); | |
2302 | ||
2303 | close(http->fd); | |
2304 | ||
2305 | return (-1); | |
2306 | } | |
2307 | # endif /* HAVE_CDSASSL */ | |
2dac9564 | 2308 | |
2309 | http->tls = conn; | |
2310 | return (0); | |
2311 | } | |
2312 | ||
2313 | ||
2314 | /* | |
2315 | * 'http_shutdown_ssl()' - Shut down SSL/TLS on a connection. | |
2316 | */ | |
2317 | ||
2318 | static void | |
2319 | http_shutdown_ssl(http_t *http) /* I - HTTP data */ | |
2320 | { | |
6fe7f729 | 2321 | # ifdef HAVE_LIBSSL |
2dac9564 | 2322 | SSL_CTX *context; /* Context for encryption */ |
2323 | SSL *conn; /* Connection for encryption */ | |
2324 | ||
6fe7f729 | 2325 | |
2dac9564 | 2326 | conn = (SSL *)(http->tls); |
2327 | context = SSL_get_SSL_CTX(conn); | |
2328 | ||
2329 | SSL_shutdown(conn); | |
2330 | SSL_CTX_free(context); | |
2331 | SSL_free(conn); | |
6fe7f729 | 2332 | |
2333 | # elif defined(HAVE_GNUTLS) | |
2334 | http_tls_t *conn; /* Encryption session */ | |
2335 | gnutls_certificate_client_credentials *credentials; | |
2336 | /* TLS credentials */ | |
2337 | ||
2dac9564 | 2338 | |
2339 | conn = (http_tls_t *)(http->tls); | |
2340 | credentials = (gnutls_certificate_client_credentials *)(conn->credentials); | |
2341 | ||
2342 | gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); | |
2343 | gnutls_deinit(conn->session); | |
2344 | gnutls_certificate_free_credentials(*credentials); | |
2345 | free(credentials); | |
2346 | free(conn); | |
6fe7f729 | 2347 | |
2348 | # elif defined(HAVE_CDSASSL) | |
2349 | SSLClose((SSLContextRef)http->tls); | |
2350 | SSLDisposeContext((SSLContextRef)http->tls); | |
2351 | # endif /* HAVE_LIBSSL */ | |
2dac9564 | 2352 | |
2353 | http->tls = NULL; | |
2354 | } | |
2355 | ||
2356 | ||
2357 | /* | |
2358 | * 'http_read_ssl()' - Read from a SSL/TLS connection. | |
2359 | */ | |
2360 | ||
2361 | static int /* O - Bytes read */ | |
2362 | http_read_ssl(http_t *http, /* I - HTTP data */ | |
2363 | char *buf, /* I - Buffer to store data */ | |
2364 | int len) /* I - Length of buffer */ | |
2365 | { | |
6fe7f729 | 2366 | # if defined(HAVE_LIBSSL) |
2dac9564 | 2367 | return (SSL_read((SSL *)(http->tls), buf, len)); |
6fe7f729 | 2368 | |
2369 | # elif defined(HAVE_GNUTLS) | |
2dac9564 | 2370 | return (gnutls_record_recv(((http_tls_t *)(http->tls))->session, buf, len)); |
6fe7f729 | 2371 | |
2372 | # elif defined(HAVE_CDSASSL) | |
2373 | OSStatus error; /* Error info */ | |
2374 | size_t processed; /* Number of bytes processed */ | |
2375 | ||
2376 | ||
2377 | error = SSLRead((SSLContextRef)http->tls, buf, len, &processed); | |
2378 | ||
2379 | if (error == 0) | |
2380 | return (processed); | |
2381 | else | |
2382 | { | |
2383 | http->error = error; | |
2384 | ||
2385 | return (-1); | |
2386 | } | |
2387 | # endif /* HAVE_LIBSSL */ | |
2dac9564 | 2388 | } |
2389 | ||
2390 | ||
2391 | /* | |
2392 | * 'http_write_ssl()' - Write to a SSL/TLS connection. | |
2393 | */ | |
2394 | ||
2395 | static int /* O - Bytes written */ | |
2396 | http_write_ssl(http_t *http, /* I - HTTP data */ | |
2397 | const char *buf, /* I - Buffer holding data */ | |
2398 | int len) /* I - Length of buffer */ | |
2399 | { | |
6fe7f729 | 2400 | # if defined(HAVE_LIBSSL) |
2dac9564 | 2401 | return (SSL_write((SSL *)(http->tls), buf, len)); |
6fe7f729 | 2402 | |
2403 | # elif defined(HAVE_GNUTLS) | |
2dac9564 | 2404 | return (gnutls_record_send(((http_tls_t *)(http->tls))->session, buf, len)); |
6fe7f729 | 2405 | # elif defined(HAVE_CDSASSL) |
2406 | OSStatus error; /* Error info */ | |
2407 | size_t processed; /* Number of bytes processed */ | |
2408 | ||
2409 | ||
2410 | error = SSLWrite((SSLContextRef)http->tls, buf, len, &processed); | |
2411 | ||
2412 | if (error == 0) | |
2413 | return (processed); | |
2414 | else | |
2415 | { | |
2416 | http->error = error; | |
2417 | return (-1); | |
2418 | } | |
2419 | # endif /* HAVE_LIBSSL */ | |
2dac9564 | 2420 | } |
2421 | ||
6fe7f729 | 2422 | |
2423 | # if defined(HAVE_CDSASSL) | |
2424 | /* | |
2425 | * 'CDSAReadFunc()' - Read function for CDSA decryption code. | |
2426 | */ | |
2427 | ||
2428 | static OSStatus /* O - -1 on error, 0 on success */ | |
2429 | CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
2430 | void *data, /* I - Data buffer */ | |
2431 | size_t *dataLength) /* IO - Number of bytes */ | |
2432 | { | |
2433 | ssize_t bytes; /* Number of bytes read */ | |
2434 | ||
2435 | ||
2436 | bytes = recv((int)connection, data, *dataLength, 0); | |
2437 | if (bytes >= 0) | |
2438 | { | |
2439 | *dataLength = bytes; | |
2440 | return (0); | |
2441 | } | |
2442 | else | |
2443 | return (-1); | |
2444 | } | |
2445 | ||
2446 | ||
2447 | /* | |
2448 | * 'CDSAWriteFunc()' - Write function for CDSA encryption code. | |
2449 | */ | |
2450 | ||
2451 | static OSStatus /* O - -1 on error, 0 on success */ | |
2452 | CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
2453 | const void *data, /* I - Data buffer */ | |
2454 | size_t *dataLength) /* IO - Number of bytes */ | |
2455 | { | |
2456 | ssize_t bytes; | |
2457 | ||
2458 | ||
2459 | bytes = write((int)connection, data, *dataLength); | |
2460 | if (bytes >= 0) | |
2461 | { | |
2462 | *dataLength = bytes; | |
2463 | return (0); | |
2464 | } | |
2465 | else | |
2466 | return (-1); | |
2467 | } | |
2468 | # endif /* HAVE_CDSASSL */ | |
2dac9564 | 2469 | #endif /* HAVE_SSL */ |
2470 | ||
6fe7f729 | 2471 | |
2dac9564 | 2472 | /* |
6269b3e2 | 2473 | * End of "$Id: http.c,v 1.138 2004/06/29 01:02:45 mike Exp $". |
3840d6ba | 2474 | */ |