1 /* HTTPSERV.C   (c)Copyright Jan Jaeger, 2002-2009                   */
2 /*              HTTP Server                                          */
3 
4 /* This file contains all code required for the HTTP server,         */
5 /* when the http_server thread is started it will listen on          */
6 /* the HTTP port specified on the HTTPPORT config statement.         */
7 /*                                                                   */
8 /* When a request comes in a http_request thread is started,         */
9 /* which will handle the request.                                    */
10 /*                                                                   */
11 /* When authentification is required (auth parm on the HTTPPORT      */
12 /* statement) then the user will be authenticated based on the       */
13 /* userid and password supplied on the HTTPPORT statement, if        */
14 /* these where not supplied then the userid and password of the      */
15 /* that hercules is running under will be used. In the latter case   */
16 /* the root userid/password will also be accepted.                   */
17 /*                                                                   */
18 /* If the request is for a /cgi-bin/ path, then the cgibin           */
19 /* directory in cgibin.c will be searched, for any other request     */
20 /* the sysblk.httproot (/usr/local/hercules) will be used as the     */
21 /* root to find the specified file.                                  */
22 /*                                                                   */
23 /* As realpath() is used to verify that the files are from within    */
24 /* the sysblk.httproot tree symbolic links that refer to files       */
25 /* outside the sysblk.httproot tree are not supported.               */
26 /*                                                                   */
27 /*                                                                   */
28 /*                                           Jan Jaeger - 28/03/2002 */
29 
30 #include "hstdinc.h"
31 
32 #define _HTTPSERV_C_
33 #define _HENGINE_DLL_
34 
35 #include "hercules.h"
36 #include "httpmisc.h"
37 #include "hostinfo.h"
38 
39 
40 #if defined(OPTION_HTTP_SERVER)
41 
42 /* External reference to the cgi-bin directory in cgibin.c */
43 extern CGITAB cgidir[];
44 
45 
46 static MIMETAB mime_types[] = {
47     { NULL,    NULL },                            /* No suffix entry */
48     { "txt",   "text/plain"                },
49     { "jcl",   "text/plain"                },
50     { "gif",   "image/gif"                 },
51     { "jpg",   "image/jpeg"                },
52     { "css",   "text/css"                  },
53     { "html",  "text/html"                 },
54     { "htm",   "text/html"                 },
55 /* This one should be:
56     { "ico",   "image/vnd.microsoft.icon"  },
57    but Apache 2 sets it as: */
58     { "ico",   "image/x-icon"              },
59 /* so we'll go with what's actually in use. --JRM */
60     { NULL,    NULL } };                     /* Default suffix entry */
61 
html_include(WEBBLK * webblk,char * filename)62 DLL_EXPORT int html_include(WEBBLK *webblk, char *filename)
63 {
64     FILE *inclfile;
65     char fullname[HTTP_PATH_LENGTH];
66     char buffer[HTTP_PATH_LENGTH];
67     int ret;
68 
69     strlcpy( fullname, sysblk.httproot, sizeof(fullname) );
70     strlcat( fullname, filename,        sizeof(fullname) );
71 
72     inclfile = fopen(fullname,"rb");
73 
74     if (!inclfile)
75     {
76         logmsg(_("HHCHT011E html_include: Cannot open %s: %s\n"),
77           fullname,strerror(errno));
78         hprintf(webblk->sock,_("ERROR: Cannot open %s: %s\n"),
79           filename,strerror(errno));
80         return FALSE;
81     }
82 
83     while (!feof(inclfile))
84     {
85         ret = fread(buffer, 1, sizeof(buffer), inclfile);
86         if (ret <= 0) break;
87         hwrite(webblk->sock,buffer, ret);
88     }
89 
90     fclose(inclfile);
91     return TRUE;
92 }
93 
html_header(WEBBLK * webblk)94 DLL_EXPORT void html_header(WEBBLK *webblk)
95 {
96     if (webblk->request_type != REQTYPE_POST)
97         hprintf(webblk->sock,"Expires: 0\n");
98 
99     hprintf(webblk->sock,"Content-type: text/html\n\n");
100 
101     if (!html_include(webblk,HTML_HEADER))
102         hprintf(webblk->sock,"<HTML>\n<HEAD>\n<TITLE>Hercules</TITLE>\n</HEAD>\n<BODY>\n\n");
103 }
104 
105 
html_footer(WEBBLK * webblk)106 DLL_EXPORT void html_footer(WEBBLK *webblk)
107 {
108     if (!html_include(webblk,HTML_FOOTER))
109         hprintf(webblk->sock,"\n</BODY>\n</HTML>\n");
110 }
111 
112 
http_exit(WEBBLK * webblk)113 static void http_exit(WEBBLK *webblk)
114 {
115 CGIVAR *cgivar;
116 int rc;
117     if(webblk)
118     {
119         /* MS SDK docs state:
120 
121             "To assure that all data is sent and received on a connected
122              socket before it is closed, an application should use shutdown
123              to close connection before calling closesocket. For example,
124              to initiate a graceful disconnect:
125 
126                 1, Call WSAAsyncSelect to register for FD_CLOSE notification.
127                 2. Call shutdown with how=SD_SEND.
128                 3. When FD_CLOSE received, call recv until zero returned,
129                    or SOCKET_ERROR.
130                 4. Call closesocket.
131 
132             Note: The shutdown function does not block regardless of the
133             SO_LINGER setting on the socket."
134         */
135 
136         // Notify other end of connection to not expect any more data from us.
137         // They should detect this via their own 'recv' returning zero bytes
138         // (thus letting them know they've thus received all the data from us
139         // they're ever going to receive). They should then do their own
140         // 'shutdown(s,SHUT_WR)' at their end letting US know we're also not
141         // going to be receiving any more data from THEM. This is called a
142         // "graceful close" of the connection...
143 
144         shutdown( webblk->sock, SHUT_WR );
145 
146         // Now wait for them to shudown THEIR end of the connection (i.e. wait
147         // for them to do their own 'shutdown(s,SHUT_WR)') by "hanging" on a
148         // 'recv' call until we either eventually detect they've shutdown their
149         // end of the connection (0 bytes received) or else an error occurs...
150 
151         do
152         {
153             BYTE c;
154             rc = read_socket( webblk->sock, &c, 1 );
155         }
156         while ( rc > 0 );
157 
158         // NOW we can SAFELY close the socket since we now KNOW for CERTAIN
159         // that they've received ALL of the data we previously sent to them...
160         // (otherwise they wouldn't have close their connection on us!)
161 
162         close_socket( webblk->sock );
163 
164         if(webblk->user) free(webblk->user);
165         if(webblk->request) free(webblk->request);
166         cgivar = webblk->cgivar;
167         while(cgivar)
168         {
169             CGIVAR *tmpvar = cgivar->next;
170             free(cgivar->name);
171             free(cgivar->value);
172             free(cgivar);
173             cgivar = tmpvar;
174         }
175         free(webblk);
176     }
177     exit_thread(NULL);
178 }
179 
180 
http_error(WEBBLK * webblk,char * err,char * header,char * info)181 static void http_error(WEBBLK *webblk, char *err, char *header, char *info)
182 {
183     hprintf(webblk->sock,"HTTP/1.0 %s\n%sConnection: close\n"
184                           "Content-Type: text/html\n\n"
185                           "<HTML><HEAD><TITLE>%s</TITLE></HEAD>"
186                           "<BODY><H1>%s</H1><P>%s</BODY></HTML>\n\n",
187                           err, header, err, err, info);
188     http_exit(webblk);
189 }
190 
191 
http_timestring(char * time_buff,int buff_size,time_t t)192 static char *http_timestring(char *time_buff,int buff_size, time_t t)
193 {
194     struct tm *tm = localtime(&t);
195     strftime(time_buff, buff_size, "%a, %d %b %Y %H:%M:%S %Z", tm);
196     return time_buff;
197 }
198 
199 
http_decode_base64(char * s)200 static void http_decode_base64(char *s)
201 {
202     char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
203     int bit_o, byte_o, idx, i, n;
204     unsigned char *d = (unsigned char *)s;
205     char *p;
206 
207     n = i = 0;
208 
209     while (*s && (p = strchr(b64, *s)))
210     {
211         idx = (int)(p - b64);
212         byte_o = (i*6)/8;
213         bit_o = (i*6)%8;
214         d[byte_o] &= ~((1<<(8-bit_o))-1);
215         if (bit_o < 3)
216         {
217             d[byte_o] |= (idx << (2-bit_o));
218             n = byte_o+1;
219         }
220         else
221         {
222             d[byte_o] |= (idx >> (bit_o-2));
223             d[byte_o+1] = 0;
224             d[byte_o+1] |= (idx << (8-(bit_o-2))) & 0xFF;
225             n = byte_o+2;
226         }
227             s++; i++;
228     }
229     /* null terminate */
230     d[n] = 0;
231 }
232 
233 
http_unescape(char * buffer)234 static char *http_unescape(char *buffer)
235 {
236     char *pointer = buffer;
237 
238     while ( (pointer = strchr(pointer,'+')) )
239         *pointer = ' ';
240 
241     pointer = buffer;
242 
243     while (pointer && *pointer && (pointer = strchr(pointer,'%')))
244     {
245         int highnibble = pointer[1];
246         int lownibble = pointer[2];
247 
248         if (highnibble >= '0' && highnibble <= '9')
249             highnibble = highnibble - '0';
250         else if (highnibble >= 'A' && highnibble <= 'F')
251             highnibble = 10 + highnibble - 'A';
252         else if (highnibble >= 'a' && highnibble <= 'f')
253             highnibble = 10 + highnibble - 'a';
254         else
255         {
256             pointer++;
257             continue;
258         }
259 
260         if (lownibble >= '0' && lownibble <= '9')
261             lownibble = lownibble - '0';
262         else if (lownibble >= 'A' && lownibble <= 'F')
263             lownibble = 10 + lownibble - 'A';
264         else if (lownibble >= 'a' && lownibble <= 'f')
265             lownibble = 10 + lownibble - 'a';
266         else
267         {
268             pointer++;
269             continue;
270         }
271 
272         *pointer = (highnibble<<4) | lownibble;
273 
274         memmove(pointer+1, pointer+3, strlen(pointer+3)+1);
275 
276         pointer++;
277     }
278 
279     return buffer;
280 }
281 
282 
http_interpret_variable_string(WEBBLK * webblk,char * qstring,int type)283 static void http_interpret_variable_string(WEBBLK *webblk, char *qstring, int type)
284 {
285 char *name;
286 char *value;
287 char *strtok_str;
288 CGIVAR **cgivar;
289 
290     for (cgivar = &(webblk->cgivar);
291         *cgivar;
292          cgivar = &((*cgivar)->next));
293 
294     for (name = strtok_r(qstring,"&; ",&strtok_str);
295          name;
296          name = strtok_r(NULL,"&; ",&strtok_str))
297     {
298         if(!(value = strchr(name,'=')))
299             continue;
300 
301         *value++ = '\0';
302 
303         (*cgivar) = malloc(sizeof(CGIVAR));
304         (*cgivar)->next = NULL;
305         (*cgivar)->name = strdup(http_unescape(name));
306         (*cgivar)->value = strdup(http_unescape(value));
307         (*cgivar)->type = type;
308         cgivar = &((*cgivar)->next);
309     }
310 }
311 
312 
313 #if 0
314 static void http_dump_cgi_variables(WEBBLK *webblk)
315 {
316     CGIVAR *cv;
317     for(cv = webblk->cgivar; cv; cv = cv->next)
318         logmsg(_("HHCHT012I cgi_var_dump: pointer(%p) name(%s) value(%s) type(%d)\n"),
319           cv, cv->name, cv->value, cv->type);
320 }
321 #endif
322 
323 
http_variable(WEBBLK * webblk,char * name,int type)324 DLL_EXPORT char *http_variable(WEBBLK *webblk, char *name, int type)
325 {
326     CGIVAR *cv;
327     for(cv = webblk->cgivar; cv; cv = cv->next)
328         if((cv->type & type) && !strcmp(name,cv->name))
329             return cv->value;
330     return NULL;
331 }
332 
333 
http_verify_path(WEBBLK * webblk,char * path)334 static void http_verify_path(WEBBLK *webblk, char *path)
335 {
336     char resolved_path[HTTP_PATH_LENGTH];
337 #if 0
338     int i;
339 
340     for (i = 0; path[i]; i++)
341         if (!isalnum((int)path[i]) && !strchr("/.-_", path[i]))
342             http_error(webblk, "404 File Not Found","",
343                                "Illegal character in filename");
344 #endif
345 
346     if (!realpath( path, resolved_path ))
347         http_error(webblk, "404 File Not Found","",
348                            "Invalid pathname");
349 
350     // The following verifies the specified file does not lie
351     // outside the specified httproot (Note: sysblk.httproot
352     // was previously resolved to an absolute path by config.c)
353 
354     if (strncmp( sysblk.httproot, resolved_path, strlen(sysblk.httproot)))
355         http_error(webblk, "404 File Not Found","",
356                            "Invalid pathname");
357 }
358 
359 
http_authenticate(WEBBLK * webblk,char * type,char * userpass)360 static int http_authenticate(WEBBLK *webblk, char *type, char *userpass)
361 {
362     char *pointer ,*user, *passwd;
363 
364     if (!strcasecmp(type,"Basic"))
365     {
366         if(userpass)
367         {
368             http_decode_base64(userpass);
369 
370             /* the format is now user:password */
371             if ((pointer = strchr(userpass,':')))
372             {
373                 *pointer = 0;
374                 user = userpass;
375                 passwd = pointer+1;
376 
377                 /* Hardcoded userid and password in configuration file */
378                 if(sysblk.httpuser && sysblk.httppass)
379                 {
380                     if(!strcmp(user,sysblk.httpuser)
381                       && !strcmp(passwd,sysblk.httppass))
382                     {
383                         webblk->user = strdup(user);
384                         return TRUE;
385                     }
386                 }
387 #if !defined(WIN32)
388                 else
389                 {
390                     struct passwd *pass = NULL;
391 
392                     /* unix userid and password check, the userid
393                        must be the same as that hercules is
394                        currently running under */
395 // ZZ INCOMPLETE
396 // ZZ No password check is being performed yet...
397                     if((pass = getpwnam(user))
398                       &&
399                        (pass->pw_uid == 0
400                           || pass->pw_uid == getuid()))
401                     {
402                         webblk->user = strdup(user);
403                         return TRUE;
404                     }
405                 }
406 #endif /*!defined(WIN32)*/
407             }
408         }
409     }
410 
411     webblk->user = NULL;
412 
413     return FALSE;
414 }
415 
416 
http_download(WEBBLK * webblk,char * filename)417 static void http_download(WEBBLK *webblk, char *filename)
418 {
419     char buffer[HTTP_PATH_LENGTH];
420     char tbuf[80];
421     int fd, length;
422     char *filetype;
423     char fullname[HTTP_PATH_LENGTH];
424     struct stat st;
425     MIMETAB *mime_type = mime_types;
426 
427     strlcpy( fullname, sysblk.httproot, sizeof(fullname) );
428     strlcat( fullname, filename,        sizeof(fullname) );
429 
430     http_verify_path(webblk,fullname);
431 
432     if(stat(fullname,&st))
433         http_error(webblk, "404 File Not Found","",
434                            strerror(errno));
435 
436     if(!S_ISREG(st.st_mode))
437         http_error(webblk, "404 File Not Found","",
438                            "The requested file is not a regular file");
439 
440     fd = hopen(fullname,O_RDONLY|O_BINARY,0);
441     if (fd == -1)
442         http_error(webblk, "404 File Not Found","",
443                            strerror(errno));
444 
445     hprintf(webblk->sock,"HTTP/1.0 200 OK\n");
446     if ((filetype = strrchr(filename,'.')))
447         for(mime_type++;mime_type->suffix
448           && strcasecmp(mime_type->suffix,filetype + 1);
449           mime_type++);
450     if(mime_type->type)
451         hprintf(webblk->sock,"Content-Type: %s\n", mime_type->type);
452 
453     hprintf(webblk->sock,"Expires: %s\n",
454       http_timestring(tbuf,sizeof(tbuf),time(NULL)+HTML_STATIC_EXPIRY_TIME));
455 
456     hprintf(webblk->sock,"Content-Length: %d\n\n", (int)st.st_size);
457     while ((length = read(fd, buffer, sizeof(buffer))) > 0)
458             hwrite(webblk->sock,buffer, length);
459     close(fd);
460     http_exit(webblk);
461 }
462 
463 
http_request(int sock)464 static void *http_request(int sock)
465 {
466     WEBBLK *webblk;
467     int authok = !sysblk.httpauth;
468     char line[HTTP_PATH_LENGTH];
469     char *url = NULL;
470     char *pointer;
471     char *strtok_str;
472     CGITAB *cgient;
473     int content_length = 0;
474 
475     if(!(webblk = malloc(sizeof(WEBBLK))))
476         http_exit(webblk);
477 
478     memset(webblk,0,sizeof(WEBBLK));
479     webblk->sock = sock;
480 
481     while (hgets(line, sizeof(line), webblk->sock))
482     {
483         if (*line == '\r' || *line == '\n')
484             break;
485 
486         if((pointer = strtok_r(line," \t\r\n",&strtok_str)))
487         {
488             if(!strcasecmp(pointer,"GET"))
489             {
490                 if((pointer = strtok_r(NULL," \t\r\n",&strtok_str)))
491                 {
492                     webblk->request_type = REQTYPE_GET;
493                     url = strdup(pointer);
494                 }
495             }
496             else
497             if(!strcasecmp(pointer,"POST"))
498             {
499                 if((pointer = strtok_r(NULL," \t\r\n",&strtok_str)))
500                 {
501                     webblk->request_type = REQTYPE_POST;
502                     url = strdup(pointer);
503                 }
504             }
505             else
506             if(!strcasecmp(pointer,"PUT"))
507             {
508                 http_error(webblk,"400 Bad Request", "",
509                                   "This server does not accept PUT requests");
510             }
511             else
512             if(!strcasecmp(pointer,"Authorization:"))
513             {
514                 if((pointer = strtok_r(NULL," \t\r\n",&strtok_str)))
515                     authok = http_authenticate(webblk,pointer,
516                                   strtok_r(NULL," \t\r\n",&strtok_str));
517             }
518             else
519             if(!strcasecmp(pointer,"Cookie:"))
520             {
521                 if((pointer = strtok_r(NULL,"\r\n",&strtok_str)))
522                     http_interpret_variable_string(webblk, pointer, VARTYPE_COOKIE);
523             }
524             else
525             if(!strcasecmp(pointer,"Content-Length:"))
526             {
527                 if((pointer = strtok_r(NULL," \t\r\n",&strtok_str)))
528                     content_length = atoi(pointer);
529             }
530         }
531     }
532     webblk->request = url;
533 
534     if(webblk->request_type == REQTYPE_POST
535       && content_length != 0)
536     {
537     char *post_arg;
538         if((pointer = post_arg = malloc(content_length + 1)))
539         {
540         int i;
541             for(i = 0; i < content_length; i++)
542             {
543                 *pointer = hgetc(webblk->sock);
544                 if(*pointer != '\n' && *pointer != '\r')
545                     pointer++;
546             }
547             *pointer = '\0';
548             http_interpret_variable_string(webblk, post_arg, VARTYPE_POST);
549             free(post_arg);
550         }
551     }
552 
553     if (!authok)
554         http_error(webblk, "401 Authorization Required",
555                            "WWW-Authenticate: Basic realm=\"HERCULES\"\n",
556                            "You must be authenticated to use this service");
557 
558     if (!url)
559         http_error(webblk,"400 Bad Request", "",
560                           "You must specify a GET or POST request");
561 
562     /* anything following a ? in the URL is part of the get arguments */
563     if ((pointer=strchr(url,'?'))) {
564         *pointer++ = 0;
565         http_interpret_variable_string(webblk, pointer, VARTYPE_GET);
566     }
567 
568     while(url[0] == '/' && url[1] == '/')
569         url++;
570 
571     webblk->baseurl = url;
572 
573     if(!strcasecmp("/",url))
574         url = HTTP_WELCOME;
575 
576     if(strncasecmp("/cgi-bin/",url,9))
577         http_download(webblk,url);
578     else
579         url += 9;
580 
581     while(*url == '/')
582         url++;
583 
584 #if 0
585     http_dump_cgi_variables(webblk);
586 #endif
587 
588     for(cgient = cgidir; cgient->path; cgient++)
589     {
590         if(!strcmp(cgient->path, url))
591         {
592         char tbuf[80];
593             hprintf(webblk->sock,"HTTP/1.0 200 OK\nConnection: close\n");
594             hprintf(webblk->sock,"Date: %s\n",
595               http_timestring(tbuf,sizeof(tbuf),time(NULL)));
596             (cgient->cgibin) (webblk);
597             http_exit(webblk);
598         }
599     }
600 
601 #if defined(OPTION_DYNAMIC_LOAD)
602     {
603     zz_cgibin dyncgi;
604 
605         if( (dyncgi = HDL_FINDSYM(webblk->baseurl)) )
606         {
607         char tbuf[80];
608             hprintf(webblk->sock,"HTTP/1.0 200 OK\nConnection: close\n");
609             hprintf(webblk->sock,"Date: %s\n",
610               http_timestring(tbuf,sizeof(tbuf),time(NULL)));
611             dyncgi(webblk);
612             http_exit(webblk);
613         }
614     }
615 #endif /*defined(OPTION_DYNAMIC_LOAD)*/
616 
617     http_error(webblk, "404 File Not Found","",
618                        "The requested file was not found");
619 
620     return NULL;
621 }
622 
623 
http_server(void * arg)624 void *http_server (void *arg)
625 {
626 int                     rc;             /* Return code               */
627 int                     lsock;          /* Socket for listening      */
628 int                     csock;          /* Socket for conversation   */
629 struct sockaddr_in      server;         /* Server address structure  */
630 fd_set                  selset;         /* Read bit map for select   */
631 int                     optval;         /* Argument for setsockopt   */
632 TID                     httptid;        /* Negotiation thread id     */
633 
634     UNREFERENCED(arg);
635 
636     /* Display thread started message on control panel */
637     logmsg (_("HHCHT001I HTTP listener thread started: "
638             "tid="TIDPAT", pid=%d\n"),
639             thread_id(), getpid());
640 
641 
642     /* If the HTTP root directory is not specified,
643        use a reasonable default */
644     if (!sysblk.httproot)
645         {
646 #if defined(_MSVC_)
647         char process_dir[HTTP_PATH_LENGTH];
648         if (get_process_directory(process_dir,HTTP_PATH_LENGTH) > 0)
649         {
650             strlcat(process_dir,"\\html",HTTP_PATH_LENGTH);
651             sysblk.httproot = strdup(process_dir);
652         }
653         else
654 #endif /*defined(WIN32)*/
655         sysblk.httproot = strdup(HTTP_ROOT);
656     }
657 
658     /* Convert the specified HTTPROOT value to an absolute path
659        ending with a '/' and save in sysblk.httproot. */
660     {
661         char absolute_httproot_path[HTTP_PATH_LENGTH];
662         int  rc;
663 #if defined(_MSVC_)
664         /* Expand any embedded %var% environ vars */
665         rc = expand_environ_vars( sysblk.httproot, absolute_httproot_path,
666             sizeof(absolute_httproot_path) );
667         if (rc == 0)
668         {
669             free(sysblk.httproot);
670             sysblk.httproot = strdup(absolute_httproot_path);
671         }
672 #endif /* defined(_MSVC_) */
673         /* Convert to absolute path */
674         if (!realpath(sysblk.httproot,absolute_httproot_path))
675         {
676             logmsg( _("HHCCF066E Invalid HTTPROOT: \"%s\": %s\n"),
677                    sysblk.httproot, strerror(errno));
678             return NULL;
679         }
680         /* Verify that the absolute path is valid */
681         // mode: 0 = exist only, 2 = write, 4 = read, 6 = read/write
682         // rc: 0 = success, -1 = error (errno = cause)
683         // ENOENT = File name or path not found.
684         if (access( absolute_httproot_path, R_OK ) != 0)
685         {
686             logmsg( _("HHCCF066E Invalid HTTPROOT: \"%s\": %s\n"),
687                    absolute_httproot_path, strerror(errno));
688             return NULL;
689         }
690         /* Append trailing [back]slash, but only if needed */
691         rc = strlen(absolute_httproot_path);
692         if (absolute_httproot_path[rc-1] != *HTTP_PS)
693             strlcat(absolute_httproot_path,HTTP_PS,sizeof(absolute_httproot_path));
694         /* Save the absolute path */
695         free(sysblk.httproot);
696         sysblk.httproot = strdup(absolute_httproot_path);
697         logmsg(_("HHCHT013I Using HTTPROOT directory \"%s\"\n"),sysblk.httproot);
698     }
699 
700     /* Obtain a socket */
701     lsock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
702 
703     if (lsock < 0)
704     {
705         logmsg(_("HHCHT002E socket: %s\n"), strerror(HSO_errno));
706         return NULL;
707     }
708 
709     /* Allow previous instance of socket to be reused */
710     optval = 1;
711     setsockopt (lsock, SOL_SOCKET, SO_REUSEADDR,
712                 (void*)&optval, sizeof(optval));
713 
714     /* Prepare the sockaddr structure for the bind */
715     memset (&server, 0, sizeof(server));
716     server.sin_family = AF_INET;
717     server.sin_addr.s_addr = INADDR_ANY;
718     server.sin_port = sysblk.httpport;
719     server.sin_port = htons(server.sin_port);
720 
721     /* Attempt to bind the socket to the port */
722     while (TRUE)
723     {
724         rc = bind (lsock, (struct sockaddr *)&server, sizeof(server));
725 
726         if (rc == 0 || HSO_errno != HSO_EADDRINUSE) break;
727 
728         logmsg (_("HHCHT003W Waiting for port %u to become free\n"),
729                 sysblk.httpport);
730         SLEEP(10);
731     } /* end while */
732 
733     if (rc != 0)
734     {
735         logmsg(_("HHCHT004E bind: %s\n"), strerror(HSO_errno));
736         return NULL;
737     }
738 
739     /* Put the socket into listening state */
740     rc = listen (lsock, 32);
741 
742     if (rc < 0)
743     {
744         logmsg(_("HHCHT005E listen: %s\n"), strerror(HSO_errno));
745         return NULL;
746     }
747 
748     logmsg(_("HHCHT006I Waiting for HTTP requests on port %u\n"),
749             sysblk.httpport);
750 
751     /* Handle http requests */
752     while (sysblk.httpport) {
753 
754         /* Initialize the select parameters */
755         FD_ZERO (&selset);
756         FD_SET (lsock, &selset);
757 
758         /* Wait for a file descriptor to become ready */
759         rc = select ( lsock+1, &selset, NULL, NULL, NULL );
760 
761         if (rc == 0) continue;
762 
763         if (rc < 0 )
764         {
765             if (HSO_errno == HSO_EINTR) continue;
766             logmsg(_("HHCHT007E select: %s\n"), strerror(HSO_errno));
767             break;
768         }
769 
770         /* If a http request has arrived then accept it */
771         if (FD_ISSET(lsock, &selset))
772         {
773             /* Accept the connection and create conversation socket */
774             csock = accept (lsock, NULL, NULL);
775 
776             if (csock < 0)
777             {
778                 logmsg(_("HHCHT008E accept: %s\n"), strerror(HSO_errno));
779                 continue;
780             }
781 
782             /* Create a thread to execute the http request */
783             if ( create_thread (&httptid, DETACHED,
784                                 http_request, (void *)(uintptr_t)csock,
785                                 "http_request")
786                )
787             {
788                 logmsg(_("HHCHT010E http_request create_thread: %s\n"),
789                         strerror(errno));
790                 close_socket (csock);
791             }
792 
793         } /* end if(lsock) */
794 
795     } /* end while */
796 
797     /* Close the listening socket */
798     close_socket (lsock);
799 
800     /* Display thread started message on control panel */
801     logmsg (_("HHCHT009I HTTP listener thread ended: "
802             "tid="TIDPAT", pid=%d\n"),
803             thread_id(), getpid());
804 
805     sysblk.httptid = 0;
806 
807     return NULL;
808 
809 } /* end function http_server */
810 
811 #endif /*defined(OPTION_HTTP_SERVER)*/
812