370 lines
7.8 KiB
C
370 lines
7.8 KiB
C
|
/* request.c
|
||
|
*
|
||
|
* This file is part of httpd.
|
||
|
*
|
||
|
* 02/17/1996 Michael Temari <Michael@TemWare.Com>
|
||
|
* 07/07/1996 Initial Release Michael Temari <Michael@TemWare.Com>
|
||
|
* 12/29/2002 Michael Temari <Michael@TemWare.Com>
|
||
|
*
|
||
|
*/
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <signal.h>
|
||
|
#include <ctype.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <time.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <pwd.h>
|
||
|
#ifdef _MINIX
|
||
|
#include <minix/minlib.h>
|
||
|
#endif
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include "http.h"
|
||
|
#include "utility.h"
|
||
|
#include "config.h"
|
||
|
|
||
|
_PROTOTYPE(static void Timeout, (int sig));
|
||
|
_PROTOTYPE(static int getline, (char *buffer, int size));
|
||
|
_PROTOTYPE(static void authorize, (char *p, struct http_request *rq));
|
||
|
_PROTOTYPE(static void decurl, (char *u));
|
||
|
|
||
|
static int TimeOut;
|
||
|
|
||
|
static void Timeout(sig)
|
||
|
int sig;
|
||
|
{
|
||
|
TimeOut = 1;
|
||
|
}
|
||
|
|
||
|
static int getline(buffer, size)
|
||
|
char *buffer;
|
||
|
int size;
|
||
|
{
|
||
|
char *p;
|
||
|
int s;
|
||
|
|
||
|
p = buffer;
|
||
|
|
||
|
while(p < (buffer + size - 1)) {
|
||
|
TimeOut = 0;
|
||
|
signal(SIGALRM, Timeout);
|
||
|
alarm(5*60);
|
||
|
s = read(0, p, 1);
|
||
|
alarm(0);
|
||
|
if(TimeOut)
|
||
|
return(-1);
|
||
|
if(s != 1)
|
||
|
return(-1);
|
||
|
if(*p == '\n') break;
|
||
|
p++;
|
||
|
}
|
||
|
*++p = '\0';
|
||
|
|
||
|
p = &buffer[strlen(buffer) - 1];
|
||
|
if(p >= buffer && (*p == '\r' || *p == '\n')) *p-- ='\0';
|
||
|
if(p >= buffer && (*p == '\r' || *p == '\n')) *p-- ='\0';
|
||
|
|
||
|
return(strlen(buffer));
|
||
|
}
|
||
|
|
||
|
static void authorize(p, rq)
|
||
|
char *p;
|
||
|
struct http_request *rq;
|
||
|
{
|
||
|
char *s;
|
||
|
|
||
|
if(toupper(*p++) == 'B' &&
|
||
|
toupper(*p++) == 'A' &&
|
||
|
toupper(*p++) == 'S' &&
|
||
|
toupper(*p++) == 'I' &&
|
||
|
toupper(*p++) == 'C' &&
|
||
|
toupper(*p++) == ' ') ;
|
||
|
else
|
||
|
return;
|
||
|
|
||
|
s = decode64(p);
|
||
|
|
||
|
if((p = strchr(s, ':')) == (char *)NULL)
|
||
|
p = "";
|
||
|
else
|
||
|
*p++ = '\0';
|
||
|
|
||
|
strncpy(rq->authuser, s, sizeof(rq->authuser));
|
||
|
strncpy(rq->authpass, p, sizeof(rq->authpass));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int getrequest(rq)
|
||
|
struct http_request *rq;
|
||
|
{
|
||
|
static char line[4096];
|
||
|
char *p, *p2, *ps;
|
||
|
int s, len;
|
||
|
struct vhost *ph;
|
||
|
|
||
|
/* get request, it may be simple */
|
||
|
|
||
|
s = getline(line, sizeof(line));
|
||
|
if(s < 0)
|
||
|
return(-1);
|
||
|
|
||
|
if(dbglog != (FILE *)NULL) {
|
||
|
fprintf(dbglog, "REQUEST: %s\n", line);
|
||
|
fflush(dbglog);
|
||
|
}
|
||
|
|
||
|
/* clear http_request */
|
||
|
memset(rq, 0, sizeof(*rq));
|
||
|
rq->ifmodsince = (time_t) -1;
|
||
|
|
||
|
/* assume simple request */
|
||
|
rq->type = HTTP_REQUEST_TYPE_SIMPLE;
|
||
|
|
||
|
/* parse the method */
|
||
|
p = line;
|
||
|
while(*p && !LWS(*p)) {
|
||
|
*p = toupper(*p);
|
||
|
p++;
|
||
|
}
|
||
|
if(*p) *p++ = '\0';
|
||
|
|
||
|
if(!strcmp(line, "GET"))
|
||
|
rq->method = HTTP_METHOD_GET; else
|
||
|
if(!strcmp(line, "HEAD"))
|
||
|
rq->method = HTTP_METHOD_HEAD; else
|
||
|
if(!strcmp(line, "POST"))
|
||
|
rq->method = HTTP_METHOD_POST; else
|
||
|
if(!strcmp(line, "PUT"))
|
||
|
rq->method = HTTP_METHOD_PUT; else
|
||
|
#if 0
|
||
|
if(!strcmp(line, "OPTIONS"))
|
||
|
rq->method = HTTP_METHOD_OPTIONS; else
|
||
|
if(!strcmp(line, "PATCH"))
|
||
|
rq->method = HTTP_METHOD_PATCH; else
|
||
|
if(!strcmp(line, "COPY"))
|
||
|
rq->method = HTTP_METHOD_COPY; else
|
||
|
if(!strcmp(line, "MOVE"))
|
||
|
rq->method = HTTP_METHOD_MOVE; else
|
||
|
if(!strcmp(line, "DELETE"))
|
||
|
rq->method = HTTP_METHOD_DELETE; else
|
||
|
if(!strcmp(line, "LINK"))
|
||
|
rq->method = HTTP_METHOD_LINK; else
|
||
|
if(!strcmp(line, "UNLINK"))
|
||
|
rq->method = HTTP_METHOD_UNLINK; else
|
||
|
if(!strcmp(line, "TRACE"))
|
||
|
rq->method = HTTP_METHOD_TRACE; else
|
||
|
if(!strcmp(line, "WRAPPED"))
|
||
|
rq->method = HTTP_METHOD_WRAPPED; else
|
||
|
#endif
|
||
|
rq->method = HTTP_METHOD_UNKNOWN;
|
||
|
|
||
|
/* parse the requested URI */
|
||
|
p2 = rq->uri;
|
||
|
len = sizeof(rq->uri) - 1;
|
||
|
while(*p && !LWS(*p) && len > 0) {
|
||
|
*p2++ = *p++;
|
||
|
len--;
|
||
|
}
|
||
|
*p2 = '\0';
|
||
|
|
||
|
/* eat up any leftovers if uri was too big */
|
||
|
while(*p && !LWS(*p))
|
||
|
p++;
|
||
|
|
||
|
/* save for continued processing later */
|
||
|
ps = p;
|
||
|
|
||
|
/* parse the requested URL */
|
||
|
p = rq->uri;
|
||
|
p2 = rq->url;
|
||
|
len = sizeof(rq->url) - 1;
|
||
|
while(*p && !LWS(*p) && *p != '?' && len > 0) {
|
||
|
*p2++ = *p++;
|
||
|
len--;
|
||
|
}
|
||
|
*p2 = '\0';
|
||
|
|
||
|
/* See if there is a query string */
|
||
|
if(*p == '?') {
|
||
|
p++;
|
||
|
p2 = rq->query;
|
||
|
len = sizeof(rq->query) - 1;
|
||
|
while(*p && !LWS(*p) && len > 0) {
|
||
|
*p2++ = *p++;
|
||
|
len--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* eat up any leftovers */
|
||
|
while(*p && !LWS(*p)) p++;
|
||
|
|
||
|
if(rq->url[0] == '\0') {
|
||
|
rq->url[0] = '/';
|
||
|
rq->url[1] = '\0';
|
||
|
}
|
||
|
|
||
|
/* url is a decoded copy of the uri */
|
||
|
decurl(rq->url);
|
||
|
|
||
|
/* restore and continue processing */
|
||
|
p = ps;
|
||
|
|
||
|
/* if this is true it is a simple request */
|
||
|
if(*p == '\0')
|
||
|
return(0);
|
||
|
|
||
|
/* parse HTTP version */
|
||
|
while(*p && LWS(*p)) p++;
|
||
|
if(toupper(*p++) != 'H') return(0);
|
||
|
if(toupper(*p++) != 'T') return(0);
|
||
|
if(toupper(*p++) != 'T') return(0);
|
||
|
if(toupper(*p++) != 'P') return(0);
|
||
|
if( *p++ != '/') return(0);
|
||
|
|
||
|
/* version major */
|
||
|
rq->vmajor = 0;
|
||
|
while((*p >= '0') && (*p <= '9'))
|
||
|
rq->vmajor = rq->vmajor * 10 + (*p++ - '0');
|
||
|
if(*p != '.')
|
||
|
return(0);
|
||
|
p++;
|
||
|
|
||
|
/* version minor */
|
||
|
rq->vminor = 0;
|
||
|
while((*p >= '0') && (*p <= '9'))
|
||
|
rq->vminor = rq->vminor * 10 + (*p++ - '0');
|
||
|
if(*p)
|
||
|
return(0);
|
||
|
|
||
|
rq->type = HTTP_REQUEST_TYPE_FULL;
|
||
|
|
||
|
p = rq->uri;
|
||
|
|
||
|
/* check if it is a proxy request */
|
||
|
if(toupper(*p++) == 'H' &&
|
||
|
toupper(*p++) == 'T' &&
|
||
|
toupper(*p++) == 'T' &&
|
||
|
toupper(*p++) == 'P' &&
|
||
|
toupper(*p++) == ':')
|
||
|
rq->type = HTTP_REQUEST_TYPE_PROXY;
|
||
|
|
||
|
/* parse any header fields */
|
||
|
while((s = getline(line, sizeof(line))) > 0) {
|
||
|
if(toupper(line[0]) == 'A' &&
|
||
|
toupper(line[1]) == 'U')
|
||
|
if(dbglog != (FILE *)NULL) {
|
||
|
fprintf(dbglog, "REQUEST: Authorization:\n");
|
||
|
fflush(dbglog);
|
||
|
} else ;
|
||
|
else
|
||
|
if(dbglog != (FILE *)NULL) {
|
||
|
fprintf(dbglog, "REQUEST: %s\n", line);
|
||
|
fflush(dbglog);
|
||
|
}
|
||
|
p = line;
|
||
|
while(*p && *p != ':') {
|
||
|
*p = toupper(*p);
|
||
|
p++;
|
||
|
}
|
||
|
if(*p != ':') continue; /* bad header field, skip it */
|
||
|
*p++ = '\0';
|
||
|
while(*p && LWS(*p)) p++;
|
||
|
|
||
|
/* header field value parsing here */
|
||
|
if(!strcmp(line, "HOST")) {
|
||
|
strncpy(rq->host, p, sizeof(rq->host));
|
||
|
p2 = strrchr(rq->host, ':');
|
||
|
if(p2 != (char *)NULL) {
|
||
|
*p2++ = '\0';
|
||
|
rq->port = atoi(p2);
|
||
|
}
|
||
|
/* if unknown virtual host then exit quietly */
|
||
|
for(ph = vhost; ph != NULL; ph = ph->next) {
|
||
|
if(!strcasecmp(ph->hname, "*")) break;
|
||
|
if(!strcasecmp(ph->hname, rq->host)) break;
|
||
|
}
|
||
|
if(rq->type != HTTP_REQUEST_TYPE_PROXY)
|
||
|
if(ph == NULL && vhost != NULL) return(1);
|
||
|
} else
|
||
|
if(!strcmp(line, "USER-AGENT"))
|
||
|
strncpy(rq->useragent, p, sizeof(rq->useragent)); else
|
||
|
if(!strcmp(line, "CONNECTION"))
|
||
|
rq->keepopen = strcasecmp(p, "Keep-Alive") ? 0 : 1; else
|
||
|
if(!strcmp(line, "IF-MODIFIED-SINCE"))
|
||
|
rq->ifmodsince = httptime(p); else
|
||
|
if(!strcmp(line, "CONTENT-LENGTH"))
|
||
|
rq->size = atol(p); else
|
||
|
if(!strcmp(line, "AUTHORIZATION")) {
|
||
|
strncpy(rq->wwwauth, p, sizeof(rq->wwwauth));
|
||
|
if(rq->type != HTTP_REQUEST_TYPE_PROXY)
|
||
|
authorize(p, rq);
|
||
|
} else
|
||
|
if(!strcmp(line, "PROXY-AUTHORIZATION")) {
|
||
|
if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
||
|
authorize(p, rq);
|
||
|
} else
|
||
|
if(!strcmp(line, "DATE"))
|
||
|
rq->msgdate = httptime(p); else
|
||
|
if(!strcmp(line, "COOKIE")) {
|
||
|
strncpy(rq->cookie, p, sizeof(rq->cookie)-1);
|
||
|
rq->cookie[sizeof(rq->cookie)-1] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(rq->type != HTTP_REQUEST_TYPE_PROXY)
|
||
|
if(*rq->host == '\0' && vhost != NULL) return(1);
|
||
|
|
||
|
if(dbglog != (FILE *)NULL && rq->authuser[0] != '\0') {
|
||
|
fprintf(dbglog, "REQUEST: AuthUser=%s\n", rq->authuser);
|
||
|
fflush(dbglog);
|
||
|
}
|
||
|
|
||
|
if(s < 0) {
|
||
|
fprintf(stderr, "httpd: getrequest: Error getline (header fields)\n");
|
||
|
return(-1);
|
||
|
}
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
static void decurl(u)
|
||
|
char *u;
|
||
|
{
|
||
|
char *p;
|
||
|
char h1, h2;
|
||
|
char c;
|
||
|
|
||
|
p = u;
|
||
|
while(*p) {
|
||
|
switch(*p) {
|
||
|
case '\0':
|
||
|
c = '\0';
|
||
|
break;
|
||
|
case '+':
|
||
|
c = ' ';
|
||
|
p++;
|
||
|
break;
|
||
|
case '%':
|
||
|
h1 = '0';
|
||
|
h2 = '0';
|
||
|
p++;
|
||
|
h1 = tolower(*p);
|
||
|
if(*p) p++;
|
||
|
h2 = tolower(*p);
|
||
|
if(*p) p++;
|
||
|
c = (h1 > '9') ? (10 + h1 - 'a') : (h1 - '0');
|
||
|
c = 16 * c + ((h2 > '9') ? (10 + h2 - 'a') : (h2 - '0'));
|
||
|
break;
|
||
|
default:
|
||
|
c = *p++;
|
||
|
}
|
||
|
*u++ = c;
|
||
|
}
|
||
|
*u = '\0';
|
||
|
}
|