/* * mod_cgisock V 0.4.2 23/1/2000 * socket portions copyright Michael Voase under the APACHE license . * The software is supplied 'as-is' without warranty . * Refer to the LICENSE document supplied with this package for license * details. * * http_script: keeps all script-related ramblings together. * * Compliant to CGI/1.1 spec (Well , sort of ....) * * Adapted by rst from original NCSA code by Rob McCool * * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for * custom error responses, and DOCUMENT_ROOT because we found it useful. * It also adds SERVER_ADMIN - useful for scripts to know who to mail when * they fail. * * Version 0.3+ of the cgisock contains contributions by Pete Vassoff * pfv@grex.org ( header length additions ). * * Note: The previous array header info has been replaced with a single * fixed width header size. This makes for faster serving. If you have * started using the array header stuff, then set the ARRAY_COUNT define * just below to return the 0.3 behaviour. This will settle down as time * goes on.... */ #undef ARRAY_COUNT #ifndef CORE_PRIVATE #define CORE_PRIVATE #endif #undef CGISOCK_LOG #define CGISOCK_MAGIC_TYPE "interface/x-httpd-cgisock" #ifndef ARRAY_COUNT #define CGISOCK_VERSION "CGISOCK=0.4" #else #define CGISOCK_VERSION "CGISOCK=0.3" /* Backward compatibility to try and stop people */ #endif /* from getting burnt too badly by these changes */ #include "httpd.h" #include "http_config.h" #include "http_request.h" #include "http_core.h" #include "http_protocol.h" #include "http_main.h" #include "http_log.h" #include "util_script.h" #include "http_conf_globals.h" #include "ap_config.h" #include module MODULE_VAR_EXPORT cgisock_module; /* Configuration stuff */ #define DEFAULT_TRANSLATE_TABLE_SIZE 20 #define DEFAULT_LOGBYTES 1038576 #define DEFAULT_BUFBYTES 8192 /* Note - script writers, change the next value if cgisock keeps mercilessly timing out your script. I could turn this into a config option but the possibility exists for a malicious DoS attack if the value is set to zero. */ #define CGISOCK_DEFAULT_TIMEOUT 300 /* This is a KLUDGE . For some reason even though the defines below are defined in earlier includes , gcc still seems to throw a fit if you build the module on its own .... hence this KLUDGE . */ #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 #endif #ifndef sockaddr_un struct sockaddr_un { unsigned short sun_family; char sun_path[UNIX_PATH_MAX]; }; #endif typedef struct { char *logname; long logbytes; int bufbytes; } cgisock_server_conf; typedef struct { char *dirname; char *anchor; int timeout; #ifdef CGISOCK_LOG char *debuglog; #endif } cgisock_dir_conf; static void *create_server_config(pool *p, server_rec *s) { cgisock_server_conf *c = (cgisock_server_conf *) ap_pcalloc(p, sizeof(cgisock_server_conf)); c->logname = NULL; c->logbytes = DEFAULT_LOGBYTES; c->bufbytes = DEFAULT_BUFBYTES; return c; } static void *merge_server_config(pool *p, void *basev, void *overridesv) { cgisock_server_conf *a = (cgisock_server_conf *) ap_pcalloc(p, sizeof(cgisock_server_conf)); cgisock_server_conf *base = (cgisock_server_conf *) basev, *overrides = (cgisock_server_conf *) overridesv; a->logname ? overrides->logname : base->logname ; return a; } static void *create_dir_config(pool *p , char *dummy ) { cgisock_dir_conf *a = (cgisock_dir_conf *) ap_palloc( p , sizeof(cgisock_dir_conf )); a->anchor = NULL; a->dirname = NULL; a->timeout = CGISOCK_DEFAULT_TIMEOUT; return a; } static void *merge_dir_config(pool *p , void *basev , void *addv ) { cgisock_dir_conf *base = (cgisock_dir_conf *) basev; cgisock_dir_conf *newv = (cgisock_dir_conf *) addv; cgisock_dir_conf *a = (cgisock_dir_conf *) ap_palloc( p , sizeof(cgisock_dir_conf)); a->anchor = newv->anchor ? newv->anchor : base->anchor; a->dirname = newv->dirname ? newv->dirname : base->dirname; if ( newv->timeout > base->timeout ) a->timeout = newv->timeout; else a->timeout = base->timeout; #ifdef CGISOCK_LOG a->debuglog = newv->debuglog ? newv->debuglog : base->debuglog; #endif return a; } static const char *set_socketlog(cmd_parms *cmd, void *dummy, char *arg) { server_rec *s = cmd->server; cgisock_server_conf *conf = (cgisock_server_conf *) ap_get_module_config(s->module_config, &cgisock_module); conf->logname = arg; return NULL; } static const char *set_socketlog_length(cmd_parms *cmd, void *dummy, char *arg) { server_rec *s = cmd->server; cgisock_server_conf *conf = (cgisock_server_conf *) ap_get_module_config(s->module_config, &cgisock_module); conf->logbytes = atol(arg); return NULL; } static const char *set_socketlog_buffer(cmd_parms *cmd, void *dummy, char *arg) { server_rec *s = cmd->server; cgisock_server_conf *conf = (cgisock_server_conf *) ap_get_module_config(s->module_config, &cgisock_module); conf->bufbytes = atoi(arg); return NULL; } static const char *add_socket_path(cmd_parms *cmd, cgisock_dir_conf *c ,char *real_path ) { c->dirname = real_path; return NULL; } static const char *add_socket_anchor(cmd_parms *cmd, cgisock_dir_conf *c ,char *real_path ) { c->anchor = real_path; return NULL; } static const char *add_socket_timeout(cmd_parms *cmd, cgisock_dir_conf *c ,char *timeout ) { char *t,n; t = timeout; while(t) { if (!isdigit(*t)) return NULL; t++; }; c->timeout = atoi(timeout); return NULL; } #ifdef CGISOCK_LOG static const char *add_debug_log(cmd_parms *cmd, cgisock_dir_conf *d ,char *deblog ) { d->debuglog = deblog ; return NULL; } #endif static const command_rec cgisock_cmds[] = { {"CgisockLog", set_socketlog, NULL, RSRC_CONF, TAKE1, "the name of a log for socket debugging info"}, {"CgisockLogLength", set_socketlog_length, NULL, RSRC_CONF, TAKE1, "the maximum length (in bytes) of the socket debug log"}, {"CgisockLogBuffer", set_socketlog_buffer, NULL, RSRC_CONF, TAKE1, "the maximum size (in bytes) to record of a POST request"}, {"CgisockPath", add_socket_path ,NULL , ACCESS_CONF , TAKE1 , "the translate path to the real location of your socket"} , {"CgisockAnchor", add_socket_anchor ,NULL , ACCESS_CONF , TAKE1 , "the fixed real location of your socket"} , {"CgisockTimeout", add_socket_timeout ,NULL , ACCESS_CONF , TAKE1 , "the timeout before cgisock gives up on a slow socket"} , #ifdef CGISOCK_LOG {"CgisockDebugLog",add_debug_log , NULL , ACCESS_CONF , TAKE1 , "the per directory debug log path for user error messages"}, #endif {NULL} }; static int log_socketerror(request_rec *r, cgisock_server_conf *conf, int ret, int show_errno, char *error) { FILE *f; struct stat finfo; ap_log_error(APLOG_MARK, show_errno|APLOG_ERR, r->server, "%s: %s", error, r->filename); if (!conf->logname || ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) && (finfo.st_size > conf->logbytes)) || ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname), "a")) == NULL)) { return ret; } /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, r->args ? "?" : "", r->args ? r->args : "", r->protocol); /* "%% 500 /usr/local/apache/cgi-bin */ fprintf(f, "%%%% %d %s\n", ret, r->filename); fprintf(f, "%%cgisock error\n%s\n", error); ap_pfclose(r->pool, f); return ret; } static int cgi_select ( int sock_rd, int sock_wr, int secs, int usecs ) { fd_set sock_set_rd, sock_set_wr; fd_set *rd = NULL, *wr = NULL; struct timeval tv; if ( sock_rd > 0 ) { FD_ZERO(&sock_set_rd); FD_SET( sock_rd,&sock_set_rd ); rd = &sock_set_rd; }; if ( sock_wr > 0 ) { FD_ZERO(&sock_set_wr); FD_SET( sock_wr,&sock_set_wr ); rd = &sock_set_wr; }; tv.tv_sec= secs; tv.tv_usec = usecs; return ap_select((sock_rd > sock_wr) ? sock_rd+1 : sock_wr+1, rd, wr, NULL, &tv); } #ifdef CGISOCK_LOG static int debuglog_cgisock(request_rec *r, cgisock_dir_conf *dirconf, cgisock_server_conf *conf , char *message, char *stuff) { FILE *f; struct stat finfo; if (dirconf->debuglog == NULL) return; if ((stat(dirconf->debuglog , &finfo) == 0) && (finfo.st_size > conf->logbytes)) return; if ((f = ap_pfopen(r->pool, dirconf->debuglog,"a")) == NULL) return; /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, r->args ? "?" : "", r->args ? r->args : "", r->protocol); /* "%% 500 /usr/local/apache/cgi-bin */ fprintf(f, "%s %s %s\n",r->filename,message, stuff); ap_pfclose(r->pool, f); return; } static int log_socket(request_rec *r, cgisock_server_conf * conf , cgisock_dir_conf * dirconf, int ret, char *dbuf, const char *sbuf, BUFF *socket_in) { array_header *hdrs_arr = ap_table_elts(r->headers_in); table_entry *hdrs = (table_entry *) hdrs_arr->elts; char argsbuffer[HUGE_STRING_LEN]; FILE *f; int i; struct stat finfo; if (!dirconf->debuglog || ((stat(dirconf->debuglog, &finfo) == 0) && (finfo.st_size > conf->logbytes)) || ((f = ap_pfopen(r->pool, dirconf->debuglog,"a")) == NULL)) { /* Soak up socket output */ while (ap_bgets(argsbuffer, HUGE_STRING_LEN, socket_in) > 0) continue; return ret; } /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ fprintf(f, "*** Begin Log Socket ***\n%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,r->args ? "?" : "", r->args ? r->args : "", r->protocol); /* "%% 500 /usr/local/apache/cgi-bin" */ fprintf(f, "%%%% %d %s\n", ret, r->filename); fputs("%request\n", f); for (i = 0; i < hdrs_arr->nelts; ++i) { if (!hdrs[i].key) continue; fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); } if ((r->method_number == M_POST || r->method_number == M_PUT) && *dbuf) { fprintf(f, "\n%s\n", dbuf); } fputs("%response\n", f); hdrs_arr = ap_table_elts(r->err_headers_out); hdrs = (table_entry *) hdrs_arr->elts; for (i = 0; i < hdrs_arr->nelts; ++i) { if (!hdrs[i].key) continue; fprintf(f, "Headers :\n%s: %s\n", hdrs[i].key, hdrs[i].val); } if (sbuf && *sbuf) fprintf(f, "Read buff :\n%s\n", sbuf); fputs("Reading output from socket\n",f); if ((cgi_select(socket_in->fd_in, 0 , d_conf->timeout, 0) <= 0)) { fputs("Blocked read from socket\n *** Premature end Log Socket *** \n",f); ap_bclose(socket_in); ap_pfclose(r->pool, f); return ret; } if (ap_bgets(argsbuffer, HUGE_STRING_LEN, socket_in) > 0) { fputs("%stdout\n", f); fputs(argsbuffer, f); while (ap_bgets(argsbuffer, HUGE_STRING_LEN, socket_in) > 0) fputs(argsbuffer, f); fputs("\n*** End Log Socket *** \n", f); } ap_bclose(socket_in); ap_pfclose(r->pool, f); return ret; } #endif /***************************************************************************** ***************************************************************************** * * Actual CGISOCK handling... * ***************************************************************************** ***************************************************************************** */ static int cgisock_handler(request_rec *r) { int retval, nph, dbpos , cgi_sock , res_con , i = 0, j, k; int dbsize, len_read ,pfv_discount; fd_set rd_set, wr_set; struct timeval tv; char *argv0, *dbuf = NULL, argsbuffer[HUGE_STRING_LEN]; char **env, *name_constr,*tmp; BUFF *socket_buff, *cgis_client; int is_included = !strcmp(r->protocol, "INCLUDED"); void *sconf = r->server->module_config, (*handler) (int); struct sockaddr_un cgi_addr; struct stat filinfo; cgisock_server_conf *conf = (cgisock_server_conf *) ap_get_module_config(sconf, &cgisock_module); core_dir_config *core_path = (core_dir_config *) ap_get_module_config(r->per_dir_config , &core_module); cgisock_dir_conf *d_conf = (cgisock_dir_conf *) ap_get_module_config(r->per_dir_config , &cgisock_module); if (!(d_conf->dirname || d_conf->anchor)) return log_socketerror( r, conf , NOT_FOUND , APLOG_NOERRNO , "Please set a CgisockPath directory before using CGISOCK"); if (r->method_number == M_OPTIONS) { /* 99 out of 100 CGI scripts, this is all they support */ r->allowed |= (1 << M_GET); r->allowed |= (1 << M_POST); return DECLINED; } if (argv0 = strrchr(r->filename, '/')) argv0++; else argv0 = r->filename; nph = !(strncmp(argv0, "nph-", 4)); if (nph && is_included) return log_socketerror(r, conf, FORBIDDEN, APLOG_NOERRNO, "attempt to include NPH CGI script"); if (retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)) return retval; #ifdef CHARSET_EBCDIC /* XXX:@@@ Is the generated/included output ALWAYS in text/ebcdic format? */ /* Or must we check the Content-Type first? */ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); #endif /*CHARSET_EBCDIC*/ ap_add_common_vars(r); ap_add_cgi_vars(r); dbuf = ap_pcalloc(r->pool , conf->bufbytes+1 ); socket_buff = ap_bcreate( r->pool , B_RDWR | B_SOCKET ); name_constr = ap_palloc( r->pool , DEFAULT_BUFBYTES ); /* Before we start grabbing sockets etc , lets do some sanity checks * so that we know exactly what we are looking at ... */ *name_constr = 0; i= strlen(core_path->d); if ( d_conf->anchor ) strcpy( name_constr, d_conf->anchor ); else { tmp = r->uri + i; strcpy(name_constr , d_conf->dirname); strcat(name_constr , tmp); }; #ifdef CGISOCK_LOG debuglog_cgisock( r , d_conf , conf , "core dir config" , core_path->d); debuglog_cgisock( r , d_conf , conf , "URI" , r->uri); debuglog_cgisock( r , d_conf , conf , "Translated URI" , name_constr); #endif if (strncmp( r->uri , core_path->d , i) != 0) { return log_socketerror (r ,conf,NOT_FOUND,APLOG_NOERRNO, "URI and config path dont match . This may be a bug"); } stat( name_constr , &filinfo ); if (!S_ISSOCK(filinfo.st_mode)) return log_socketerror ( r , conf , NOT_FOUND ,APLOG_NOERRNO, "CgisockPath and URI does not translate to a socket"); cgi_sock = ap_psocket( r-> pool , AF_UNIX , SOCK_STREAM , PF_UNIX); if ( cgi_sock <= 0 ) return log_socketerror (r,conf,NOT_FOUND,APLOG_NOERRNO, "cannot allocate a socket"); ap_note_cleanups_for_socket( r->pool , cgi_sock ); ap_bpushfd( socket_buff , cgi_sock , cgi_sock ); cgi_addr.sun_family = AF_UNIX; strcpy(cgi_addr.sun_path , name_constr); ap_hard_timeout("CGISOCK connect", r); i = sizeof(cgi_addr); res_con = connect( cgi_sock , (struct sockaddr *)&cgi_addr , i ); ap_kill_timeout( r ); if ( res_con ) { ap_log_error(APLOG_MARK,APLOG_ERR,r->server, "Connect fail path:%s, fd:%d, len:%d",cgi_addr.sun_path,cgi_sock,i); return log_socketerror(r,conf,SERVER_ERROR,APLOG_ERR, "Cannot connect to socket"); } /* To help with future version support , if demand requires it , cgisock can * be extended wrt what information it can give to the script on the other end * and what actions it can take . The 'ACCEPTED' response can be substituted * for other parameters ( say 'REQUEST_REC' whatever ) to produce other * actions without comprimising the basic function of cgisock ( to provide * a gateway over a socket ) . Although these functions are not implemented * yet , they will remain quietly in the backround until *someone* needs * needs them . To ensure backward compatability with the cgisock module * this version string will always be supplied first so that scripts can * *quickly* decide what action they want to take next depending on what * this cgisock module is capable of ... * * OK, Here is where we need to stop pretending the version is an env-var * and send out up to 8192 characters - in a LINE - of "prelude". * Question is "What should the server learn right now?" pfv. * Of course, one of things I wouldnt mind knowing is the length of * line itself... mhv */ env = ap_create_environment ( r->pool , r->subprocess_env); *argsbuffer = 0; pfv_discount = 0; k = 0; for ( j = 1, i = 0 ; env[i] ; ++i ) { char *pp; pp = env[i]; while (*pp) if( strrchr("\r\n",*pp)) *(pp++) = 0; else pp++; if ( env[i][0] && env[i][0]>32 ) { #ifdef ARRAY_COUNT sprintf(argsbuffer+strlen(argsbuffer), "%d,", strlen(env[i])+1 ); #else j += strlen(env[i]) + 1; #endif /* ARRAY_COUNT */ } #ifdef ARRAY_COUNT else pfv_discount++; #endif }; #ifdef ARRAY_COUNT *(argsbuffer+strlen(argsbuffer)-1) = ';'; #else if ( j > 99999999 ) return log_socketerror ( r , conf,HTTP_BAD_GATEWAY, APLOG_NOERRNO , "Environment args too long" ); #endif ap_hard_timeout("Writing env to socket ",r); #ifdef ARRAY_COUNT ap_bprintf(socket_buff, "%s;%d:%s\n", CGISOCK_VERSION, i-pfv_discount, argsbuffer); #else ap_bprintf(socket_buff, "%s;%08u;\n", CGISOCK_VERSION,j); #endif for ( i = 0 ; env[i] ; ++i ) { if ( env[i][0] && env[i][0]>32) ap_bprintf( socket_buff, "%s\n", env[i]); } #ifndef ARRAY_COUNT ap_bprintf( socket_buff,"\n"); #endif ap_bflush(socket_buff); ap_kill_timeout(r); *name_constr = 0; res_con = cgi_select(cgi_sock, 0, d_conf->timeout, 0); if (res_con <= 0) { ap_log_error(APLOG_MARK,APLOG_ERR,r->server, "Timeout:%d",d_conf->timeout); return log_socketerror ( r , conf,HTTP_REQUEST_TIME_OUT, APLOG_NOERRNO , "Timeout on cgisock"); } len_read = ap_bread( socket_buff , dbuf , HUGE_STRING_LEN); #ifdef CGISOCK_LOG debuglog_cgisock( r , d_conf , conf , "Socket reply" , dbuf); #endif /* Sorry about the huge if statement, this code really needs a cleanup... * This should have a real command parser rather than a quick strncmp... */ if (!(strncmp(dbuf , "DECLINED" , 8))) return DECLINED; /***************************************************************************** ***************************************************************************** * * Accepted.. * */ if (!(strncmp (dbuf ,"ACCEPTED", 8 ))) { len_read = 0 ; dbpos = 0 ; ap_bflush(socket_buff); /* Transfer any put/post args, CERN style... * Note that if a buggy script fails to read everything we throw * at it, or a buggy client sends too much, we get a SIGPIPE, so * we have to ignore SIGPIPE while doing this. CERN does the same * (and in fact, they pretty nearly guarantee themselves a SIGPIPE * on every invocation by chasing the real client data with a * spurious newline). */ ap_hard_timeout("copy socket args", r); #ifdef SIGPIPE handler = signal(SIGPIPE, SIG_IGN); #endif if (ap_should_client_block(r)) { if (conf->logname) { dbpos = 0; } len_read =0; while ((len_read = ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) { if (conf->logname) { if ((dbpos + len_read) > conf->bufbytes) { dbsize = conf->bufbytes - dbpos; } else { dbsize = len_read; } memcpy(dbuf + dbpos, argsbuffer, dbsize); dbpos += dbsize; } ap_reset_timeout(r); if (ap_bwrite(socket_buff, argsbuffer, len_read) < len_read) { /* script stopped reading, soak up remaining message */ while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) { /* read the rest and forget about it */ } break; } } } else { len_read = ap_bwrite(socket_buff,"CGISOCK: no client block\0",25); if (len_read<=0) { ap_kill_timeout(r); #ifdef SIGPIPE signal(SIGPIPE,handler); #endif return log_socketerror( r , conf , HTTP_CONFLICT , APLOG_NOERRNO , "CGI server must read the client block"); } } ap_bflush(socket_buff); ap_kill_timeout(r); #ifdef SIGPIPE signal(SIGPIPE, handler); #endif #ifdef CGISOCK_LOG debuglog_cgisock( r , d_conf , conf , "So far so good" , "reading body"); #endif /* Handle script return... */ if (socket_buff && !nph) { const char *location; char sbuf[MAX_STRING_LEN]; int ret; res_con = cgi_select(cgi_sock, 0, d_conf->timeout , 0 ); if ( res_con <= 0 ) { #ifdef CGISOCK_LOG debuglog_cgisock( r , d_conf , conf , "Timeout on header" , sbuf); log_socket(r, conf , d_conf , ret, dbuf, sbuf, socket_buff); #endif return log_socketerror ( r ,conf , HTTP_REQUEST_TIME_OUT , APLOG_NOERRNO , "Timeout on header"); } if ((ret = ap_scan_script_header_err_buff(r, socket_buff, sbuf))) { #ifdef CGISOCK_LOG debuglog_cgisock( r , d_conf , conf , "Failed header" , sbuf); log_socket(r, conf , d_conf , ret, dbuf, sbuf, socket_buff); #endif return log_socketerror ( r ,conf , HTTP_BAD_GATEWAY , APLOG_NOERRNO , "Malformed header return by socket"); } #ifdef CHARSET_EBCDIC /* Now check the Content-Type to decide if conversion is needed */ ap_checkconv(r); #endif /*CHARSET_EBCDIC*/ location = ap_table_get(r->headers_out, "Location"); if (location && location[0] == '/' && r->status == 200) { /* Soak up all the script output */ res_con = cgi_select(cgi_sock, 0, d_conf->timeout, 0 ); if (res_con < 0) return log_socketerror ( r ,conf , HTTP_REQUEST_TIME_OUT , APLOG_NOERRNO , "Request body timeout"); ap_hard_timeout("read from socket", r); while (ap_bgets(argsbuffer, HUGE_STRING_LEN, socket_buff) > 0) { continue; } ap_kill_timeout(r); /* This redirect needs to be a GET no matter what the original * method was. */ r->method = ap_pstrdup(r->pool, "GET"); r->method_number = M_GET; /* We already read the message body (if any), so don't allow * the redirected request to think it has one. We can ignore * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. */ ap_table_unset(r->headers_in, "Content-Length"); ap_internal_redirect_handler(location, r); return OK ; } else if (location && r->status == 200) { /* XX Note that if a script wants to produce its own Redirect * body, it now has to explicitly *say* "Status: 302" */ return REDIRECT; } ap_send_http_header(r); if (!r->header_only) { ap_send_fb(socket_buff, r); } ap_bclose(socket_buff); } if (socket_buff && nph) { ap_send_fb(socket_buff, r); } return OK; /* NOT r->status, even if it has changed. */ } /* if ACCEPTED */ /***************************************************************************** ***************************************************************************** * * Stream mode * */ if (!(strncmp (dbuf ,"STREAM", 6 ))) { /* We jump straight to streaming mode with no possibility for redirects. * You can redirect too a streamer interface, but not from a server declaring * itself as a streamer interface then deciding to redirect or do something * else that doesnt fit with this model. The intent here is to provide a * facility to handle nice things like VRML event interfaces....Possibly * Dynamic HTML if it ever eventuates one day...This mode will run until * either side closes its end. Note this facility is experimental, you * have been warned. */ tmp = dbuf + 6; if ( !strncmp( tmp, "_OUT", 4)) { cgis_client = socket_buff; socket_buff = r->connection->client; log_socketerror( r , conf , HTTP_OK, APLOG_INFO ,"Stream out selected"); } else { cgis_client= r->connection->client; log_socketerror( r , conf , HTTP_OK, APLOG_INFO ,"Stream (in) selected"); }; #ifdef SIPIPE handler = signal( SIGPIPE, SIG_IGN); #endif while(1) { FD_ZERO( &rd_set ); FD_SET(cgis_client->fd, &rd_set); FD_SET(socket_buff->fd, &rd_set); tv.tv_usec = 0; tv.tv_sec = d_conf->timeout; i = ( cgis_client->fd > socket_buff->fd ) ? cgis_client->fd + 1: socket_buff->fd + 1; if (!select(i, &rd_set, NULL, NULL, &tv )) break; if (FD_ISSET( socket_buff->fd, &rd_set)) { ap_log_error(APLOG_MARK, APLOG_INFO, r->server, "Caught bump off from receiver"); break; }; if ( B_EOF & cgis_client->flags ) { ap_log_error(APLOG_MARK, APLOG_INFO, r->server, "Stream read error: %d", cgis_client->flags); break; }; if ( B_EOUT & socket_buff->flags ) { ap_log_error(APLOG_MARK,APLOG_INFO, r->server, "Stream write error: %d", cgis_client->flags); break; }; len_read = ap_bread( cgis_client, argsbuffer, DEFAULT_BUFBYTES); i = 0; while ((i = ap_bwrite( socket_buff, argsbuffer, len_read)) < len_read ) { /* Do a select then try again */ if ( i < 0 ) break; len_read -= i; if ( !i && (cgi_select( 0, socket_buff->fd,d_conf->timeout,0)< 1)) break; }; /* rigth, now flush it. Unix buffering is out to get you...*/ ap_bflush( socket_buff ); if ( len_read - i > 0 ) { ap_log_error(APLOG_MARK, APLOG_INFO, r->server, "Stream write done"); break; }; }; #ifdef SIPIPE signal( SIGPIPE, handler); #endif log_socketerror( r , conf , HTTP_OK, APLOG_INFO ,"Stream complete"); return OK; }; /* Additional methods can be added here if required */ return log_socketerror( r , conf , NOT_ACCEPTABLE , APLOG_NOERRNO , "Socket refused connection or version conflict"); } /* cgisock handler */ static const handler_rec cgisock_handlers[] = { { CGISOCK_MAGIC_TYPE , cgisock_handler}, {"cgi-socket", cgisock_handler}, {NULL} }; module MODULE_VAR_EXPORT cgisock_module = { STANDARD_MODULE_STUFF, NULL, /* initializer */ create_dir_config, /* dir config creater */ merge_dir_config, /* dir merger --- default is to override */ create_server_config, /* server config */ merge_server_config, /* merge server config */ cgisock_cmds, /* command table */ cgisock_handlers, /* handlers */ NULL, /* filename translation */ NULL, /* check_user_id */ NULL, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* fixups */ NULL, /* logger */ NULL, /* header parser */ NULL, /* child_init */ NULL, /* child_exit */ NULL /* post read-request */ };