?? http.c
字號:
/*- * Copyright (c) 2000-2004 Dag-Erling Co飀an Sm鴕grav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include <sys/cdefs.h>/* * The following copyright applies to the base64 code: * *- * Copyright 1997 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include <sys/param.h>#include <sys/socket.h>#include <ctype.h>#include <err.h>#include <errno.h>#include <locale.h>#include <netdb.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <unistd.h>#include "../include/rmlibhttp.h"#include "cache.h"#include "common.h"#include "httperr.h"/* Maximum number of redirects to follow */#define MAX_REDIRECT 5#define CACHE /* Symbolic names for reply codes we care about */#define HTTP_CONTINUE 100#define HTTP_OK 200#define HTTP_PARTIAL 206#define HTTP_MOVED_PERM 301#define HTTP_MOVED_TEMP 302#define HTTP_SEE_OTHER 303#define HTTP_NEED_AUTH 401#define HTTP_NEED_PROXY_AUTH 407#define HTTP_BAD_RANGE 416#define HTTP_PROTOCOL_ERROR 999#define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ || (xyz) == HTTP_MOVED_TEMP \ || (xyz) == HTTP_SEE_OTHER)#define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)/***************************************************************************** * I/O functions for decoding chunked streams */#define CONN(io) ((io)->request->conn)struct httpio{ struct http_request_s *request; /* HTTP request */ RMint32 eof; /* end-of-file flag */ RMint32 error; /* error flag */ RMuint32 chunksize; /* remaining size of current chunk */#ifdef HTTP_DEBUG_ENABLED RMuint32 total;#endif};static time_t my_timegm (struct tm *tm) { time_t ret; RMascii *tz; tz = getenv("TZ"); setenv("TZ", "", 1); tzset(); ret = mktime(tm); if (tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); return ret;}struct http_request_s { struct url *url; const RMascii *op; struct url *purl; struct url_stat *us; RMHTTPFlags flags; RMint32 chunked; RMint32 direct; RMint32 noredirect; RMint32 verbose; RMint32 need_auth; RMint32 open; RMint32 need_reopen; RMint64 offset; RMint64 clength; RMint64 length; RMint64 size; /* Total size of the requested file */ time_t mtime; RMint32 e; struct url *new; conn_t *conn; RMascii *host; struct cache_s *cache; RMint32 last_read_short; RMascii *custom_header; /* Custom header for this request */ void *custom_cookie; /* Used by custom hooks */ HttpHookOps *custom_hooks; /* Used by custom hooks */};static RMint32 __http_get_reply(struct http_request_s *request);static RMint32 __http_get_headers(struct http_request_s *request);static RMint32 __http_send_request(struct http_request_s *request);static RMint32 __http_connect(struct http_request_s *request);static RMint32 _http_single_request(struct http_request_s *request);static RMint32 _http_seekn(void *cookie, RMint64 *position, RMint32 whence);static RMint32 _http_closefn(void *v);static RMint32 _http_readfn(void *v, RMuint8 *buf, RMuint32 len);typedef struct { void *readfn; void *writefnL; void *seekfn; void *closefn;} httpFileOps_s;static httpFileOps_s p_httpFileOps = { (void *) _http_readfn, (void *) NULL, (void *) _http_seekn, (void *) _http_closefn };void *httpFileOps = &p_httpFileOps;/* Custom header */static RMascii *fetchCustomHeader = NULL;/* Custom hooks */static void *fetchCustomCookie = NULL;static HttpHookOps *fetchCustomHooks = NULL;/* * Get next chunk header */static RMint32_http_new_chunk(struct httpio *io){ RMascii *p; if (_fetch_getln(CONN(io)) == -1) return (-1); if (CONN(io)->buflen < 2 || !isxdigit(*CONN(io)->buf)) return (-1); for (p = (RMascii *)CONN(io)->buf; *p && !isspace(*p); ++p) { if (*p == ';') break; if (!isxdigit(*p)) return (-1); if (isdigit(*p)) { io->chunksize = io->chunksize * 16 + *p - '0'; } else { io->chunksize = io->chunksize * 16 + 10 + tolower(*p) - 'a'; } }#ifdef HTTP_DEBUG_ENABLED io->total += io->chunksize; if (io->chunksize == 0) RMDBGLOG((HTTPDEBUG, "%s(): end of last chunk\n", __func__)); else RMDBGLOG((HTTPDEBUG, "%s(): new chunk: %lu (%lu)\n", __func__, (RMuint32)io->chunksize, (RMuint32)io->total));#endif return (io->chunksize);}/* * Fill the given buffer, do chunk decoding on the fly */static RMint32_http_fillbuf(struct httpio *io, RMuint32 len, RMuint8 *buffer){ RMint32 read; if (io->error) return (-1); if (io->eof) return (0); if (io->request->chunked == 0) { read = _fetch_read(CONN(io), buffer, len); if (read < 0){ io->error = 1; return (-1); } return (read); } if (io->chunksize == 0) { switch (_http_new_chunk(io)) { case -1: io->error = 1; return (-1); case 0: io->eof = 1; return (0); } } if (len > io->chunksize) len = io->chunksize; read = _fetch_read(CONN(io), buffer, len); if (read == -1) { io->error = 1; return (-1); } io->chunksize -= read; if (io->chunksize == 0) { RMascii endl[2]; if (_fetch_read(CONN(io), (RMuint8 *)endl, 2) != 2 || endl[0] != '\r' || endl[1] != '\n'){ RMDBGLOG((ENABLE,"No valid chunk separator\n")); return (-1); } } return (read);}static RMint32 __http_read(void *v, RMuint8 *buf){ struct httpio *io = (struct httpio *)v; struct http_request_s *request = io->request; RMint32 status; RMint32 len = request->url->offset_end - request->url->offset + 1; RMint32 nread = 0; RMint32 total_read = 0; RMint32 hook_read = 0; /* Used by custom hooks */ RMuint8 *hook_buf = NULL; /* Used by custom hooks */ if (len == 0) return 0; while(1) { if (request->need_reopen){ RMDBGLOG((HTTPDEBUG,"Need to open a new connection\n")); status = _fetch_close(request->conn); if (status < 0) perror("Error closing previous connection, keep going"); SAFE(__http_connect(request)); /* New connection */ request->need_reopen = 0; /* Reset the chunk size */ io->chunksize = 0; } status = __http_send_request(request); if (status < 0){ RMDBGLOG((HTTPDEBUG,"Error sending request : %ld, %ld, %ld\n", status, request->conn->err, fetchLastErrCode)); if (fetchLastErrCode == FETCH_OK || fetchLastErrCode == FETCH_PIPE){ /* EPIPE possible if connection is closed by peer */ request->need_reopen = 1; continue; }else return(-1); }get_reply: status = __http_get_reply(request); if (status < 0){ RMDBGLOG((HTTPDEBUG,"Error getting reply : %ld, %ld, %ld\n", status, request->conn->err, fetchLastErrCode)); if (fetchLastErrCode == FETCH_OK){ request->need_reopen = 1; continue; }else return(-1); } status = __http_get_headers(request); if (status < 0){ RMDBGLOG((HTTPDEBUG,"Error getting headers : %ld, %ld, %ld\n", status, request->conn->err, fetchLastErrCode)); if (fetchLastErrCode == FETCH_OK){ request->need_reopen = 1; continue; }else return(-1); } if (request->conn->err == HTTP_CONTINUE){ goto get_reply; } /* If custom decryption hooks are being used, ... */ if ((request->custom_cookie != NULL) && (request->custom_hooks != NULL) && (request->custom_hooks->preread != NULL) && (request->custom_hooks->postread != NULL)) { RMint32 new_read_size; RMint32 more_data = 0; RMint32 result; /* Resync the decryption block boundary, if necessary */ if (request->custom_hooks->reopen != NULL) { if ((result = request->custom_hooks->reopen(request->custom_cookie)) < 0) return result; } /* Truncate read size if necessary to prevent reading beyond a decryption block boundary */ if ((new_read_size = request->custom_hooks->preread(request->custom_cookie, buf, len)) <= 0) return new_read_size; /* Read the start of the decryption block */ if ((nread = _http_fillbuf(io, new_read_size, buf)) <= 0) return(-1); /* Because of HTTP chunking, a second read may be necessary */ if (nread < new_read_size) { RMint32 delta_read = new_read_size - nread; if (_http_fillbuf(io, delta_read, buf + delta_read) != delta_read) return(-1); } /* Decrypt the data */ if ((hook_read = request->custom_hooks->postread(request->custom_cookie, buf, new_read_size, &more_data)) <= 0) return hook_read; /* Adjust the pointers */ len -= hook_read; buf += hook_read; hook_buf = buf; } while (len > 0){ nread = _http_fillbuf(io, len, buf); if (nread <= 0){ RMDBGLOG((ENABLE,"nread <= 0 : %ld\n", total_read)); /* For some servers (Intel DTCPIP for example), * a bad range is not a problem, so the request * above will not fail at EOF, but the read * should at least ... */ if (request->last_read_short){ /* EOF */ io->eof = 1; return total_read; } break; } total_read += nread; len -= nread; buf += nread; } /* If custom decryption hooks are being used, ... */ if ((request->custom_cookie != NULL) && (request->custom_hooks != NULL) && (request->custom_hooks->preread != NULL) && (request->custom_hooks->postread != NULL)) { RMint32 more_data; RMint32 result; /* Decrypt the data */ if ((result = request->custom_hooks->postread(request->custom_cookie, hook_buf, total_read, &more_data)) <= 0) return result; /* The total read size includes the first read */ total_read += hook_read; } if (!total_read && io->error){ RMDBGLOG((ENABLE,"IO error %ld\n", io->error)); return (-1); } if (nread == 0){ /* Might be end of file or peer closed connection (time * out, ...), so try to reopen, if it was end of file, * it will fail */ request->need_reopen = 1; request->last_read_short = 1; continue; } request->last_read_short = 0; return(total_read); } return(-1);}#define mmin(a,b) ((a)<(b))?(a):(b);static RMint32 __http_read_cached(void *v, RMuint8 *buf, RMuint32 len){ struct httpio *io = (struct httpio *)v; struct http_request_s *request = io->request; RMint32 cached, status; RMuint8 *buf_cache; RMint32 plen; RMint64 start_save, end_save; RMint64 start, offset; if ((request->url->offset > (request->size - 1)) || len == 0) return(0); /* Ensure request offset are valid */ request->url->offset_end = request->url->offset + len - 1; if (request->url->offset_end > ( request->size - 1 )){
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -