/* ** Server Processes Library ** servproc.c Copyright (C) 1995, 96 Alex Belits ** This code is a part of fhttpd distribution */ #include "servproc.h" /* Max number of connections that could be passed in one sendmsg() */ #ifndef MAX_CONNECTIONS_IN_ONE_SEND #define MAX_CONNECTIONS_IN_ONE_SEND 24 /* R. Stevens, TCP/IP Illustrated, Volume 3, Addison-Wesley 1994, p. 272 */ #endif #ifndef CONNECTIONS_QUEUE_BLOCK #define CONNECTIONS_QUEUE_BLOCK 32 #endif /************************************************** ** Create server descriptor **************************************************/ struct http_server *createserver(void){ struct http_server *server; server=(struct http_server*)malloc(sizeof(struct http_server)); if(!server) return NULL; server->infd=-1; server->outfd=-1; server->app_progname=NULL; server->app_name=NULL; server->app_hostname=NULL; server->app_username=NULL; server->app_password=NULL; server->app_hostname_allocated=0; server->app_username_allocated=0; server->app_password_allocated=0; server->app_port=0; server->client_sessions=NULL; server->client_last_session=NULL; server->requests=NULL; server->lastrequest=NULL; server->session_created=0; server->session_expired=0; server->capabilities=0; server->exitflag=0; server->use_af_unix_socket=0; server->connectionfds=NULL; server->connectionfdshead=0; server->connectionfdstail=0; server->connectionfdscount=0; server->connectionfdsallocated=0; return server; } /************************************************** ** Delete server descriptor **************************************************/ void deleteserver(struct http_server *server){ if(!server) return; if(server->connectionfds){ free(server->connectionfds); } if(server->app_username&&server->app_username_allocated){ free(server->app_username); } if(server->app_hostname&&server->app_hostname_allocated){ free(server->app_hostname); } if(server->app_password&&server->app_password_allocated){ free(server->app_password); } free(server); } /************************************************** ** Put passed connection FDs into array **************************************************/ int putconnectionfds(struct http_server *server,int n,int *fdsarray){ int needconnections,oldconnectionfdsallocated,i,j; int *newconnectionfds; oldconnectionfdsallocated=server->connectionfdsallocated; needconnections=server->connectionfdscount+n; if(!server->connectionfds){ needconnections=(needconnections/CONNECTIONS_QUEUE_BLOCK+1) *CONNECTIONS_QUEUE_BLOCK; server->connectionfds=(int*)malloc(needconnections*sizeof(int)); if(!server->connectionfds) return -1; else server->connectionfdsallocated=needconnections; }else{ if(needconnections>server->connectionfdsallocated){ needconnections=(needconnections/CONNECTIONS_QUEUE_BLOCK+1) *CONNECTIONS_QUEUE_BLOCK; newconnectionfds=(int*)realloc((char*)server->connectionfds, needconnections*sizeof(int)); if(newconnectionfds){ server->connectionfds=newconnectionfds; server->connectionfdsallocated=needconnections; }else{ needconnections=server->connectionfdscount+n; newconnectionfds=(int*)realloc((char*)server->connectionfds, needconnections*sizeof(int)); if(newconnectionfds){ server->connectionfds=newconnectionfds; server->connectionfdsallocated=needconnections; }else{ return -1; } } if(server->connectionfdstail+server->connectionfdscount>oldconnectionfdsallocated){ j=server->connectionfdsallocated-oldconnectionfdsallocated; if(j>server->connectionfdshead){ j=server->connectionfdshead; } memcpy((char*)(server->connectionfds+oldconnectionfdsallocated), (char*)server->connectionfds,j*sizeof(int)); if(server->connectionfdstail+server->connectionfdscount>server->connectionfdsallocated){ server->connectionfdshead-=j; #ifdef NO_MEMMOVE for(i=0;iconnectionfdshead;i++) server->connectionfds[i]=server->connectionfds[i+j]; #else memmove((char*)server->connectionfds, (char*)(server->connectionfds+j),server->connectionfdshead*sizeof(int)); #endif }else{ server->connectionfdshead=server->connectionfdstail+server->connectionfdscount; } } } } for(i=0;iconnectionfdshead>=server->connectionfdsallocated) server->connectionfdshead=0; server->connectionfds[server->connectionfdshead++]=fdsarray[i]; server->connectionfdscount++; } return n; } /************************************************** ** Get passed connection FDs from array **************************************************/ int getconnectionfd(struct http_server *server){ int needsize; int *newconnectionfds; if(!server->connectionfdscount) return -1; if(server->connectionfdstail>=server->connectionfdsallocated) server->connectionfdstail=0; if(server->connectionfdstail+server->connectionfdscount==server->connectionfdshead){ needsize=(server->connectionfdshead/CONNECTIONS_QUEUE_BLOCK+1) *CONNECTIONS_QUEUE_BLOCK; if(server->connectionfdsallocated>needsize){ newconnectionfds=(int*)realloc((char*)server->connectionfds,needsize*sizeof(int)); if(newconnectionfds){ server->connectionfds=newconnectionfds; server->connectionfdsallocated=needsize; } } } server->connectionfdscount--; return server->connectionfds[server->connectionfdstail++]; } /* Defaults */ static const char *app_def_accept="*/*"; static const char *app_def_user_agent="Unknown"; static const char *app_def_content_type="application/octet-stream"; static const char *app_def_server_software="httpd"; /* Response codes for HTTP */ static int responsecodes[17]={ 200,201,202,204, 300,301,302,304, 400,401,403,404, 500,501,502,503, 0}; static const char *responsenames[17]={ /*200*/"OK",/*201*/"Created",/*202*/"Accepted",/*204*/"No Content", /*300*/"Multiple Choices",/*301*/"Moved Permanently", /*302*/"Moved Temporarily",/*304*/"Not Modified", /*400*/"Bad Request",/*401*/"Unauthorized",/*403*/"Forbidden", /*404*/"Not Found", /*500*/"Internal Server Error",/*501*/"Not Implemented", /*502*/"Bad Gateway",/*503*/"Service Unavailable"}; /* Constants for time */ static char *monthnames[12]={"Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"}; static char *weekdaynames[7]={"Sunday","Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday"}; /************************************************** ** Encode BASE64 6-bit character **************************************************/ signed char base64char(signed char c){ if(c<0||c>63) return '='; if(c<26) return c+'A'; if(c<52) return c-26+'a'; if(c<62) return c-52+'0'; if(c==62) return '+'; return '/'; } /************************************************** ** Decode BASE64 6-bit character **************************************************/ signed char char64value(signed char c){ if(c>='A'&&c<='Z') return c-'A'; if(c>='a'&&c<='z') return c-'a'+26; if(c>='0'&&c<='9') return c-'0'+52; switch(c){ case '+': return 62; case '/': return 63; case 0: case '=': return -2; } return -1; } /************************************************** ** BASE64 encode **************************************************/ void en64(char *dst,const char *src,long l){ const signed char *rptr,*srcend; signed char *wptr; signed char c0,c1=0,c2=0; int ll; rptr=(const signed char*)src; wptr=(signed char*)dst; srcend=(const signed char*)src+l; while(rptr>2); if(ll>1){ wptr[1]=base64char(((c0&0x03)<<4)|((c1&0xf0)>>4)); if(ll>2){ wptr[2]=base64char(((c1&0x0f)<<2)|((c2&0xc0)>>6)); wptr[3]=base64char(c2&0x3f); }else{ wptr[2]=base64char((c1&0x0f)<<2); wptr[3]='='; } }else{ wptr[1]=base64char((c0&0x03)<<4); wptr[2]='='; wptr[3]='='; } wptr+=4; rptr+=ll; } *wptr=0; } /************************************************** ** Decode BASE64-encoded data **************************************************/ unsigned long un64(char *s){ signed char *rptr,*wptr,*endptr; signed char c0,c1,c2,c3; int l; rptr=(signed char*)s; wptr=(signed char*)s; while(*rptr){ if((*wptr=char64value(*rptr))!=-1){ wptr++; } rptr++; } *wptr=-2; rptr=(signed char*)s; wptr=(signed char*)s; do{ c0=*rptr; if(c0!=-2) c1=rptr[1]; else c1=-2; if(c1!=-2) c2=rptr[2]; else c2=-2; if(c2!=-2) c3=rptr[3]; else c3=-2; if(c0==-2) l=0; else if(c1==-2) l=1; else if(c2==-2) l=2; else if(c3==-2) l=3; else l=4; if(l>1){ *wptr=(c0<<2)|((c1&0x30)>>4); if(l>2){ wptr[1]=((c1&0x0f)<<4)|((c2&0x3c)>>2); if(l>3){ wptr[2]=((c2&0x03)<<6)|c3; wptr[3]=0; endptr=wptr+3; }else{ wptr[2]=0; endptr=wptr+2; } }else{ wptr[1]=0; endptr=wptr+1; } }else{ *wptr=0; endptr=wptr; } rptr+=l; wptr+=3; }while(*rptr!=-2); return endptr-(signed char*)s; } /************************************************** ** URL-encode data **************************************************/ int urlencode(char *dst,const char *src,char except){ const unsigned char *p,*p0; int l=0; p=(const unsigned char*)src; p0=(const unsigned char*)src; while(1){ while((((*p&&*p>='@'&&*p<'~') ||(*p>='0'&&*p<='9') ||*p=='*'||*p=='-'||*p=='.') &&*p!='{'&&*p!='}'&&*p!='['&&*p!=']') ||(*p=='/')||(*p==except&&except)) p++; memcpy(dst,p0,p-p0); dst+=p-p0; l+=p-p0; if(!*p) return l; *dst='%'; dst++; *dst=(char)((*p&0xf0)>>4); if(*dst<=9) *dst+='0';else *dst+='A'-10; dst++; *dst=(char)(*p&0x0f); if(*dst<=9) *dst+='0';else *dst+='A'-10; dst++; l+=3; p++; p0=p; } } /************************************************** ** Decode URL-encoded data **************************************************/ void unescape(char *s){ int j; unsigned char *p; for(j=0;s[j];j++){ p=(unsigned char*)s+j; switch(*p){ case '+': *p=' '; break; case '%': if((p[1]>='0'&&p[1]<='9')||((p[1]&0xdf)>='A'&&(p[1]&0xdf)<='F')){ if((p[2]>='0'&&p[2]<='9')||((p[2]&0xdf)>='A'&&(p[2]&0xdf)<='F')){ if(p[1]>='a'&&p[1]<='f') *p=(p[1]-'a'+0xa)<<4; else if(p[1]>='A'&&p[1]<='F') *p=(p[1]-'A'+0xa)<<4; else *p=(p[1]-'0')<<4; if(p[2]>='a'&&p[2]<='f') *p|=p[2]-'a'+0xa; else if(p[2]>='A'&&p[2]<='F') *p|=p[2]-'A'+0xa; else *p|=p[2]-'0'; while(p[3]){ p++; *p=p[2]; } p++; *p=0; } } } } } /************************************************** ** Make standard time string for HTTP **************************************************/ char *linefromtime(char *s,time_t t){ struct tm *ttm; ttm=gmtime(&t); if(ttm){ sprintf(s,"%s, %02u-%s-%04u %02u:%02u:%02u GMT",weekdaynames[ttm->tm_wday%7], ttm->tm_mday,monthnames[ttm->tm_mon%12],ttm->tm_year+1900,ttm->tm_hour, ttm->tm_min,ttm->tm_sec); }else *s=0; return s; } static char dataleft[4096]; static int dataleftlen=0; /************************************************** ** Get request from the server ** return request structure **************************************************/ struct request *readrequest(struct http_server *server){ struct request *req; __s32 q; char *p; int i,j,maxfds,sopt,soptlen=sizeof(int); int sizeknown=0; long rln,l,dl; struct iovec iov[2]; struct msghdr msg; #ifdef BSD43_MSGHDR char fdtmpbuffer[MAX_CONNECTIONS_IN_ONE_SEND*sizeof(int)]; #else struct cmsghdr *cm; char fdtmpbuffer[sizeof(struct cmsghdr)+MAX_CONNECTIONS_IN_ONE_SEND*sizeof(int)]; #endif req=(struct request*)malloc(sizeof(struct request)); if(!req) return NULL; req->appinfo=NULL; req->buffsize=4096; req->buffer=(char*)malloc(req->buffsize-sizeof(__s32)+1); if(!req->buffer){ free(req); return NULL; } l=0; if(dataleftlen){ if(dataleftlenbuffer,dataleft+sizeof(__s32),dataleftlen-sizeof(__s32)); } l=dataleftlen; if(!sizeknown&&l>=sizeof(__s32)){ req->buffsize=htonl(q); sizeknown=1; if(req->buffsizebuffsize; for(i=0;ibuffer[i+req->buffsize-sizeof(__s32)]; } }else dataleftlen=0; p=(char*)realloc(req->buffer,req->buffsize-sizeof(__s32)+1); if(!p){ free(req->buffer); free(req); return NULL; }else{ req->buffer=p; req->buffer[req->buffsize-sizeof(__s32)]=0; } } } while(lbuffsize){ msg.msg_iov=iov; if(!sizeknown){ iov[0].iov_base=((char*)&q)+l; iov[0].iov_len=sizeof(__s32)-l; iov[1].iov_base=req->buffer; iov[1].iov_len=req->buffsize-sizeof(__s32); msg.msg_iovlen=2; }else{ iov[0].iov_base=req->buffer+l-sizeof(__s32); iov[0].iov_len=req->buffsize-l; msg.msg_iovlen=1; } if(server->use_af_unix_socket){ #ifdef BSD43_MSGHDR memset(fdtmpbuffer,-1,MAX_CONNECTIONS_IN_ONE_SEND*sizeof(int)); #else cm=(struct cmsghdr*)fdtmpbuffer; cm->cmsg_level=SOL_SOCKET; cm->cmsg_type=SCM_RIGHTS; cm->cmsg_len=sizeof(struct cmsghdr)+MAX_CONNECTIONS_IN_ONE_SEND*sizeof(int); #ifdef CMSG_DATA_IS_A_MACRO memset(CMSG_DATA(cm),-1,MAX_CONNECTIONS_IN_ONE_SEND*sizeof(int)); #else memset(cm->cmsg_data,-1,MAX_CONNECTIONS_IN_ONE_SEND*sizeof(int)); #endif #endif #ifdef BSD43_MSGHDR msg.msg_accrightslen=MAX_CONNECTIONS_IN_ONE_SEND*sizeof(int); msg.msg_accrights=(caddr_t)fdtmpbuffer; #else msg.msg_control=(caddr_t)cm; msg.msg_controllen=cm->cmsg_len; msg.msg_flags=0; #endif msg.msg_name=(caddr_t)0; msg.msg_namelen=0; dl=recvmsg(server->infd,&msg,0); #ifdef BSD43_MSGHDR if(msg.msg_accrightslen>0){ maxfds=msg.msg_accrightslen/sizeof(int); for(i=0;isizeof(struct cmsghdr)){ maxfds=(cm->cmsg_len-sizeof(struct cmsghdr))/sizeof(int); #ifdef CMSG_DATA_IS_A_MACRO for(i=0;icmsg_data)[i]!=-1;i++); if(i) putconnectionfds(server,i,(int*)cm->cmsg_data); #endif } #endif }else{ dl=readv(server->infd,iov,msg.msg_iovlen); } if(dl<=0){ if(dl<0){ if(errno!=EINTR){ free(req->buffer); free(req); return NULL; }else{ dl=0; } }else{ free(req->buffer); free(req); return NULL; } } l+=dl; if(!sizeknown&&l>=sizeof(__s32)){ req->buffsize=htonl(q); sizeknown=1; if(req->buffsizebuffsize; for(i=0;ibuffer[i+req->buffsize-sizeof(__s32)]; } }else dataleftlen=0; p=(char*)realloc(req->buffer,req->buffsize-sizeof(__s32)+1); if(!p){ free(req->buffer); free(req); return NULL; }else{ req->buffer=p; req->buffer[req->buffsize-sizeof(__s32)]=0; } } } if(req->buffsizebuffer); free(req); return NULL; } req->id=htonl(((__s32*)req->buffer)[0]); req->reqtype=htonl(((__s32*)req->buffer)[1]); req->rlsize=htonl(((__s32*)req->buffer)[2]); req->rl=req->buffer+3*sizeof(__s32); p=(req->buffer+3*sizeof(__s32)+req->rlsize); #ifdef MUST_ALIGN memcpy(&q,p,sizeof(__s32)); req->nlines=htonl(q); #else req->nlines=htonl(*(__s32*)p); #endif if(req->nlines){ req->lines=(struct rline*)malloc(req->nlines*sizeof(struct rline)); if(!req->lines){ free(req->buffer); free(req); return NULL; } p+=sizeof(__s32); for(i=0;inlines;i++){ #ifdef MUST_ALIGN memcpy(&q,p,sizeof(__s32)); req->lines[i].paramc=htonl(q); #else req->lines[i].paramc=htonl(*(__s32*)p); #endif req->lines[i].params=(char**)malloc(sizeof(char*)*req->lines[i].paramc); if(!req->lines[i].params){ for(j=0;jlines[i].params); free(req->lines); free(req->buffer); free(req); return NULL; } p+=sizeof(__s32); for(j=0;jlines[i].paramc;j++){ #ifdef MUST_ALIGN memcpy(&q,p,sizeof(__s32)); rln=htonl(q); #else rln=htonl(*(__s32*)p); #endif p+=sizeof(__s32); req->lines[i].params[j]=p; p+=rln; } } }else{ req->lines=NULL; p+=sizeof(__s32); } #ifdef MUST_ALIGN memcpy(&q,p,sizeof(__s32)); req->databuffsize=htonl(q); #else req->databuffsize=htonl(*(__s32*)p); #endif p+=sizeof(__s32); req->databuffer=p; req->nform=0; req->form=NULL; req->accept=app_def_accept; req->user_agent=app_def_user_agent; req->content_type=app_def_content_type; req->server_software=app_def_server_software; req->server_port=0; req->server_name=NULL; req->script_name=NULL; req->script_name_resolved=NULL; req->query_string=NULL; req->remote_addr=NULL; req->remote_host=NULL; req->remote_user=NULL; req->ver_major=0; req->ver_minor=0; req->method=APP_METHOD_UNKNOWN; req->ifmodifiedsince=0; req->keepalive=0; req->fd=getconnectionfd(server); if(req->fd!=-1){ if(!getsockopt(req->fd,SOL_SOCKET,SO_SNDBUF,&sopt,&soptlen)){ fcntl(req->fd,F_SETFL,0); }else{ close(req->fd); req->fd=-1; } } for(i=0;inlines;i++){ if(req->lines[i].paramc>1){ if(req->lines[i].params[0]){ if(!strcasecmp(req->lines[i].params[0],"HTTP_IF_MODIFIED_SINCE")){ if(req->lines[i].params[1]){ sscanf(req->lines[i].params[1],"%d",&req->ifmodifiedsince); } }else if(!strcasecmp(req->lines[i].params[0],"HTTP_ACCEPT")) req->accept=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"HTTP_USER_AGENT")) req->user_agent=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"CONTENT_TYPE")) req->content_type=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"SERVER_SOFTWARE")) req->server_software=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"SERVER_NAME")) req->server_name=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"SCRIPT_NAME")) req->script_name=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"SCRIPT_NAME_RESOLVED")) req->script_name_resolved=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"QUERY_STRING")) req->query_string=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"REMOTE_ADDR")) req->remote_addr=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"REMOTE_HOST")) req->remote_host=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"REMOTE_USER")) req->remote_user=req->lines[i].params[1]; else if(!strcasecmp(req->lines[i].params[0],"KEEPALIVE")) req->keepalive=!strcasecmp(req->lines[i].params[1],"Y"); else if(!strcasecmp(req->lines[i].params[0],"SERVER_PROTOCOL")){ if(req->lines[i].params[1]) sscanf(req->lines[i].params[1],"HTTP/%d.%d", &req->ver_major,&req->ver_minor); }else{ if(!strcasecmp(req->lines[i].params[0],"SERVER_PORT")){ if(req->lines[i].params[1]) sscanf(req->lines[i].params[1],"%d",&req->server_port); }else{ if(!strcasecmp(req->lines[i].params[0],"REQUEST_METHOD")){ if(req->lines[i].params[1]){ if(!strcasecmp(req->lines[i].params[1],"GET")) req->method=APP_METHOD_GET; else if(!strcasecmp(req->lines[i].params[1],"HEAD")) req->method=APP_METHOD_HEAD; else if(!strcasecmp(req->lines[i].params[1],"POST")) req->method=APP_METHOD_POST; else if(!strcasecmp(req->lines[i].params[1],"PUT")) req->method=APP_METHOD_PUT; } } } } } } } req->narg=0; req->arg=NULL; req->session=NULL; req->next=NULL; req->prev=server->lastrequest; if(server->lastrequest){ server->lastrequest->next=req; } if(!server->requests) server->requests=req; server->lastrequest=req; req->httpserver=server; return req; } /************************************************** ** Parse request **************************************************/ void parserequest(struct request *req){ int i; char *p,*p1,*p2; p=req->databuffer; if(!strcmp(req->content_type,"application/x-www-form-urlencoded") &&req->method==APP_METHOD_POST&&req->databuffer){ req->nform=1; for(p1=p;p1buffer+req->buffsize-sizeof(__s32);p1++){ if(*p1=='&') req->nform++; } req->form=(struct rline*)malloc(req->nform*sizeof(struct rline)); if(req->form){ i=0; do{ p2=strchr(p,'&'); if(p2){ *p2=0; p2++; } p1=strchr(p,'='); if(p1){ *p1=0; p1++; req->form[i].paramc=2; req->form[i].params=(char**)malloc(sizeof(char*)*2); if(req->form[i].params){ unescape(p); unescape(p1); req->form[i].params[0]=p; req->form[i].params[1]=p1; }else req->form[i].paramc=0; }else{ req->form[i].paramc=1; req->form[i].params=(char**)malloc(sizeof(char*)); if(req->form[i].params){ unescape(p); req->form[i].params[0]=p; }else req->form[i].paramc=0; } i++; p=p2; }while(p&&inform); }else req->nform=0; } if((p=strchr(req->rl,'?'))){ *p=0; p++; req->narg=1; for(p1=p;p1rl+req->rlsize-1;p1++){ if(*p1=='&') req->narg++; } req->arg=(struct rline*)malloc(req->narg*sizeof(struct rline)); if(req->arg){ i=0; do{ p2=strchr(p,'&'); if(p2){ *p2=0; p2++; } p1=strchr(p,'='); if(p1){ *p1=0; p1++; req->arg[i].paramc=2; req->arg[i].params=(char**)malloc(sizeof(char*)*2); if(req->arg[i].params){ unescape(p); unescape(p1); req->arg[i].params[0]=p; req->arg[i].params[1]=p1; }else req->arg[i].paramc=0; }else{ req->arg[i].paramc=1; req->arg[i].params=(char**)malloc(sizeof(char*)); if(req->arg[i].params){ unescape(p); req->arg[i].params[0]=p; }else req->arg[i].paramc=0; } i++; p=p2; }while(p&&inarg); }else req->narg=0; } } /************************************************** ** Get request from the server, ** parse, return request structure **************************************************/ struct request *getrequest(struct http_server *server){ struct request *req=readrequest(server); if(req) parserequest(req); return req; } /************************************************** ** Delete request **************************************************/ void deleterequest(struct request *req){ int i; if(req){ for(i=0;inarg;i++){ free(req->arg[i].params); } free(req->arg); for(i=0;inform;i++){ free(req->form[i].params); } free(req->form); for(i=0;inlines;i++){ free(req->lines[i].params); } free(req->lines); free(req->buffer); if(req->prev){ /* delete it from the list */ req->prev->next=req->next; }else req->httpserver->requests=req->next; if(req->next){ req->next->prev=req->prev; }else req->httpserver->lastrequest=req->prev; if(req->session){ /* delete it from the session (if any) */ if(req->session->currequest==req){ req->session->currequest=NULL; } } if(req->fd!=-1) close(req->fd); free(req); } } /************************************************** ** Send reply **************************************************/ void sendreply(struct http_server *server,long opnum,long size,long id, char *s){ __s32 size1=htonl(size+3*sizeof(__s32)); __s32 opnum1=htonl(opnum); __s32 id1=htonl(id); struct iovec vector[4]={ {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,0} }; vector[0].iov_base=&size1; vector[1].iov_base=&opnum1; vector[2].iov_base=&id1; vector[3].iov_base=s; vector[3].iov_len=size; writev(server->outfd,vector,(size&&s)?4:3); } /************************************************** ** Send reply from zero-terminated string **************************************************/ void sendlinereply(struct http_server *server,long opnum,long id,char *s){ __s32 size0=strlen(s); __s32 size1=htonl(size0+3*sizeof(__s32)); __s32 opnum1=htonl(opnum); __s32 id1=htonl(id); struct iovec vector[4]={ {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,0} }; vector[0].iov_base=&size1; vector[1].iov_base=&opnum1; vector[2].iov_base=&id1; vector[3].iov_base=s; vector[3].iov_len=size0; writev(server->outfd,vector,4); } /************************************************** ** Finish reply **************************************************/ void finishreply(struct http_server *server,long id){ __s32 size1=htonl(3*sizeof(__s32)); __s32 opnum1=htonl(FHTTPD_FINISH); __s32 id1=htonl(id); struct iovec vector[3]={ {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)} }; vector[0].iov_base=&size1; vector[1].iov_base=&opnum1; vector[2].iov_base=&id1; writev(server->outfd,vector,3); } /************************************************** ** Finish reply, drop client **************************************************/ void finishdropreply(struct http_server *server,long id){ __s32 size1=htonl(3*sizeof(__s32)); __s32 opnum1=htonl(FHTTPD_FINISH_DROP); __s32 id1=htonl(id); struct iovec vector[3]={ {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)} }; vector[0].iov_base=&size1; vector[1].iov_base=&opnum1; vector[2].iov_base=&id1; writev(server->outfd,vector,3); } /************************************************** ** Set capabilities **************************************************/ void setcapabilities(struct http_server *server,long newcap){ __s32 cap1; server->capabilities=newcap; cap1=htonl(server->capabilities); sendreply(server,FHTTPD_INST_CAP,sizeof(__s32),0,(char*)&cap1); } /************************************************** ** Request/cancel exit **************************************************/ void requestexit(struct http_server *server,int newexitflag){ __s32 exf; server->exitflag=newexitflag; exf=htonl(server->exitflag); sendreply(server,FHTTPD_INST_EXITREQ,sizeof(__s32),0,(char*)&exf); } /************************************************** ** Log message **************************************************/ void logmessage(struct http_server *server,struct httpresponse *response, char *key,char *value){ __s32 size0; __s32 size1=0; __s32 size2; __s32 opnum1; __s32 id1; char nn='\n'; struct iovec vector[6]={ {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,sizeof(__s32)}, {NULL,0}, {NULL,1}, {NULL,0} }; if(!key&&!value) return; if(!key){ key=value; value=NULL; } size0=strlen(key); if(value){ size1=strlen(value); size2=htonl(size0+size1+1+3*sizeof(__s32)); }else{ size2=htonl(size0+3*sizeof(__s32)); } opnum1=htonl(FHTTPD_LOG_MESSAGE); id1=htonl(response?response->id:0); vector[0].iov_base=&size2; vector[1].iov_base=&opnum1; vector[2].iov_base=&id1; vector[3].iov_base=key; vector[3].iov_len=size0; vector[4].iov_base=&nn; vector[5].iov_base=value; vector[5].iov_len=size1; writev(server->outfd,vector,value?6:4); } /************************************************** ** Create response **************************************************/ struct httpresponse *createresponse(int initsize,long id,int fd, int withheader){ struct httpresponse *response; response=malloc(sizeof(struct httpresponse)); if(!response) return NULL; if(initsize) response->buffer=malloc(initsize); else response->buffer=NULL; response->header=NULL; response->body=NULL; if(response->buffer) response->buffsize=initsize; else response->buffsize=0; response->datasize=0; response->id=id; response->fd=fd; if(withheader){ response->header=createresponse(64,id,fd,0); if(!response->header){ if(response->buffer) free(response->buffer); free(response); return NULL; } response->header->body=response; } return response; } /************************************************** ** Delete response **************************************************/ void deleteresponse(struct httpresponse *response){ if(!response) return; if(response->buffer) free(response->buffer); if(response->header) response->header->body=NULL; if(response->body) response->body->header=NULL; deleteresponse(response->header); deleteresponse(response->body); free(response); } /************************************************** ** Write data to response **************************************************/ int writetoresponse(struct httpresponse *response,const char *buffer,long l){ int l1,l2; char *buf1; if(!response) return -1; if(l<=0) return 0; l1=response->datasize+l; if(response->buffsizedatasize+l+(l>>1); if(response->buffer){ buf1=realloc(response->buffer,l2); }else{ buf1=malloc(l2); } if(!buf1){ l2=response->datasize+l; if(response->buffer){ buf1=realloc(response->buffer,l2); }else{ buf1=malloc(l2); } } if(buf1){ response->buffer=buf1; response->buffsize=l2; }else return -1; } memcpy(response->buffer+response->datasize,buffer,l); response->datasize+=l; return l; } /************************************************** ** Write zero-terminated string to response **************************************************/ int putlinetoresponse(struct httpresponse *response,const char *buffer){ if(!buffer) return -1; if(!*buffer) return 0; return writetoresponse(response,buffer,strlen(buffer)); } /************************************************** ** Write zero-terminated string, converted to HTML, ** to response **************************************************/ int putmlinetoresponse(struct httpresponse *response,const char *buffer){ const char *p,*p0; int l=0,r=0; if(!buffer) return -1; if(!*buffer) return 0; p=buffer; p0=buffer; while(1){ while(*p&&*p!='<'&&*p!='>'&&*p!='&') p++; r=writetoresponse(response,p0,p-p0); if(r<0) return r; else l+=r; switch(*p){ case '<': r=writetoresponse(response,"<",4); break; case '>': r=writetoresponse(response,">",4); break; case '&': r=writetoresponse(response,"&",5); break; default: return l; } if(r<0) return r; else l+=r; p++; p0=p; } } /************************************************** ** Write URL-encoded zero-terminated string ** to response **************************************************/ int putulinetoresponse(struct httpresponse *response,const char *buffer, char except){ const unsigned char *p,*p0; char s[3]; int l=0,r=0; if(!buffer) return -1; if(!*buffer) return 0; s[0]='%'; p=(const unsigned char*)buffer; p0=(const unsigned char*)buffer; while(1){ while((((*p&&*p>='@'&&*p<'~') ||(*p>='0'&&*p<='9') ||*p=='*'||*p=='-'||*p=='.') &&*p!='{'&&*p!='}'&&*p!='['&&*p!=']') ||(*p=='/')||(*p==except&&except)) p++; r=writetoresponse(response,p0,p-p0); if(r<0) return r; else l+=r; if(!*p) return l; s[1]=(char)((*p&0xf0)>>4); s[2]=(char)(*p&0x0f); if(s[1]<=9) s[1]+='0';else s[1]+='A'-10; if(s[2]<=9) s[2]+='0';else s[2]+='A'-10; r=writetoresponse(response,s,3); if(r<0) return r; else l+=r; p++; p0=p; } } /************************************************** ** Write URL to response **************************************************/ int puturltoresponse(struct httpresponse *response, struct httpclientsession *session, const char *protocol,const char *host,int port,const char *path, const char *arg,const char *fragment){ char tmpstr[20]; int l=0,r=0,qmark=0; if(protocol){ r=putulinetoresponse(response,protocol,0); if(r<0) return r; else l+=r; r=writetoresponse(response,"://",3); if(r<0) return r; else l+=r; }else{ if(host){ r=writetoresponse(response,"http://",7); if(r<0) return r; else l+=r; } } if(host){ r=putulinetoresponse(response,host,0); if(r<0) return r; else l+=r; if(port&&port!=80){ sprintf(tmpstr,":%d",port); r=putlinetoresponse(response,tmpstr); if(r<0) return r; else l+=r; } } if(path){ if(host&&*path!='/'){ r=writetoresponse(response,"/",1); if(r<0) return r; else l+=r; } r=putulinetoresponse(response,path,'~'); } if(session){ if(session->cookieinurl){ r=writetoresponse(response,"?CSES=",6); if(r<0) return r; else l+=r; r=putlinetoresponse(response,session->cookie); if(r<0) return r; else l+=r; qmark=1; } } if(arg){ r=writetoresponse(response,qmark?"&":"?",1); if(r<0) return r; else l+=r; r=putulinetoresponse(response,arg,'='); if(r<0) return r; else l+=r; } if(fragment){ r=writetoresponse(response,"#",1); if(r<0) return r; else l+=r; r=putulinetoresponse(response,fragment,0); if(r<0) return r; else l+=r; } return l; } /************************************************** ** Write zero-terminated string to response header **************************************************/ int putlinetoheader(struct httpresponse *response,const char *buffer){ if(!response->header) return -1; if(!buffer) return -1; if(!*buffer) return 0; return writetoresponse(response->header,buffer,strlen(buffer)); } /************************************************** ** Fill buffer by requested URL (arguments replaced) **************************************************/ int makemyurl(char *dst,struct request *req,int maxsize, struct httpclientsession *session,char *arg){ char portstring[20]; int l,l1,qmark=0; if(!dst) return -1; if(!req) return -1; if(!req->server_name||!req->rl) return -1; dst[maxsize-1]=0; strncpy(dst,"http://",maxsize-2); strncat(dst,req->server_name,maxsize-2-strlen(dst)); if(req->server_port!=80){ sprintf(portstring,":%u",req->server_port); strncat(dst,portstring,maxsize-2-strlen(dst)); } if(*req->rl!='/') strcat(dst,"/"); strncat(dst,req->rl,maxsize-1-strlen(dst)); if(session&&maxsize>strlen(dst)+7){ if(session->cookieinurl){ l=strlen(dst); l1=strlen(session->cookie); if(maxsize>l+7+l1){ memcpy(dst+l,"?CSES=",6); strcpy(dst+l+6,session->cookie); qmark=1; } } } if(arg){ l=strlen(dst); if(l+2+strlen(arg)header) return -1; if(code>0){ for(i=0;responsecodes[i]&&responsecodes[i]!=code;i++); if(responsecodes[i]) charresponse=responsenames[i]; sprintf(tmpline,"HTTP/1.0 %d %s\r\nContent-Type: ",code,charresponse); i=putlinetoheader(response,tmpline); }else{ i=putlinetoheader(response,"Content-Type: "); } if(!contenttype){ i|=putlinetoheader(response,"text/html"); }else{ i|=putlinetoheader(response,contenttype); } i|=putlinetoheader(response,"\r\nServer: "); if(!servername){ i|=putlinetoheader(response,"fhttpd process"); }else{ i|=putlinetoheader(response,servername); } if(keepalive){ i|=putlinetoheader(response, "\r\nConnection: Keep-Alive\r\nKeep-Alive: max=0, timeout=30"); } linefromtime(tmpline1,time(NULL)); sprintf(tmpline,"\r\nDate: %s\r\nContent-Length: %ld\r\n", tmpline1,response->datasize); i|=putlinetoheader(response,tmpline); if(i<0) i=-1; return i; } /************************************************** ** Make HTTP response header for session **************************************************/ int makesessionheader(struct httpresponse *response,int code, const char *contenttype,const char *servername, struct httpclientsession *session,int keepalive){ int i; if(!session) return makestandardheader(response,code,contenttype,servername, keepalive); if(!session->cookie) return -1; time(&session->lastaccessed); i=makestandardheader(response,code,contenttype,servername,keepalive); i|=putlinetoheader(response,"Set-Cookie: CSES="); i|=putlinetoheader(response,session->cookie); i|=putlinetoheader(response,"\r\n"); return i; } /************************************************** ** Send response with header **************************************************/ int sendresponse(struct http_server *server,struct httpresponse *response){ int i=0; if(!response) return -1; if(response->header){ i=putlinetoheader(response,"\r\n"); if(i>=0){ if(response->fd==-1){ sendreply(server,FHTTPD_SEND,response->header->datasize,response->id, response->header->buffer); sendreply(server,FHTTPD_SEND,response->datasize,response->id, response->buffer); }else{ write(response->fd,response->header->buffer,response->header->datasize); write(response->fd,response->buffer,response->datasize); } i=0; } }else{ if(response->fd==-1){ sendreply(server,FHTTPD_SEND,response->datasize,response->id, response->buffer); }else{ write(response->fd,response->buffer,response->datasize); } } return i; } /************************************************** ** Send response without header **************************************************/ int sendshortresponse(struct http_server *server, struct httpresponse *response){ if(!response) return -1; if(response->fd==-1){ sendreply(server,FHTTPD_SEND,response->datasize,response->id, response->buffer); }else{ write(response->fd,response->buffer,response->datasize); } return 0; } /************************************************** ** Finish response **************************************************/ int finishresponse(struct http_server *server,struct httpresponse *response){ if(!response) return -1; finishreply(server,response->id); return 0; } /************************************************** ** Finish response, drop client **************************************************/ int finishdropresponse(struct http_server *server, struct httpresponse *response){ if(!response) return -1; finishdropreply(server,response->id); return 0; } /************************************************** ** Make, send and delete redirect message **************************************************/ int redirect(struct request *req,char *destination){ struct httpresponse *redirectresponse; struct http_server *server; int i=0; if(!req||!destination) return -1; server=req->httpserver; redirectresponse=createresponse(1024,req->id,req->fd,req->ver_major>0); if(!redirectresponse) return -1; i|=putlinetoresponse(redirectresponse, "

