G:/ScriptBasic/source/httpd.c

Go to the documentation of this file.
00001 /*
00002 FILE: httpd.c
00003 HEADER: httpd.h
00004 
00005 TO_HEADER:
00006 
00007 #ifdef WIN32
00008 typedef u_short port_t;
00009 #else
00010 #ifndef __DARWIN__
00011 typedef int port_t;
00012 #endif
00013 #define closesocket close
00014 #endif
00015 
00016 // the default port to listen for the http protocol
00017 #define HTTPD_PORT 80
00018 // the default ip address for the http protocol
00019 #define HTTPD_IP INADDR_ANY
00020 
00021 // the listen backlog that we pass to the operating system
00022 #define LISTEN_BACKLOG 200
00023 
00024 // the maximum number of concurrent hits that we handle
00025 #define CONCURRENT_HITS 100
00026 
00027 // Because this http daemon is designed to serve only GET data
00028 // we maximize the size of a http request
00029 #define HIT_MAX_SIZE 32768
00030 // the maximum number of header lines that we can handle
00031 #define MAX_HEADER_LINES 100
00032 // the maximum length of the query string
00033 #define MAX_QUERY_LENGTH 256
00034 
00035 // the call-back function table for the HttpProc threads
00036 struct _fun {
00037   char *(*pGetServerVariable)();
00038   int (*pWriteClient)();
00039   int (*pReadClient)();
00040   int (*pWriteClientText)();
00041   int (*pState)();
00042   int (*pContentType)();
00043   int (*pHeader)();
00044   int (*pStartBody)();
00045   char *(*pGetParam)();
00046   char *(*pPostParam)();
00047   char *(*pScriptName)();
00048   void (*pCloseClient)();
00049   void (*HttpProc)();
00050   int  (*FtpProc)();
00051   };
00052 
00053 //Defines that help the programmer writing the HttpProc
00054 // make less number of errors
00055 #define GetServerVariable(X) (pT->pFunctions->pGetServerVariable(pT,(X)))
00056 #define WriteClient(X,Y)     (pT->pFunctions->pWriteClient(pT,(X),(Y)))
00057 #define ReadClient(X,Y)      (pT->pFunctions->pReadClient(pT,(X),(Y)))
00058 #define WriteClientText(X)   (pT->pFunctions->pWriteClientText(pT,(X)))
00059 #define State(X)             (pT->pFunctions->pState(pT,(X)))
00060 #define ContentType(X)       (pT->pFunctions->pContentType(pT,(X)))
00061 #define Header(X,Y)          (pT->pFunctions->pHeader(pT,(X),(Y)))
00062 #define StartBody()          (pT->pFunctions->pStartBody(pT))
00063 #define GetParam(X)          (pT->pFunctions->pGetParam(pT,(X)))
00064 #define PostParam(X)         (pT->pFunctions->pPostParam(pT,(X)))
00065 #define ScriptName()         (pT->pFunctions->pScriptName(pT))
00066 #define CloseClient()        (pT->pFunctions->pCloseClient(pT))
00067 
00068 // data that we maintain about each HttpProc thread
00069 typedef struct _ThreadData {
00070   int                ThreadIndex;      // the index of this element
00071   int                SocketOpened;     // true when the socket is opened
00072   int                server_index;     // the index of the server this thread belongs to
00073   SOCKET             msgsock;          // the messaging socket
00074   int                NextFree;         // the next free thread data
00075   void              *pThreadLocalData; // pointer to local data that the thread may use
00076   THREADHANDLE       T;
00077   struct sockaddr_in addr;
00078 
00079 // In these variables we store the header lines so that the application can access it
00080   int                  iHeaderLines;            // the number of header lines
00081   char                *HeaderKey[MAX_HEADER_LINES],
00082                       *HeaderString[MAX_HEADER_LINES];
00083 
00084 // the string of the method
00085   char                *pszMethod;             // REQUEST_METHOD
00086 // the unaltered query string the way the client has sent to us
00087   char                *pszQueryString;        // QUERY_STRING
00088 
00089   unsigned char        ClientIP[4];
00090 
00091   unsigned int         cbAvailable;   // Available number of bytes
00092   unsigned char       *pszData;      // Pointer to cbAvailable bytes
00093   struct _fun         *pFunctions;          // Call Back Function Control Block
00094   char                 getparams[MAX_QUERY_LENGTH];
00095   int                  getparlen;
00096   char                 script[MAX_QUERY_LENGTH];
00097   int                  scriptlen;
00098   char                 buffer[HIT_MAX_SIZE];
00099   struct _HttpdThread *pHT;  
00100   // the application thread data pointer
00101   void                *AppThreadData;
00102   } ThreadData, *pThreadData;
00103 
00104   // constants to define the individual services
00105 #define SERVER_NONE      0
00106 #define SERVER_HTTP      1
00107 #define SERVER_FTP       2
00108 typedef struct _ServerData {
00109   port_t        port;
00110   unsigned long ip;
00111   int           type;
00112   char          *salute;
00113   char          *codebase; // directory where the programs are
00114   // This is a global variable so that the httpproc shutdown can close it.
00115   SOCKET         sock;// This is a global variable so that the httpproc shutdown can close it.
00116   unsigned long cAllowed;// the number of allowed IP masks
00117   unsigned long *plAllowedIP;
00118   unsigned long *plAllowedMask;
00119 
00120   unsigned long cDenied;
00121   unsigned long *plDeniedIP;
00122   unsigned long *plDeniedMask;
00123 
00124   // ftp parameters, BASIC programs file names to start when the command is issued by the client
00125   char          *USER;
00126   char          *PASS;
00127   char          *ACCT;
00128   char          *CWD;
00129   char          *CDUP;
00130   char          *SMNT;
00131   char          *REIN;
00132   char          *QUIT;
00133   char          *PORT;
00134   char          *PASV;
00135   char          *TYPE;
00136   char          *STRU;
00137   char          *MODE;
00138   char          *RETR;
00139   char          *STOR;
00140   char          *STOU;
00141   char          *APPE;
00142   char          *ALLO;
00143   char          *REST;
00144   char          *RNFR;
00145   char          *RNTO;
00146   char          *ABOR;
00147   char          *DELE;
00148   char          *MKD;
00149   char          *PWD;
00150   char          *LIST;
00151   char          *NLST;
00152   char          *SITE;
00153   char          *SYST;
00154   char          *STAT;
00155   char          *HELP;
00156   char          *NOOP;
00157 
00158   } ServerData, *pServerData;
00159 
00160 typedef struct _HttpdThread {
00161 
00162   // This is an array of thread data. They are linked together so that we have a list of free threads.
00163   pThreadData    threads;
00164 
00165   // This variable indexes the first free thread or has the value -1 if there are no free threads
00166   int            FirstFreeThread;
00167 
00168   // Mutex to lock the variable FirstFreeThread
00169   MUTEX          mxFirstFreeThread;
00170 
00171   // The number of threads running
00172   int            cThread;
00173 
00174   // the number of seconds to wait for the threads to stop during shut down
00175   unsigned long  lWaitSec;
00176 
00177   // the number of times to wait lWaitSec seconds before forcefully exiting
00178   unsigned long  lWaitCount;
00179 
00180   // the number of ip:port that we can store
00181 #define MAX_SERVERS 100
00182   ServerData    server[MAX_SERVERS];
00183   // the actual number of servers configured
00184   int            c_servers;
00185 
00186   // The maximum number of threads that we start.
00187   int            threadmax;
00188 
00189   // The listen backlog passed as parameter to listen.
00190   int            listenmax;
00191 
00192   int            iState; // State of the server.
00193 #define STATE_NORMAL 0
00194 #define STATE_SHUT   1 // The server is shutting down.
00195 
00196   MUTEX          mxState; // mutex to lock the state
00197 
00198   // the application  data pointer
00199   void          *AppData;
00200   } HttpdThread, *pHttpdThread;
00201 
00202 */
00203 
00204 #ifndef __NO_SOCKETS__
00205 
00206 #include <stdio.h>
00207 #include <string.h>
00208 #ifdef WIN32
00209 #include <winsock2.h>
00210 #include <winbase.h>
00211 #define SLEEPER 1000
00212 #else
00213 #include <sys/time.h>
00214 #include <sys/types.h>
00215 #include <sys/socket.h>
00216 #include <netinet/in.h>
00217 #include <netdb.h>
00218 #include <unistd.h>
00219 #include <signal.h>
00220 #include <sys/wait.h>
00221 #define Sleep sleep
00222 #define SLEEPER 1
00223 #endif
00224 
00225 #include "thread.h"
00226 #include "filesys.h"
00227 #include "httpd.h"
00228 #include "getopt.h"
00229 
00230 #define MAXBINDTRIAL 1200 /* so many times do we try the bind*/
00231 #define BINDSLEEP    1   /* so many seconds we sleep after an unsuccessful bind */
00232 
00233 /*POD
00234 =H httpd module
00235 
00236 This module is used only by the standalone webserver variation of ScriptBasic.
00237 
00238 The module contains a function R<httpd> that the main application should start. This
00239 function calls the initialization function R<AppInit> and the application starting
00240 functios R<AppStart>. After R<AppStart> returns it starts to listen on the configured
00241 port and accepts http requests and passes the requests to R<HttpProc>.
00242 
00243 CUT*/
00244 
00245 /*POD
00246 =section AppInit
00247 =H AppInit
00248 =bold
00249 This function is not implemented in this module. This function is used by this module
00250 and the program using this module should provide this function.
00251 =nobold
00252 
00253 This function is called by the function R<httpd> practically before anything
00254 is done.
00255 
00256 =verbatim
00257 int AppInit(int argc,char *argv[],pHttpdThread pHT,void **AppData),
00258 =noverbatim
00259 
00260 The R<httpd> function passes the command line arguments back as it gets them plain. The
00261 pointer T<pApp> points to an applicatrion specific T<void> pointer that is initialized to be
00262 T<NULL> and is guaranteed not been changed. The pointer to the same T<void> pointer is passed
00263 also to R<AppStart>. This pointer should be used to pass data between T<AppInit> and R<AppStart>.
00264 
00265 The pointer T<pHT> points to a T<HttpThread> structure and the function T<AppInit> can change
00266 the values of this structure.
00267 
00268 The entry point of the function T<AppInit> should be given to the function R<httpd> as argument.
00269 
00270 CUT*/
00271 
00272 /*POD
00273 =section AppStart
00274 =H AppStart
00275 
00276 =bold
00277 This function is not implemented in this module. This function is used by this module
00278 and the program using this module should provide this function.
00279 =nobold
00280 
00281 This function is called by R<httpd> after binding on the desired port, and after forking
00282 to background on UNIX. This function should start all threads instead of R<AppInit>, otherwise the
00283 forking looses all threads except the main thread. The first version of this
00284 code started the logger threads before the fork and the parent exited with the
00285 running logger threads whil the child daemon did not run the logger threads.
00286 
00287 =verbatim
00288 int AppStart(void **pApp);
00289 =noverbatim
00290 CUT*/
00291 
00292 /*POD
00293 =section HttpProc
00294 =H HttpProc
00295 
00296 =bold
00297 This function is not implemented in this module. This function is used by this module
00298 and the program using this module should provide this function.
00299 =nobold
00300 
00301 This function is called by R<httpd> for each hit in a separate thread.
00302 
00303 =verbatim
00304 void HttpProc(pHttpdThread pHT,pThreadData ThisThread);
00305 =noverbatim
00306 
00307 CUT*/
00308 /*POD
00309 =section FtpProc
00310 =H FtpProc
00311 
00312 =bold
00313 This function is not implemented in this module. This function is used by this module
00314 and the program using this module should provide this function.
00315 =nobold
00316 
00317 This function is called by R<httpd> for each ftp command.
00318 
00319 =verbatim
00320 void FtProc(pHttpdThread pHT,pThreadData ThisThread, char *pszCommand);
00321 =noverbatim
00322 
00323 CUT*/
00324 #ifdef WIN32
00325 /* This is a total DUMMY under Windows NT */
00326 void InitSignalHandlers(){}
00327 #else
00328 void SEGV_handle(int i) {
00329     char fname[2000];
00330     int pid;
00331     pid=getpid();
00332     fclose(fopen(fname,"w+"));
00333     sleep(1000000);
00334 }
00335 
00336 /*POD
00337 =section InitSignalHandlers
00338 =H Initialize the signal handlers under UNIX
00339 
00340 This function is called under UNIX to initialize the signal handlers.
00341 This makes the application ignoring if the socket pipe is broken by the
00342 client application.
00343 
00344 Under Win32 there is a dummy function doing nothing in place of this function.
00345 
00346 /*FUNCTION*/
00347 void InitSignalHandlers(
00348   ){
00349 /*noverbatim
00350 CUT*/
00351   signal(SIGPIPE,SIG_IGN);
00352   signal(SIGSEGV,SEGV_handle); 
00353  
00354   }
00355 
00356 #endif
00357 
00358 static struct _StateCode {
00359   int Code;
00360   char *StateMessage;
00361   }StateMessages[]={
00362   200, "OK",
00363   201, "CREATED",
00364   202, "Accepted",
00365   203, "Partial Information",
00366   204, "No Response",
00367   301, "Found, but moved",
00368   302, "Found, but data resides under different URL (add a /)",
00369   303, "Method",
00370   304, "Not Modified",
00371   400, "Bad request",
00372   401, "Unauthorized",
00373   402, "PaymentRequired",
00374   403, "Forbidden",
00375   404, "Not found",
00376   500, "Internal Error",
00377   501, "Not implemented",
00378   502, "Service temporarily overloaded",
00379   503, "Gateway timeout ",
00380   600, "Bad request",
00381   601, "Not implemented",
00382   602, "Connection failed",
00383   603, "Timed out",
00384   000, NULL
00385   };
00386 
00387 static int unhex(int t){
00388   if( t >= 'A' && t <= 'F' )return t-'A'+10;
00389   if( t >= 'a' && t <= 'f' )return t-'a'+10;
00390   return t-'0';
00391   }
00392 
00393 static int strIcmp(char *a, char *b){
00394   int ca,cb;
00395 
00396   while( (ca=*a) && (cb=*b) ){           
00397     if( isupper(ca) )ca = tolower(ca);
00398     if( isupper(cb) )cb = tolower(cb);
00399     if( ca != cb )return ca-cb;
00400     a++,b++;
00401     }
00402   return 0;
00403   }
00404 
00405 static char *_GetServerVariable(pThreadData ThisThread,
00406                             char *VariableName){
00407   int i;
00408 
00409   for( i = 0 ; i < ThisThread->iHeaderLines ; i++ ){
00410     if( ! strIcmp(ThisThread->HeaderKey[i],VariableName) )return ThisThread->HeaderString[i];
00411     }
00412   return NULL;
00413   }
00414 
00415 static void _CloseClient(pThreadData ThisThread){
00416   if( ThisThread->SocketOpened ){
00417     closesocket(ThisThread->msgsock);
00418     ThisThread->SocketOpened = 0;
00419     }
00420   }
00421 
00422 static int _WriteClient(pThreadData ThisThread,
00423                     char *pszBuffer,
00424                     int cbBuffer){
00425   int i;
00426   fd_set writefds;
00427   struct timeval timeout;
00428 
00429   FD_ZERO(&writefds);
00430   FD_SET(ThisThread->msgsock,&writefds);
00431   timeout.tv_sec=60;
00432   timeout.tv_usec=0;
00433   i = select(FD_SETSIZE,NULL,&writefds,NULL,&timeout);
00434   if( i == 0 )return 1;
00435   return cbBuffer != send(ThisThread->msgsock,pszBuffer,cbBuffer,0);
00436   }
00437 
00438 static int _WriteClientText(pThreadData ThisThread,
00439                             char *pszBuffer){
00440   int i;
00441   fd_set writefds;
00442   struct timeval timeout;
00443 
00444   FD_ZERO(&writefds);
00445   FD_SET(ThisThread->msgsock,&writefds);
00446   timeout.tv_sec=60;
00447   timeout.tv_usec=0;
00448   i = select(FD_SETSIZE,NULL,&writefds,NULL,&timeout);
00449   if( i == 0 )return 1;
00450 
00451   if( pszBuffer == NULL )
00452     return 4 != send(ThisThread->msgsock,"null",4,0);
00453 
00454   i = strlen(pszBuffer);
00455   return i != send(ThisThread->msgsock,pszBuffer,i,0);
00456   }
00457 
00458 static int _State(pThreadData ThisThread,
00459                    int StateCode){
00460   int i;
00461   char buffer[80];
00462 
00463   for( i = 0 ; StateMessages[i].StateMessage && 
00464                StateMessages[i].Code <= StateCode ; i++ ){
00465     if( StateMessages[i].Code == StateCode ){
00466       sprintf(buffer,"HTTP/1.0 %d %s\n",StateCode,StateMessages[i].StateMessage);
00467       return _WriteClientText(ThisThread,buffer);
00468       }
00469     }
00470   sprintf(buffer,"HTTP/1.0 %d\n",StateCode);
00471   return _WriteClientText(ThisThread,buffer);
00472   }
00473 
00474 static int _ContentType(pThreadData ThisThread,
00475                         char *pszContentType){
00476   _WriteClient(ThisThread,"Content-Type: ",14);
00477   _WriteClientText(ThisThread,pszContentType);
00478   return _WriteClient(ThisThread,"\n",1);
00479   }
00480 
00481 static int _Header(pThreadData ThisThread,
00482                    char *pszHeaderKey,
00483                    char *pszHeaderValue){
00484   _WriteClientText(ThisThread,pszHeaderKey);
00485   _WriteClient(ThisThread,": ",2);
00486   _WriteClientText(ThisThread,pszHeaderValue);
00487   return _WriteClient(ThisThread,"\n",1);
00488   }
00489 
00490 static int _StartBody(pThreadData ThisThread){
00491   return _WriteClient(ThisThread,"\n",1);
00492   }
00493 
00494 static int _ReadClient(pThreadData ThisThread,
00495                    char *pszBuffer,
00496                    int cbBuffer){
00497   int i;
00498   fd_set readfds;
00499   struct timeval timeout;
00500 
00501   FD_ZERO(&readfds);
00502   FD_SET(ThisThread->msgsock,&readfds);
00503   timeout.tv_sec=60;
00504   timeout.tv_usec=0;
00505   i = select(FD_SETSIZE,&readfds,NULL,NULL,&timeout);
00506   if( i == 0 )return 0;
00507 
00508   return recv(ThisThread->msgsock,pszBuffer,cbBuffer,0);
00509   }
00510 
00511 static char *_ScriptName(pThreadData ThisThread){
00512   int i,j;
00513 
00514   if( ! ThisThread->script[0] ){
00515     for( i = 0 ; ThisThread->pszQueryString[i] ; i++ )
00516       if( ThisThread->pszQueryString[i] == '?' )break;
00517     while( i && ThisThread->pszQueryString[i] != '/' )i--;
00518     if( ThisThread->pszQueryString[i] == '/' )i++;
00519     for( j = 0 ; ThisThread->pszQueryString[i] &&
00520                  ThisThread->pszQueryString[i] != '?' ; i++, j++ ){
00521       ThisThread->script[j] = ThisThread->pszQueryString[i];
00522       if( j >= MAX_QUERY_LENGTH ){
00523         ThisThread->script[0] = (char)0;
00524         return NULL;
00525         }
00526       }
00527     ThisThread->script[j] = (char)0;
00528     }
00529   return ThisThread->script;
00530   }
00531 
00532 static char *_GetParam(pThreadData ThisThread,
00533                      char *key){
00534   char *s;
00535   int i,j;
00536 
00537   if( ! ThisThread->getparams[0] ){
00538     /* when first called it contains no data */
00539     s = ThisThread->pszQueryString;
00540     while( *s && *s != '?' )s++;
00541     if( ! *s )return NULL;
00542     s++; /* step over the ? */
00543     /* check the size before copiing */
00544     ThisThread->getparlen = strlen(s);
00545     if( ThisThread->getparlen >= MAX_QUERY_LENGTH )return NULL;
00546     strcpy(ThisThread->getparams,s);
00547     s = ThisThread->getparams;
00548     for( i=j=0 ; ; i++ ){
00549        s[i] = s[j];
00550        if( ! s[j] )break;
00551        if( s[j] == '%' && s[j+1] && s[j+2] ){
00552          s[i] = unhex(s[j+1])*16+unhex(s[j+2]);
00553          j += 3;
00554          }else j++;
00555        }
00556     ThisThread->getparlen = i;
00557     s = ThisThread->getparams;
00558     while( *s ){
00559       if( *s == '&' )*s = (char)0;
00560       s++;
00561       }
00562     }
00563   /* Now we have the get params in ThisThread->getparams */
00564   s = ThisThread->getparams;
00565   for( i = 0 ; i < ThisThread->getparlen ; i++ ){
00566     for( j = 0 ; key[j] && s[i] && s[i] != '=' ; j++, i++ ){
00567       if( s[i] != key[j] ){
00568         while( s[i] )i++;
00569         break;
00570         }
00571       }
00572     if( s[i] )return s+i+1;
00573     }
00574   return NULL;
00575   }
00576 
00577 static char *_PostParam(pThreadData ThisThread,
00578                       char *key){
00579   return NULL;
00580   }
00581 
00582 /*POD
00583 =section httpd
00584 =H httpd
00585 
00586 This is the main entry point of the module. This function should be called by the
00587 main function passing the command line arguments and the three callback functions:
00588 
00589 =itemize
00590 =item R<AppInit> is called to initialize the webserver. Should read configuration.
00591 =item R<AppStart> should start the worker threads.
00592 =item R<HttpProc> should handle the individual http requests.
00593 =item R<FtpProc> should handle the individual ftp commands.
00594 =noitemize
00595 
00596 The function T<httpd> never returns.
00597 /*FUNCTION*/
00598 int httpd(int argc,
00599           char *argv[],
00600           int (*AppInit)(int argc,char *argv[],pHttpdThread pHT,void **AppData),
00601           int (*AppStart)(void **AppData),
00602           void (*HttpProc)(pHttpdThread pHT,pThreadData ThisThread),
00603           int (*FtpProc)(pHttpdThread pHT,pThreadData ThisThread, char *pszCommand)
00604 ){
00605 /*noverbatim
00606 CUT*/
00607   int addr_size;
00608   int i,j,so;
00609   int length;
00610   struct _fun Functions;
00611   struct sockaddr_in server;
00612   pThreadData ThisThread;
00613   HttpdThread HT;
00614   int iState;
00615   int cThread;
00616   unsigned long iL;
00617 #ifdef WIN32
00618   WORD wVersionRequested;
00619   WSADATA wsaData;
00620   int err;
00621 #else
00622   int cpid;
00623 #endif
00624   char *optarg,opt;
00625   int OptionIndex;
00626   fd_set acceptfds;
00627   struct timeval timeout;
00628 
00629 
00630 #ifdef WIN32
00631   wVersionRequested = MAKEWORD( 2, 2 );
00632   err = WSAStartup( wVersionRequested, &wsaData );
00633   if( err != 0 ){
00634     fprintf(stderr,"Error initializing the Windows Socket subsystem\n");
00635     exit(1);
00636     }
00637 #endif
00638   HT.server[0].port      = HTTPD_PORT;
00639   HT.server[0].ip        = HTTPD_IP;
00640   HT.c_servers           = 1;
00641   HT.threadmax           = CONCURRENT_HITS;
00642   HT.listenmax           = LISTEN_BACKLOG;
00643   HT.server[0].cAllowed  = 0;
00644   HT.server[0].cDenied   = 0;
00645 
00646   /* init the signal handlerts to SIG_IGN so that a brokern pipe does not kill us */
00647   InitSignalHandlers();
00648 
00649   /* This is just a pointer that we guarantee not to be altered. */
00650   HT.AppData = NULL;
00651   /* AppInit should return zero on success. */
00652   if( i=AppInit(argc,argv,&HT,&(HT.AppData)) ){
00653     fprintf(stderr,"AppInit returned %d\n",i);
00654     exit(i);
00655     }
00656 
00657   OptionIndex = 0;
00658   while( (opt = getoptt(argc, argv, "p:h:",&optarg,&OptionIndex)) != ':'){
00659     switch( opt ){
00660       case 'p' : /* you can specify a port number on the command line to bind on */
00661                  /* this overrides the configuration port */
00662          HT.server[0].port  = atoi(optarg);
00663          HT.c_servers = 1;
00664          break;
00665       case 'h' : /* you can specify a single host IP to bind on */
00666                  /* this overrides the configuration ip */
00667          HT.server[0].ip     = inet_addr(optarg);
00668          HT.c_servers  = 1;
00669          break;
00670       }
00671     }
00672 
00673   for( i=0 ; i < HT.c_servers ; i++ ){
00674     HT.server[i].sock = socket(AF_INET, SOCK_STREAM, 0);
00675     so=1;
00676     setsockopt(HT.server[i].sock, SOL_SOCKET, SO_REUSEADDR, (char *)(&so),sizeof(i));
00677     if( HT.server[i].sock < 0 ){
00678       fprintf(stderr, "Error at socket");
00679       exit(1);
00680       }
00681     server.sin_family = AF_INET;   
00682     server.sin_addr.s_addr = HT.server[i].ip;
00683     server.sin_port = htons(HT.server[i].port);
00684 
00685     for( j=0 ; j<MAXBINDTRIAL ; j++ ){
00686       if( ! bind(HT.server[i].sock, (struct sockaddr *)&server, sizeof(server)) )break;
00687       if( j == MAXBINDTRIAL-1 ){
00688         fprintf(stderr, "\nError at bind.");
00689         exit(1);
00690         }
00691       if( j== 0 )fprintf(stderr,"Bind failed on %s:%d, retrying at most %d times\n.",
00692                             inet_ntoa(server.sin_addr), ntohs(server.sin_port),MAXBINDTRIAL);
00693       else fprintf(stderr,".");
00694       if( j%40 == 39 )fprintf(stderr,"\n");
00695       Sleep(BINDSLEEP);
00696       }
00697     if( j )fprintf(stderr,"\nBind finally successful after %d trials\n",j);
00698     
00699     length = sizeof(server);
00700     if( getsockname(HT.server[i].sock, (struct sockaddr *)&server, &length) ){ 
00701       fprintf(stderr, "Error at getsockname.");
00702       exit(1);
00703       }
00704 
00705     listen(HT.server[i].sock, HT.listenmax);
00706     }
00707   HT.iState = STATE_NORMAL;
00708 
00709   if( j=AppStart(&(HT.AppData)) ){
00710     fprintf(stderr,"Appstart returned %d\n",j);
00711     exit(j);
00712     }
00713 
00714   HT.threads = (pThreadData)malloc(HT.threadmax*sizeof(ThreadData));
00715   if( HT.threads == NULL ){
00716     fprintf(stderr,"Not enough memory\n");
00717     exit(1);
00718     }
00719 
00720   /* Initialize the list of thread data
00721   */
00722   for( i=0 ; i < HT.threadmax ; i++ ){
00723     HT.threads[i].ThreadIndex = i;
00724     HT.threads[i].pFunctions = &Functions;
00725     HT.threads[i].NextFree   = i+1;
00726     HT.threads[i].pHT        = &HT;
00727     }
00728   HT.cThread = 0;
00729   /* make it dead end */
00730   HT.threads[HT.threadmax-1].NextFree = -1;
00731   HT.FirstFreeThread = 0;
00732   thread_InitMutex(&(HT.mxFirstFreeThread));
00733   Functions.pGetServerVariable = _GetServerVariable;
00734   Functions.pWriteClient       = _WriteClient;
00735   Functions.pWriteClientText   = _WriteClientText;
00736   Functions.pReadClient        = _ReadClient;
00737   Functions.pState             = _State;
00738   Functions.pContentType       = _ContentType;
00739   Functions.pHeader            = _Header;
00740   Functions.pStartBody         = _StartBody;
00741   Functions.pGetParam          = _GetParam;
00742   Functions.pPostParam         = _PostParam;
00743   Functions.pCloseClient       = _CloseClient;
00744   Functions.pScriptName        = _ScriptName;
00745   Functions.HttpProc           = HttpProc;
00746   Functions.FtpProc            = FtpProc;
00747   while(1){
00748     ThisThread = GetFreeThread(&HT);
00749 
00750     FD_ZERO(&acceptfds);
00751     for( i=0 ; i < HT.c_servers ; i++ )
00752       FD_SET(HT.server[i].sock,&acceptfds);
00753     timeout.tv_sec=60;
00754     timeout.tv_usec=0;
00755     i = select(FD_SETSIZE,&acceptfds,NULL,NULL,&timeout);
00756     for( i=0 ; i < HT.c_servers ; i++ ){
00757       if( FD_ISSET(HT.server[i].sock,&acceptfds) ){
00758         do{
00759           addr_size=sizeof(struct sockaddr);
00760           ThisThread->msgsock = accept(HT.server[i].sock,
00761                                       (struct sockaddr *)&(ThisThread->addr),
00762                                       &addr_size);
00763           }while(
00764     #ifdef WIN32
00765                   ThisThread->msgsock == INVALID_SOCKET
00766     #else
00767                   ThisThread->msgsock <= 0
00768     #endif
00769                   );
00770         thread_LockMutex(&(HT.mxState));
00771         iState = HT.iState;
00772         thread_UnlockMutex(&(HT.mxState));
00773         if( iState == STATE_SHUT ){/* we have to stop */
00774           /* wait for all threads to stop */
00775           for( iL = 0 ; iL < HT.lWaitCount ; iL++ ){
00776             thread_LockMutex(&(HT.mxFirstFreeThread));
00777             cThread = HT.cThread;
00778             thread_UnlockMutex(&(HT.mxFirstFreeThread));
00779             if( cThread == 1 )break;
00780             Sleep(HT.lWaitSec*SLEEPER);
00781             }
00782           return 0;
00783           }
00784         ThisThread->SocketOpened = 1;
00785         ThisThread->server_index = i;
00786         thread_CreateThread(&(ThisThread->T),HitHandler,ThisThread);
00787         }
00788       }
00789     }
00790   /* we never get here */
00791   return 0;
00792   }
00793 
00794 /*FUNCTION*/
00795 pThreadData GetFreeThread(pHttpdThread pHT
00796   ){
00797   pThreadData t;
00798   /* get exclusive access to the first free thread index */
00799   thread_LockMutex(&(pHT->mxFirstFreeThread));
00800   /* while there is no free thread wait a second and try again */
00801   while( pHT->FirstFreeThread == -1 ){
00802     thread_UnlockMutex(&(pHT->mxFirstFreeThread));
00803     Sleep(1*SLEEPER);
00804     thread_LockMutex(&(pHT->mxFirstFreeThread));
00805     }
00806   t = pHT->threads+pHT->FirstFreeThread;
00807   pHT->FirstFreeThread = pHT->threads[pHT->FirstFreeThread].NextFree;
00808   pHT->cThread++;
00809   thread_UnlockMutex(&(pHT->mxFirstFreeThread));
00810 
00811   return t;
00812   }
00813 
00814 /*FUNCTION*/
00815 void ReleaseThreadData(pHttpdThread pHT,
00816                        int index
00817   ){
00818   thread_LockMutex(&(pHT->mxFirstFreeThread));
00819   pHT->threads[index].NextFree = pHT->FirstFreeThread;
00820   pHT->FirstFreeThread = index;
00821   pHT->cThread--;
00822   thread_UnlockMutex(&(pHT->mxFirstFreeThread));
00823   }
00824 
00825 #define MSPACE(x) while( *x && isspace(*x)) x++
00826 #define SSPACE(x) if( *x && isspace(*x)) x++
00827 
00828 /*FUNCTION*/
00829 int CheckAllowDeny(pThreadData ThisThread
00830   ){
00831   int fAllowed;
00832   pHttpdThread pHT;
00833   unsigned long h;
00834   unsigned long lClientIP;
00835   pServerData ThisServer;
00836 
00837   pHT = ThisThread->pHT;
00838   /*
00839    * check that the client is on the allowed list and is not on the denied list
00840    */
00841   fAllowed = 1;/* allowed if there is no allowed/denied client list */
00842 
00843   /* pointer to the server data that this thread belongs to */
00844   ThisServer = pHT->server + ThisThread->server_index;
00845 
00846   if( ThisServer->cAllowed || ThisServer->cDenied ){
00847 
00848     lClientIP = ThisThread->ClientIP[3] + 256*(
00849                 ThisThread->ClientIP[2] + 256*(
00850                 ThisThread->ClientIP[1] + 256*(
00851                 ThisThread->ClientIP[0] )));
00852     if( ThisServer->cAllowed ){
00853       fAllowed = 0;/* allowed only if any of the allowed host list matches */
00854       for( h=0 ; h < ThisServer->cAllowed ; h++ ){
00855         if( (lClientIP&ThisServer->plAllowedMask[h]) == 
00856             (ThisServer->plAllowedIP[h]&ThisServer->plAllowedMask[h]) ){
00857           fAllowed = 1;
00858           break;
00859           }
00860         }
00861       }
00862     if( fAllowed && ThisServer->cDenied ){
00863       for( h=0 ; h < ThisServer->cDenied ; h++ ){
00864         if( (lClientIP&ThisServer->plDeniedMask[h]) == 
00865             (ThisServer->plDeniedIP[h]&ThisServer->plDeniedMask[h]) ){
00866           fAllowed = 0;
00867           break;
00868           }
00869         }
00870       }
00871     }
00872     return fAllowed;
00873   }
00874 /*POD
00875 =section FinishConnection
00876 =H FinishConnection
00877 
00878 This function closes the client connection and finishes the thread this is
00879 running in. Thus this fucntion does not return. This function should be called
00880 from the hit handling functions that are started in asynchronous thread when
00881 the connection was handled or should for some reason abandoned.
00882 
00883 The function T<FinishConnection> never returns.
00884 
00885 /*FUNCTION*/
00886 void FinishConnection(pThreadData ThisThread
00887   ){
00888 /*noverbatim
00889 CUT*/
00890   if( ThisThread->SocketOpened ){
00891     closesocket(ThisThread->msgsock);
00892     ThisThread->SocketOpened = 0;
00893     }
00894   ReleaseThreadData(ThisThread->pHT,ThisThread->ThreadIndex);
00895   thread_ExitThread();
00896   }
00897 
00898 
00899 /*FUNCTION*/
00900 void HitHandler(void *t
00901   ){
00902   pThreadData ThisThread;
00903   pServerData ThisServer;
00904   pHttpdThread pHT;
00905 
00906   ThisThread = (pThreadData)t;
00907   pHT = ThisThread->pHT;
00908 
00909   ThisThread->AppThreadData = NULL;
00910 
00911 #ifdef WIN32
00912   ThisThread->ClientIP[0] = ThisThread->addr.sin_addr.S_un.S_un_b.s_b1;
00913   ThisThread->ClientIP[1] = ThisThread->addr.sin_addr.S_un.S_un_b.s_b2;
00914   ThisThread->ClientIP[2] = ThisThread->addr.sin_addr.S_un.S_un_b.s_b3;
00915   ThisThread->ClientIP[3] = ThisThread->addr.sin_addr.S_un.S_un_b.s_b4;
00916 #else
00917   memcpy(ThisThread->ClientIP,&(ThisThread->addr.sin_addr.s_addr),4);
00918 #endif
00919 
00920   if( ! CheckAllowDeny(ThisThread) )FinishConnection(ThisThread);
00921 
00922   /* pointer to the server data that this thread belongs to */
00923   ThisServer = pHT->server + ThisThread->server_index;
00924   switch( ThisServer->type ){
00925     case SERVER_HTTP:
00926       HandleHttpHit(ThisThread);
00927     case SERVER_FTP:
00928       HandleFtpHit(ThisThread);
00929     }
00930   FinishConnection(ThisThread);
00931   }
00932 
00933 /*FUNCTION*/
00934 void HandleFtpHit(pThreadData ThisThread
00935   ){
00936   pServerData ThisServer;
00937   fd_set readfds;
00938   struct timeval timeout;
00939   pHttpdThread pHT;
00940   char *Buffer;
00941   int cbBuffer,cbCharsRead,i;
00942 
00943   Buffer = ThisThread->buffer;
00944   cbBuffer = HIT_MAX_SIZE;
00945 
00946   /* pointer to the server data that this thread belongs to */
00947   pHT = ThisThread->pHT;
00948   ThisServer = pHT->server + ThisThread->server_index;
00949 
00950   send(ThisThread->msgsock,ThisServer->salute,strlen(ThisServer->salute),0);
00951   send(ThisThread->msgsock,"\r\n",2,0);
00952 
00953   while(1){
00954     FD_ZERO(&readfds);
00955     FD_SET(ThisThread->msgsock,&readfds);
00956     timeout.tv_sec=60;
00957     timeout.tv_usec=0;
00958     i = select(FD_SETSIZE,&readfds,NULL,NULL,&timeout);
00959     if( i == 0 )FinishConnection(ThisThread);
00960     cbCharsRead = recv(ThisThread->msgsock,Buffer,cbBuffer,0);
00961     /* some clients send such packets, but I could not figure out how */
00962     if( cbCharsRead == 0 )FinishConnection(ThisThread);
00963     if( cbCharsRead < 0 )FinishConnection(ThisThread);
00964     Buffer[cbCharsRead] = (char)0;
00965     /* note that FinishConnection function not only closes the socket but also terminates the this thread thus
00966        there us no need to get out of the loop and return from this function */
00967     if( ThisThread->pFunctions->FtpProc(ThisThread->pHT,ThisThread,Buffer) )FinishConnection(ThisThread);
00968     }
00969   }
00970 
00971 /*FUNCTION*/
00972 void HandleHttpHit(pThreadData ThisThread
00973   ){
00974   pServerData ThisServer;
00975   int j,i,cbBuffer,cbCharsRead,cbCharsReadTotal;
00976   char *Buffer;
00977   char *s;
00978   void MyHttpExtensionProc(pThreadData);
00979   fd_set readfds;
00980   struct timeval timeout;
00981   pHttpdThread pHT;
00982 
00983   Buffer = ThisThread->buffer;
00984   cbBuffer = HIT_MAX_SIZE;
00985 
00986   /* pointer to the server data that this thread belongs to */
00987   pHT = ThisThread->pHT;
00988   ThisServer = pHT->server + ThisThread->server_index;
00989   
00990   cbCharsReadTotal = 0;
00991   while(1){
00992     FD_ZERO(&readfds);
00993     FD_SET(ThisThread->msgsock,&readfds);
00994     timeout.tv_sec=60;
00995     timeout.tv_usec=0;
00996     i = select(FD_SETSIZE,&readfds,NULL,NULL,&timeout);
00997     if( i == 0 )FinishConnection(ThisThread);
00998     cbCharsRead = recv(ThisThread->msgsock,Buffer,cbBuffer,0);
00999     /* some clients send such packets, but I could not figure out how */
01000     if( cbCharsRead == 0 )FinishConnection(ThisThread);
01001     if( cbCharsRead < 0 )FinishConnection(ThisThread);
01002 
01003     /* If this is the first chunk read then we are searching for the header terminating
01004        empty line from the first character. If this is not the first chunk then we start the
01005        search from the end of the previous chunk. */
01006     if( cbCharsReadTotal < 3 )j = 4 - cbCharsReadTotal; else j = 1;
01007 
01008     cbCharsReadTotal += cbCharsRead;
01009 
01010     /* if we have found the empty line following the header */
01011     for( ; j <= cbCharsRead ; j++ ){
01012       if( Buffer[j-1] == '\n' && Buffer[j-2] == '\r' &&
01013           Buffer[j-3] == '\n' && Buffer[j-4] == '\r' ){
01014         Buffer[j-4] = (char)0;
01015         ThisThread->pszData = Buffer+j;
01016         ThisThread->cbAvailable = cbCharsRead - j;
01017         goto HEADER_IS_READ;
01018         }
01019       }
01020     cbBuffer -= i;
01021     Buffer += i;
01022     if( cbBuffer <= 0 )FinishConnection(ThisThread);
01023     }
01024 HEADER_IS_READ:;
01025 
01026   /* Here we have the total HTTP GET header. */
01027   s = ThisThread->pszMethod = ThisThread->buffer;
01028   while( *s && ! isspace(*s) )s++;
01029   if( *s )
01030     *s++ = (char)0;
01031   else FinishConnection(ThisThread);
01032 
01033   ThisThread->pszQueryString = s;
01034   while( *s && ! isspace(*s) )s++;
01035   if( *s )
01036     *s++ = (char)0;
01037   else FinishConnection(ThisThread);
01038 
01039   /* if there is anything after the URL until the new line then skip it */
01040   while( *s && *s != '\n' )s++;
01041   /* skip the CR/LF */
01042   while( *s == '\n' || *s == '\r' )s++;
01043 
01044   ThisThread->iHeaderLines = 0;
01045   /* now s points to the start of the first header line */
01046   while( *s ){
01047 
01048     ThisThread->HeaderKey[ThisThread->iHeaderLines] = s;
01049     while( *s && ! isspace(*s) && *s != ':' )s++;
01050     if( *s )*s++ = (char)0;
01051     while( isspace(*s) )s++;
01052     if( *s ){
01053       ThisThread->HeaderString[ThisThread->iHeaderLines] = s;
01054       while( *s && *s != '\n' && *s != '\r' )s++;
01055       if( *s )*s++ = (char)0;
01056       while( *s == '\n' || *s == '\r' )s++;
01057       ThisThread->iHeaderLines++;
01058       }
01059     }
01060 
01061   ThisThread->getparams[0] = (char)0;
01062   ThisThread->script[0] = (char)0;
01063   ThisThread->pThreadLocalData = NULL;
01064 
01065   ThisThread->pFunctions->HttpProc(ThisThread->pHT,ThisThread);
01066 
01067   FinishConnection(ThisThread);
01068   }
01069 #endif /* __NO_SOCKETS__ */

Generated on Sun Mar 12 23:56:30 2006 for ScriptBasic by  doxygen 1.4.6-NO