Redirect

Redirected "); if(req->ver_major>0){ if(makestandardheader(redirectresponse,302,"text/html", req->server_software,req->keepalive)<0){ deleteresponse(redirectresponse); return -1; } i|=putlinetoheader(redirectresponse,"Location: "); i|=putlinetoheader(redirectresponse,destination); i|=putlinetoheader(redirectresponse,"\r\n"); } if(i>=0){ i|=sendresponse(server,redirectresponse); i|=finishresponse(server,redirectresponse); } deleteresponse(redirectresponse); return i; } /************************************************** ** Create multipart response **************************************************/ struct multipartresponse *createmultipartresponse(long id,int fd){ struct multipartresponse *response; struct hostent *hostst; int tmpboundarysize; response=malloc(sizeof(struct multipartresponse)); if(!response) return NULL; response->headerbuffer=malloc(256); response->first=NULL; response->last=NULL; response->nsegments=0; if(response->headerbuffer) response->headerbuffsize=256; else response->headerbuffsize=0; response->headerdatasize=0; response->id=id; response->fd=fd; tmpboundarysize=sizeof(((struct in_addr*)(hostst->h_addr))->s_addr) +sizeof(pid_t)+sizeof(time_t)+sizeof(__s32)*2; tmpboundarysize+=3-(tmpboundarysize%3); tmpboundarysize=tmpboundarysize/3*4+6; response->boundary=malloc(tmpboundarysize+1); if(!response->boundary){ free(response); return NULL; } makeid(response->boundary+4); response->boundary[0]='\r'; response->boundary[1]='\n'; response->boundary[2]='-'; response->boundary[3]='-'; response->boundary[tmpboundarysize-2]='\r'; response->boundary[tmpboundarysize-1]='\n'; response->boundary[tmpboundarysize]=0; response->boundarysize=tmpboundarysize; return response; } /************************************************** ** Delete multipart response **************************************************/ void deletemultipartresponse(struct multipartresponse *response,int clean){ if(!response) return; while(response->first) deletemultipartsegment(response->first,clean); if(response->headerbuffer) free(response->headerbuffer); if(response->boundary) free(response->boundary); free(response); } /************************************************** ** Create multipart response segment **************************************************/ struct multipartsegment *createmultipartsegment( struct multipartresponse *response,struct httpresponse *responsebody){ struct multipartsegment *segment; segment=malloc(sizeof(struct multipartsegment)); if(!segment) return NULL; segment->s_response=responsebody; segment->m_response=NULL; segment->parentresponse=response; segment->next=NULL; if(response){ segment->prev=response->last; if(response->last){ response->last->next=segment; } if(!response->first) response->first=segment; response->last=segment; }else segment->prev=NULL; return segment; } /************************************************** ** Delete multipart response segment **************************************************/ void deletemultipartsegment(struct multipartsegment *segment,int clear){ if(!segment) return; if(clear){ if(segment->s_response) deleteresponse(segment->s_response); if(segment->m_response) deletemultipartresponse(segment->m_response,1); } if(segment->parentresponse){ if(segment->prev){ segment->prev->next=segment->next; }else segment->parentresponse->first=segment->next; if(segment->next){ segment->next->prev=segment->prev; }else segment->parentresponse->last=segment->prev; } free(segment); } /************************************************** ** Write data to multipart response header **************************************************/ int writetomultipartresponseheader(struct multipartresponse *response, const char *buffer,long l){ int l1,l2; char *buf1; if(!response) return -1; if(l<=0) return 0; l1=response->headerdatasize+l; if(response->headerbuffsizeheaderdatasize+l+(l>>1); if(response->headerbuffer){ buf1=realloc(response->headerbuffer,l2); }else{ buf1=malloc(l2); } if(!buf1){ l2=response->headerdatasize+l; if(response->headerbuffer){ buf1=realloc(response->headerbuffer,l2); }else{ buf1=malloc(l2); } } if(buf1){ response->headerbuffer=buf1; response->headerbuffsize=l2; }else return -1; } memcpy(response->headerbuffer+response->headerdatasize,buffer,l); response->headerdatasize+=l; return l; } /************************************************** ** Write zero-terminated string to multipart ** response header **************************************************/ int putlinetomultipartheader(struct multipartresponse *response, const char *buffer){ if(!buffer) return -1; if(!*buffer) return 0; return writetomultipartresponseheader(response,buffer,strlen(buffer)); } /************************************************** ** Make HTTP multipart response header **************************************************/ int makemultipartheader(struct multipartresponse *response,int code, const char *contenttype,const char *servername,int keepalive){ int i; char tmpline[128]; const char *charresponse="Internal"; if(!response) return -1; if(code>0){ for(i=0;responsecodes[i]&&responsecodes[i]!=code;i++); if(responsecodes[i]) charresponse=responsenames[i]; sprintf(tmpline,"HTTP/1.0 %d %s\r\nContent-Type: ",code,charresponse); i=putlinetomultipartheader(response,tmpline); }else{ i=putlinetomultipartheader(response,"Content-Type: "); } if(!contenttype){ i|=putlinetomultipartheader(response,"multipart/x-mixed-replace"); }else{ i|=putlinetomultipartheader(response,contenttype); } i|=putlinetomultipartheader(response,"; boundary="); i|=putlinetomultipartheader(response,response->boundary+4); i|=putlinetomultipartheader(response,response->boundary); if(i<0) i=-1; return i; } /************************************************** ** Send multipart response header **************************************************/ int sendmultipartresponseheader(struct http_server *server, struct multipartresponse *response){ if(!response) return -1; if(response->headerbuffer){ if(response->fd==-1){ sendreply(server,FHTTPD_SEND,response->headerdatasize,response->id, response->headerbuffer); }else{ write(response->fd,response->headerbuffer,response->headerdatasize); } } return 0; } /************************************************** ** Send multipart response segment **************************************************/ int sendmultipartresponsesegment(struct http_server *server, struct multipartsegment *segment){ int i; if(!server||!segment) return -1; if((!segment->s_response&&!segment->m_response) ||!segment->parentresponse) return -1; if(segment->s_response){ i=sendresponse(server,segment->s_response); }else{ i=sendmultipartresponse(server,segment->m_response); } if(!i){ if(segment->parentresponse->fd==-1){ sendreply(server,FHTTPD_SEND,segment->parentresponse->boundarysize, segment->parentresponse->id,segment->parentresponse->boundary); }else{ write(segment->parentresponse->fd,segment->parentresponse->boundary, segment->parentresponse->boundarysize); } } return i; } /************************************************** ** Send multipart response last boundary **************************************************/ int sendmultipartresponsefinish(struct http_server *server, struct multipartresponse *response){ if(!response) return -1; if(response->boundary){ if(response->fd==-1){ sendreply(server,FHTTPD_SEND,response->boundarysize,response->id, response->boundary-2); sendreply(server,FHTTPD_SEND,4,response->id,"--\r\n"); }else{ write(response->fd,response->boundary,response->boundarysize-2); write(response->fd,"--\r\n",4); } } return 0; } /************************************************** ** Send multipart response last boundary **************************************************/ int sendmultipartresponse(struct http_server *server, struct multipartresponse *response){ int i; struct multipartsegment *segment; if(!response) return -1; i=sendmultipartresponseheader(server,response); if(i) return i; segment=response->first; while(segment){ i=sendmultipartresponsesegment(server,segment); if(i) return i; segment=segment->next; } i=sendmultipartresponsefinish(server,response); return i; } /************************************************** ** Create client session **************************************************/ struct httpclientsession *createsession(struct request *req, const void *appinfo,char *cookie){ struct httpclientsession *mysession; struct http_server *server; char idstring[256]; if(!req) return NULL; if(!req->httpserver) return NULL; mysession=malloc(sizeof(struct httpclientsession)); if(!mysession) return NULL; time(&mysession->created); mysession->lastaccessed=mysession->created; mysession->currequest=req; req->session=mysession; server=req->httpserver; mysession->httpserver=server; mysession->useragent=NULL; if(req->user_agent){ mysession->useragent=malloc(strlen(req->user_agent)+1); if(mysession->useragent) strcpy(mysession->useragent,req->user_agent); } mysession->username=NULL; if(req->remote_user){ mysession->username=malloc(strlen(req->remote_user)+1); if(mysession->username) strcpy(mysession->username,req->remote_user); } mysession->ver_major=req->ver_major; mysession->ver_minor=req->ver_minor; mysession->useragentcap=0; mysession->cookieinurl=0; if(!cookie){ /* no predefined cookie */ *idstring='-'; makeid(idstring+1); /* cookie */ strcat(idstring,"-"); mysession->cookie=malloc(strlen(idstring)+1); if(mysession->cookie) strcpy(mysession->cookie,idstring); }else{ mysession->cookie=malloc(strlen(cookie)+1); if(mysession->cookie) strcpy(mysession->cookie,cookie); } mysession->appinfo=appinfo; mysession->next=NULL; mysession->prev=server->client_last_session; if(server->client_last_session){ server->client_last_session->next=mysession; } if(!server->client_sessions) server->client_sessions=mysession; server->client_last_session=mysession; return mysession; } /************************************************** ** Tell the server to send all requests from the ** client session to this instance **************************************************/ int selectinstancesession(struct httpclientsession *session){ char *cookieline; if(!session) return -1; if(!session->cookie) return -1; cookieline=malloc(strlen(session->cookie)+23); if(!cookieline) return -1; time(&session->lastaccessed); memcpy(cookieline,"AHE\nHTTP_COOKIE\nCSES=",21); strcpy(cookieline+21,session->cookie); strcat(cookieline+21,"*"); sendlinereply(session->httpserver,FHTTPD_INST_SELECT,0,cookieline); memcpy(cookieline,"ALE\n*?CSES=",11); strcpy(cookieline+11,session->cookie); strcat(cookieline+11,"*"); sendlinereply(session->httpserver,FHTTPD_INST_SELECT,0,cookieline); free(cookieline); return 0; } /************************************************** ** Delete instance selection **************************************************/ int deselectinstancesession(struct httpclientsession *session){ char *cookieline; if(!session) return -1; if(!session->cookie) return -1; cookieline=malloc(strlen(session->cookie)+26); if(!cookieline) return -1; memcpy(cookieline,"AHE\nHTTP_COOKIE\nCSES=",21); strcpy(cookieline+25,session->cookie); sendlinereply(session->httpserver,FHTTPD_INST_DESELECT,0,cookieline); memcpy(cookieline,"ALE\n*?CSES=",11); strcpy(cookieline+11,session->cookie); strcat(cookieline+11,"*"); sendlinereply(session->httpserver,FHTTPD_INST_DESELECT,0,cookieline); free(cookieline); return 0; } /************************************************** ** Make, send and delete redirect message ** to create the session identifier in URL **************************************************/ int redirecttosession(struct httpclientsession *session){ char destination[1024]; int i,l,l1,dl; if(!session) return -1; if(!session->currequest||!session->cookie) return -1; session->cookieinurl=1; l=makemyurl(destination,session->currequest,1024,session,NULL); if(l<0) return -1; if(session->currequest->narg){ for(i=0;icurrequest->narg;i++){ l1=strlen(destination); if(session->currequest->arg[i].paramc==1){ if(strcmp(session->currequest->arg[i].params[0],"CSES")){ dl=1+strlen(session->currequest->arg[i].params[0])*3; if(l1+dl<1023){ destination[l1]='&'; l1++; urlencode(destination+l1,session->currequest->arg[i].params[0],0); } } }else{ if(session->currequest->arg[i].paramc==2){ if(strcmp(session->currequest->arg[i].params[0],"CSES")){ dl=2+strlen(session->currequest->arg[i].params[0])*3 +strlen(session->currequest->arg[i].params[1])*3; if(l1+dl<1023){ destination[l1]='&'; l1++; urlencode(destination+l1,session->currequest->arg[i].params[0],0); l1+=strlen(destination+l1); destination[l1]='='; l1++; urlencode(destination+l1,session->currequest->arg[i].params[1],0); } } } } } } time(&session->lastaccessed); return redirect(session->currequest,destination); } /************************************************** ** Delete client session (and its request, if any) **************************************************/ void deletesession(struct httpclientsession *mysession){ if(!mysession) return; if(mysession->prev){ /* delete it from the list */ mysession->prev->next=mysession->next; }else mysession->httpserver->client_sessions=mysession->next; if(mysession->next){ mysession->next->prev=mysession->prev; }else mysession->httpserver->client_last_session=mysession->prev; if(mysession->currequest){ mysession->currequest->session=NULL; deleterequest(mysession->currequest); } if(mysession->useragent) free(mysession->useragent); if(mysession->username) free(mysession->username); if(mysession->cookie) free(mysession->cookie); free(mysession); } /************************************************** ** Find next session to expire **************************************************/ struct httpclientsession *findsessiontoexpire(struct http_server *server, int maxcount,int maxlife, int maxidle){ struct httpclientsession *session=server->client_sessions; int i=0; time_t currtime; time(&currtime); while(session){ if(maxidle){ if(currtime-session->lastaccessed>maxidle) return session; } if(maxlife){ if(currtime-session->created>maxlife) return session; } i++; session=session->next; } return (i>maxcount)?server->client_sessions:NULL; } /************************************************** ** Find client session by request **************************************************/ struct httpclientsession *findsession(struct request *req,int autocreate){ struct httpclientsession *mysession; struct http_server *server; char *p,*p2,*p3,*cookie=NULL,*cookieend=NULL; int i; server=req->httpserver; server->session_created=0; server->session_expired=0; if(!req) return NULL; for(i=0;inlines&&!cookie;i++){ /* look for cookie in the header */ if(req->lines[i].paramc==2){ if(req->lines[i].params[0]&&req->lines[i].params[1]){ if(!strcmp(req->lines[i].params[0],"HTTP_COOKIE")){ p=req->lines[i].params[1]; if(p){ do{ p2=strchr(p,';'); if(p2){ *p2=0; } p3=strchr(p,'='); if(p3){ if(p3-p==4){ if(!memcmp(p,"CSES",4)){ cookie=p3+1; cookieend=p2; if(!cookieend) cookieend=cookie+strlen(cookie); mysession=server->client_sessions; while(mysession){/* looking for the session with received cookie */ if(mysession->cookie){ /* fprintf(stderr,"Compare: \"%s\" \"%s\"\n",cookie,mysession->cookie);*/ if(!memcmp(cookie,mysession->cookie,cookieend-cookie)){ time(&mysession->lastaccessed); return mysession; } } mysession=mysession->next; } server->session_expired=1; } } } if(p2){ *p2=';'; p=p2+1; while(*p&&isspace(*p)) p++; if(!*p) p=NULL; }else p=NULL; }while(p); } } } } } if(!cookie){ /* if no cookies found, look for it in the request line*/ for(i=0;inarg&&!cookie;i++){ if(req->arg[i].paramc==2){ if(req->arg[i].params[0]&&req->arg[i].params[1]){ if(!strcmp(req->arg[i].params[0],"CSES")){ cookie=req->arg[i].params[1]; cookieend=cookie+strlen(cookie); } } } } } if(cookie){ mysession=server->client_sessions; while(mysession){ /* looking for the session with received cookie */ if(mysession->cookie){ /* fprintf(stderr,"Compare: \"%s\" \"%s\"\n",cookie,mysession->cookie);*/ if(!memcmp(cookie,mysession->cookie,cookieend-cookie)){ time(&mysession->lastaccessed); return mysession; } } mysession=mysession->next; } server->session_expired=1; } if(autocreate){ mysession=createsession(req,NULL,NULL); if(mysession){ server->session_created=1; } return mysession; }else return NULL; } /* Constants */ static int is_logapplication=0; static int alive=1; static struct linger llinger={1,1000}; #define MY_PROTOCOL 0 /* TCP/IP */ /************************************************** ** Set logger application flag **************************************************/ void set_logapplication(int logapplication){ is_logapplication=logapplication; } /************************************************** ** Read line (for login) **************************************************/ int readline(int h,char *buffer,int length){ int dl=0; char *p=buffer,*endbuf=buffer+length; while(pbuffer){ if(p[-1]=='\r') p--; } *p=0; return p-buffer; }else p++; }else{ *p=0; return p-buffer; } } *p=0; return p-buffer; } /************************************************** ** Connect to remote HTTP server **************************************************/ int connecttohttpd(char *host,int port,char *user,char *password, char *appname,int messagestostderr,int askpassword){ struct sockaddr_in address; int h,l,r; char buffer[256]; struct hostent *hostst; unsigned n1,n2,n3,n4; if(host){ bzero((char*)&address,sizeof(struct sockaddr_in)); address.sin_family=AF_INET; if(*host<'0'||*host>'9'){ hostst=gethostbyname(host); if(!hostst){ return APP_ERR_HOSTNAME; } address.sin_addr.s_addr=((struct in_addr*)(hostst->h_addr))->s_addr; }else{ if(sscanf(host,"%u.%u.%u.%u",&n4,&n3,&n2,&n1)==4){ if((n1>255)||(n2>255)||(n3>255)||(n4>255)){ return APP_ERR_HOSTNAME; } address.sin_addr.s_addr= htonl((long)n1|(long)n2<<8|(long)n3<<16|(long)n4<<24); }else{ return APP_ERR_HOSTNAME; } } }else return APP_ERR_HOSTNAME; address.sin_port=htons(port); h=socket(AF_INET,SOCK_STREAM,MY_PROTOCOL); if(h<0){ return APP_ERR_SOCKET; } if(connect(h,(struct sockaddr*)&address,sizeof(struct sockaddr_in))){ shutdown(h,2); close(h); h=-1; return APP_ERR_CONNECT; } setsockopt(h,SOL_SOCKET,SO_KEEPALIVE,&alive,sizeof(alive)); setsockopt(h,SOL_SOCKET,SO_LINGER,&llinger,sizeof(struct linger)); l=readline(h,buffer,255); if(l<0){ shutdown(h,2); close(h); return APP_ERR_READ; } buffer[l]=0; if(messagestostderr) fprintf(stderr,"%s\n",buffer); r=0; sscanf(buffer,"%d",&r); if(r!=220){ shutdown(h,2); close(h); return APP_ERR_APPCONNECT; } write(h,"USER ",5); if(user) write(h,user,strlen(user)); write(h,"\n",1); l=readline(h,buffer,255); if(l<0){ shutdown(h,2); close(h); return APP_ERR_READ; } buffer[l]=0; if(messagestostderr) fprintf(stderr,"%s\n",buffer); r=0; sscanf(buffer,"%d",&r); if(r!=331){ shutdown(h,2); close(h); return APP_ERR_USER; } if(isatty(0)&&askpassword){ if(!password) password=getpass("Password: "); } write(h,"PASS ",5); if(password) write(h,password,strlen(password)); write(h,"\n",1); l=readline(h,buffer,255); if(l<0){ shutdown(h,2); close(h); return APP_ERR_READ; } buffer[l]=0; if(messagestostderr) fprintf(stderr,"%s\n",buffer); r=0; sscanf(buffer,"%d",&r); if(r!=230){ shutdown(h,2); close(h); return APP_ERR_PASSWORD; } if(is_logapplication){ write(h,"LOGAPPLICATION ",15); }else{ write(h,"APPLICATION ",12); } if(appname) write(h,appname,strlen(appname)); write(h,"\n",1); l=readline(h,buffer,255); if(l<0){ shutdown(h,2); close(h); return APP_ERR_READ; } buffer[l]=0; if(messagestostderr) fprintf(stderr,"%s\n",buffer); r=0; sscanf(buffer,"%d",&r); if(r!=200){ shutdown(h,2); close(h); return APP_ERR_APPLICATION; } return h; } /************************************************** ** Log an error through syslog (not recommended ** for regular application errors) **************************************************/ void errtosyslog(struct http_server *server,char *s){ openlog(server->app_progname,LOG_CONS|LOG_PID,LOG_USER); syslog(LOG_ERR,s); closelog(); } /************************************************** ** Smart[ass] application initialization **************************************************/ int servproc_init(struct http_server *server,int human,int argc,char **argv){ struct stat statbuf; struct passwd *localpasswd; struct servent *servbuf; char tmp_str[256], *p,*p1; uid_t my_uid; FILE *passwdfile; int i,this_application,port_name_error=0,i0=0,i1=0,sopt,soptlen=sizeof(int); if(argc<=0) return APP_ERR_INSANE; /* sanity check */ if(!argv) return APP_ERR_INSANE; if(!argv[0]) return APP_ERR_INSANE; server->app_progname=strrchr(argv[0],'/'); if(!server->app_progname) server->app_progname=argv[0]; else server->app_progname++; if(argc==1 ||(argc==2&&((i0=!strcmp(argv[1],"-s"))||(i1=!strcmp(argv[1],"-p"))))){ /* either start from server or erroneous manual start*/ if(human){ if(isatty(0)||isatty(1)||isatty(2)){ return APP_ERR_HUMAN; } } /* are we really started from server? */ if(getppid()==1){ /* server can't be init, neither it can be dead */ errtosyslog(server,"wrong arguments"); return APP_ERR_DAEMON; } if(fstat(0,&statbuf)){ errtosyslog(server,"no stdin"); return APP_ERR_DAEMON; } if(!S_ISFIFO(statbuf.st_mode) &&!(server->use_af_unix_socket=(!getsockopt(0,SOL_SOCKET,SO_SNDBUF, &sopt,&soptlen)))){ errtosyslog(server,"stdin is not a pipe"); return APP_ERR_DAEMON; } if(fstat(1,&statbuf)){ errtosyslog(server,"no stdout"); return APP_ERR_DAEMON; } if(!S_ISFIFO(statbuf.st_mode) &&!(server->use_af_unix_socket=(!getsockopt(1,SOL_SOCKET,SO_SNDBUF, &sopt,&soptlen)))){ return APP_ERR_DAEMON; } if(i0) server->use_af_unix_socket=1; if(i1) server->use_af_unix_socket=0; server->infd=0; server->outfd=1; }else{ if(server->infd>=0){ close(server->infd); server->infd=-1; } if(server->outfd>=0){ close(server->outfd); server->outfd=-1; } if(server->connectionfds){ free(server->connectionfds); server->connectionfds=NULL; server->connectionfdsallocated=0; } if(server->app_username&&server->app_username_allocated){ free(server->app_username); } server->app_username=NULL; server->app_username_allocated=0; if(server->app_hostname&&server->app_hostname_allocated){ free(server->app_hostname); } server->app_hostname=NULL; server->app_hostname_allocated=0; if(server->app_password&&server->app_password_allocated){ free(server->app_password); } server->app_password=NULL; server->app_password_allocated=0; server->app_port=0; server->app_name=argv[1]; if(argc>2) server->app_username=argv[2]; if(argc>3) server->app_hostname=argv[3]; if(argc>4){ if(*argv[4]>='0'&&*argv[4]<='9'){ sscanf(argv[4],"%d",&server->app_port); if(!server->app_port) port_name_error=1; }else{ servbuf=getservbyname(argv[4],"tcp"); if(servbuf){ server->app_port=htons(servbuf->s_port); }else port_name_error=1; } } if(argc>5){ if(human){ if(isatty(0)||isatty(1)||isatty(2)){ return APP_ERR_HUMAN; } } errtosyslog(server,"wrong number of parameters"); return APP_ERR_DAEMON; } my_uid=getuid(); if((my_uid)==0){ /* it's root! */ passwdfile=fopen(ROOT_HTTPPROCRC,"r"); }else{ localpasswd=getpwuid(my_uid); if(!localpasswd){ sprintf(tmp_str,"started from unknown userid %d",my_uid); errtosyslog(server,tmp_str); return APP_ERR_AUTH; }else{ strncpy(tmp_str,localpasswd->pw_dir,256-12); tmp_str[255-12]=0; strcat(tmp_str,"/" USER_HTTPPROCRC); passwdfile=fopen(tmp_str,"r"); } } if(!passwdfile){ if(server->app_hostname&&server->app_username&&server->app_port&&human){ if(!isatty(0)||!isatty(2)) human=0; if(human){ server->infd=connecttohttpd(server->app_hostname,server->app_port,server->app_username,server->app_password, server->app_name,1,1); server->outfd=server->infd; return server->infd; } } return APP_ERR_CONFIG; } i=0; this_application=0; while(fgets(tmp_str,256,passwdfile)){ tmp_str[255]=0; p=strchr(tmp_str,'\n'); if(p) *p=0; p=strchr(tmp_str,'\r'); if(p) *p=0; p=strchr(tmp_str,'#'); if(p) *p=0; p=tmp_str+strlen(tmp_str); if(p>tmp_str) p--; while(p>tmp_str&&isspace(*p)){ *p=0; p--; } p=tmp_str; while(isspace(*p)) p++; p1=p; while(*p1&&!isspace(*p1)) p1++; if(*p1){ *p1=0; p1++; while(isspace(*p1)) p1++; if(!strcasecmp(p,"APPLICATION")){ this_application=!strcmp(p1,server->app_name); }else{ if(this_application){ if(!strcasecmp(p,"HOST")){ if(!server->app_hostname){ server->app_hostname=malloc(strlen(p1)+1); if(server->app_hostname){ strcpy(server->app_hostname,p1); server->app_hostname_allocated=1; } } }else{ if(!strcasecmp(p,"USER")){ if(!server->app_username){ server->app_username=malloc(strlen(p1)+1); if(server->app_username){ strcpy(server->app_username,p1); server->app_username_allocated=1; } } }else{ if(!strcasecmp(p,"PASSWORD")){ if(!server->app_password){ server->app_password=malloc(strlen(p1)+1); if(server->app_password){ strcpy(server->app_password,p1); server->app_password_allocated=1; } } }else{ if(!strcasecmp(p,"PORT")){ if(!server->app_port){ if(*p1>='0'&&*p1<='9'){ sscanf(p1,"%d",&server->app_port); if(!server->app_port) port_name_error=1; }else{ servbuf=getservbyname(p1,"tcp"); if(servbuf){ server->app_port=servbuf->s_port; }else port_name_error=1; } } } } } } } } } } fclose(passwdfile); if(!server->app_username||!server->app_hostname||!server->app_port||port_name_error){ if(human){ if(isatty(0)||isatty(1)||isatty(2)){ if(!server->app_username){ fprintf(stderr,"%s: no username defined\n",server->app_progname); }else{ if(!server->app_hostname){ fprintf(stderr,"%s: no hostname defined\n",server->app_progname); }else{ if(!port_name_error){ fprintf(stderr,"%s: no port defined\n",server->app_progname); }else{ fprintf(stderr,"%s: wrong port\n",server->app_progname); } } } }else human=0; } if(!human){ if(!server->app_username){ errtosyslog(server,"no username defined"); }else{ if(!server->app_hostname){ errtosyslog(server,"no hostname defined"); }else{ if(!port_name_error){ errtosyslog(server,"no port defined"); }else{ errtosyslog(server,"wrong port"); } } } } if(server->connectionfds){ free(server->connectionfds); server->connectionfds=NULL; server->connectionfdsallocated=0; } if(server->app_username&&server->app_username_allocated){ free(server->app_username); } server->app_username=NULL; server->app_username_allocated=0; if(server->app_hostname&&server->app_hostname_allocated){ free(server->app_hostname); } server->app_hostname=NULL; server->app_hostname_allocated=0; if(server->app_password&&server->app_password_allocated){ free(server->app_password); } server->app_password=NULL; server->app_password_allocated=0; server->app_name=NULL; server->app_port=0; return APP_ERR_CONFIG; }else{ if(human){ if(!isatty(0)||!isatty(2)) human=0; } server->infd=connecttohttpd(server->app_hostname,server->app_port,server->app_username,server->app_password,server->app_name, human,human); server->outfd=server->infd; } } if(server->infd<0){ if(server->connectionfds){ free(server->connectionfds); server->connectionfds=NULL; server->connectionfdsallocated=0; } if(server->app_username&&server->app_username_allocated){ free(server->app_username); } server->app_username=NULL; server->app_username_allocated=0; if(server->app_hostname&&server->app_hostname_allocated){ free(server->app_hostname); } server->app_hostname=NULL; server->app_hostname_allocated=0; if(server->app_password&&server->app_password_allocated){ free(server->app_password); } server->app_password=NULL; server->app_password_allocated=0; server->app_name=NULL; server->app_port=0; } return server->infd; }