-1

I take sample.c of kcgi add sqlite3 and put what's in sendindex() into sqlite_exec() callback with adding some stuff.

struct kreq req is passed to callback. Dunno if khtmlreq is good here but looks like yes.

/*  $Id$ */
/*
 * Copyright (c) 2014, 2015, 2017 Kristaps Dzonsons <kristaps@bsd.lv>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <sys/types.h> /* size_t, ssize_t */
#include <stdarg.h> /* va_list */
#include <stddef.h> /* NULL */
#include <stdint.h> /* int64_t */
#include <stdlib.h>
#include <string.h> /* memset */
#include <stdio.h>
#include <sqlite3.h>
#include <kcgi.h>
#include <kcgihtml.h>

/*
 * Simple CGI application.
 * Compile it with `make samples` (or using gmake) and install it into
 * your web server's /cgi-bin.
 * The "template.xml" file should be in the /cgi-bin directory as well
 * and readable by the server process.
 * (Obviously this is just for a sample.)
 *
 * Assuming localhost/cgi-bin, the script is localhost/cgi-bin/sample.
 * The pages recognised are:
 *
 *   - /cgi-bin/sample/index.html
 *   - /cgi-bin/sample/template.html
 *   - /cgi-bin/sample/senddata.html
 *
 * See the sendindex et al. functions for what these do.
 */

/* Recognised page requests.  See pages[]. */
enum    page {
    PAGE_INDEX,
    PAGE_TEMPLATE,
    PAGE_SENDDATA,
    PAGE__MAX
};

/*
 * All of the keys (input field names) we accept. 
 * The key names are in the "keys" array.
 * See sendindex() for how these are used.
 */
enum    key {
    KEY_INTEGER, 
    KEY_FILE,
    KEY_PAGECOUNT,
    KEY_PAGESIZE,
    KEY__MAX
};

/*
 * The elements in our template file.
 * The element key names are in the "templs" array.
 * See sendtemplate() for how this is used.
 */
enum    templ {
    TEMPL_TITLE,
    TEMPL_NAME,
    TEMPL_REMOTE_ADDR,
    TEMPL__MAX
};

/*
 * We need a structure because we can't get the "r" from the request.
 * This is used by our template callback.
 */
struct  tstrct {
    struct khtmlreq  req;
    struct kreq *r;
};

/*
 * We'll use this to route pages by creating an array indexed by our
 * page.
 * Then when the page is parsed, we'll route directly into it.
 */
typedef void (*disp)(struct kreq *);

static void senddata(struct kreq *);
static void sendindex(struct kreq *);
static void sendtemplate(struct kreq *);

static const disp disps[PAGE__MAX] = {
    sendindex, /* PAGE_INDEX */
    sendtemplate, /* PAGE_TEMPLATE */
    senddata, /* PAGE_SENDDATA */
};

static const struct kvalid keys[KEY__MAX] = {
    { kvalid_int, "integer" }, /* KEY_INTEGER */
    { NULL, "file" }, /* KEY_FILE */
    { kvalid_uint, "count" }, /* KEY_PAGECOUNT */
    { kvalid_uint, "size" }, /* KEY_PAGESIZE */
};

/*
 * Template key names (as in @@TITLE@@ in the file).
 */
static const char *const templs[TEMPL__MAX] = {
    "title", /* TEMPL_TITLE */
    "name", /* TEMPL_NAME */
    "remote_addr", /* TEMPL_REMOTE_ADDR */
};

/* 
 * Page names (as in the URL component) mapped from the first name part
 * of requests, e.g., /sample.cgi/index.html -> index -> PAGE_INDEX.
 */
static const char *const pages[PAGE__MAX] = {
    "index", /* PAGE_INDEX */
    "template", /* PAGE_TEMPLATE */
    "senddata" /* PAGE_SENDDATA */
};

/*
 * Open an HTTP response with a status code and a particular
 * content-type, then open the HTTP content body.
 * You can call khttp_head(3) before this: CGI doesn't dictate any
 * particular header order.
 */
static void
resp_open(struct kreq *req, enum khttp http)
{
    enum kmime   mime;

    /*
     * If we've been sent an unknown suffix like '.foo', we won't
     * know what it is.
     * Default to an octet-stream response.
     */
    if (KMIME__MAX == (mime = req->mime))
        mime = KMIME_APP_OCTET_STREAM;

    khttp_head(req, kresps[KRESP_STATUS], 
        "%s", khttps[http]);
    khttp_head(req, kresps[KRESP_CONTENT_TYPE], 
        "%s", kmimetypes[mime]);
    khttp_body(req);
}

/*
 * Callback for filling in a particular template part.
 * Let's just be simple for simplicity's sake.
 */
static int
template(size_t key, void *arg)
{
    struct tstrct   *p = arg;

    switch (key) {
    case (TEMPL_TITLE):
        khtml_puts(&p->req, "title");
        break;
    case (TEMPL_NAME):
        khtml_puts(&p->req, "name");
        break;
    case (TEMPL_REMOTE_ADDR):
        khtml_puts(&p->req, p->r->remote);
        break;
    default:
        return(0);
    }

    return(1);
}

/*
 * Demonstrates how to use templates.
 * Returns HTTP 200 and the template content.
 */
static void
sendtemplate(struct kreq *req)
{
    struct ktemplate t;
    struct tstrct    p;

    memset(&t, 0, sizeof(struct ktemplate));
    memset(&p, 0, sizeof(struct tstrct));

    p.r = req;
    t.key = templs;
    t.keysz = TEMPL__MAX;
    t.arg = &p;
    t.cb = template;

    resp_open(req, KHTTP_200);
    khtml_open(&p.req, req, 0);
    khttp_template(req, &t, "template.xml");
    khtml_close(&p.req);
}

/*
 * Send a random amount of data.
 * Requires KEY_PAGECOUNT (optional), KEY_PAGESIZE (optional).
 * Page count is the number of times we flush a page (with the given
 * size) to the wire.
 * Returns HTTP 200 and the random data.
 */
static void
senddata(struct kreq *req)
{
    int64_t   i, j, nm, sz;
    char     *buf;

    nm = 1024 * 1024;
    if (NULL != req->fieldmap[KEY_PAGECOUNT])
        nm = req->fieldmap[KEY_PAGECOUNT]->parsed.i;
    if (0 == nm)
        nm = 1;

    sz = 1;
    if (NULL != req->fieldmap[KEY_PAGESIZE])
        sz = req->fieldmap[KEY_PAGESIZE]->parsed.i;
    if (0 == sz || (uint64_t)sz > SIZE_MAX)
        sz = 1;

    buf = kmalloc(sz);

    resp_open(req, KHTTP_200);
    for (i = 0; i < nm; i++) {
        for (j = 0; j < sz; j++)
#ifndef __linux__
            buf[j] = 65 + arc4random_uniform(24);
#else
            buf[j] = 65 + (random() % 24);
#endif
        khttp_write(req, buf, sz);
    }

    free(buf);
}

/*
 * Demonstrates how to use GET and POST forms and building with the HTML
 * builder functions.
 * Returns HTTP 200 and HTML content.
 */

Here is the callback function including what was in sendindex() :

static int callback(void *ptr, int argc, char **argv, char **azColName) {
    struct kreq *req = (struct kreq *)ptr;

        char            *page;
        struct khtmlreq  r;
        const char      *cp;

        cp = NULL == req->fieldmap[KEY_INTEGER] ?
                "" : req->fieldmap[KEY_INTEGER]->val;
        kasprintf(&page, "%s/%s", req->pname, pages[PAGE_INDEX]);

        resp_open(req, KHTTP_200);
        khtml_open(&r, req, 0);
        khtml_elem(&r, KELEM_DOCTYPE);
        khtml_elem(&r, KELEM_HTML);
        khtml_elem(&r, KELEM_HEAD);
        khtml_elem(&r, KELEM_TITLE);
        khtml_puts(&r, "Welcome!");
        khtml_closeelem(&r, 2);
        khtml_elem(&r, KELEM_BODY);
        khtml_puts(&r, "Welcome!");
        khtml_attr(&r, KELEM_FORM,
                KATTR_METHOD, "post",
                KATTR_ENCTYPE, "multipart/form-data",
                KATTR_ACTION, page,
                KATTR__MAX);
        khtml_elem(&r, KELEM_FIELDSET);
        khtml_elem(&r, KELEM_LEGEND);
        khtml_puts(&r, "Post (multipart)");
        khtml_closeelem(&r, 1);
        khtml_elem(&r, KELEM_P);
        khtml_attr(&r, KELEM_INPUT,
                KATTR_TYPE, "number",
                KATTR_NAME, keys[KEY_INTEGER].name,
                KATTR_VALUE, cp, KATTR__MAX);
        khtml_closeelem(&r, 1);
        khtml_elem(&r, KELEM_P);
        khtml_attr(&r, KELEM_INPUT,
                KATTR_TYPE, "file",
                KATTR_MULTIPLE, "",
                KATTR_NAME, keys[KEY_FILE].name,
                KATTR__MAX);
        if (NULL != req->fieldmap[KEY_FILE]) {
                if (NULL != req->fieldmap[KEY_FILE]->file) {
                        khtml_puts(&r, "file: ");
                        khtml_puts(&r, req->fieldmap[KEY_FILE]->file);
                        khtml_puts(&r, " ");
                }
                if (NULL != req->fieldmap[KEY_FILE]->ctype) {
                        khtml_puts(&r, "ctype: ");
                        khtml_puts(&r, req->fieldmap[KEY_FILE]->ctype);
                }
        }
  int i;
  for(i=0; i<argc; i++){
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    khtml_puts(&r, "OK");
  }

        khtml_closeelem(&r, 1);
        khtml_elem(&r, KELEM_P);
        khtml_attr(&r, KELEM_INPUT,
                KATTR_TYPE, "submit",
                KATTR__MAX);
        khtml_closeelem(&r, 0);
        khtml_close(&r);
        free(page);

}

Here is sendindex() function :

static void
sendindex(struct kreq *req)
{
    sqlite3 *db;
        char *zErrMsg = 0;
        int rc;
        rc = sqlite3_open("/var/www/MaSSH/databases/massh.db", &db);
        if(rc){
                sqlite3_close(db);
        }
        rc = sqlite3_exec(db, "SELECT * FROM hosts;", callback, &req, &zErrMsg);
        if(rc!= SQLITE_OK){
                sqlite3_free(zErrMsg);
        }
        sqlite3_close(db);
}

int
main(void)
{
    struct kreq  r;
    enum kcgi_err    er;

    /* Set up our main HTTP context. */

    er = khttp_parse(&r, keys, KEY__MAX, 
        pages, PAGE__MAX, PAGE_INDEX);

    if (KCGI_OK != er)
        return(EXIT_FAILURE);

    /* 
     * Accept only GET, POST, and OPTIONS.
     * Restrict to text/html and a valid page.
     * If all of our parameters are valid, use a dispatch array to
     * send us to the page handlers.
     */

    if (KMETHOD_OPTIONS == r.method) {
        khttp_head(&r, kresps[KRESP_ALLOW], 
            "OPTIONS GET POST");
        resp_open(&r, KHTTP_200);
    } else if (KMETHOD_GET != r.method && 
           KMETHOD_POST != r.method) {
        resp_open(&r, KHTTP_405);
    } else if (PAGE__MAX == r.page || 
           KMIME_TEXT_HTML != r.mime) {
        resp_open(&r, KHTTP_404);
    } else
        (*disps[r.page])(&r);

    khttp_free(&r);
    return(EXIT_SUCCESS);
}

1 Answers1

1

If you run your code through a debugger you will see

Program received signal SIGSEGV, Segmentation fault.
0x00000b60dbbd86b6 in callback (ptr=0x7f7ffffeb878, argc=1, argv=0xb6317623210,azColName=0xb6317623208) at /tmp/so.c:262
262             cp = NULL == req->fieldmap[KEY_INTEGER] ?

(gdb) bt
#0  0x0000040c108fc6b6 in callback () from /home/dsp/a.out
#1  0x0000040e744f172a in sqlite3_exec () from /usr/local/lib/libsqlite3.so.37.7
#2  0x0000040c108fcb79 in sendindex () from /home/dsp/a.out
#3  0x0000040c108fccbe in main () from /home/dsp/a.out

that tells that that req is probably not holding what you think it's holding. We see from looking at the source that the callback function does:

static int callback(void *ptr, int argc, char **argv, char **azColName) {
    struct kreq *req = (struct kreq *)ptr;

so the void * passed to it is not what it should be. we see from the backtrace that this is passed from the sqlite3_exec function.

It seems it requires the pointer to the request to be the 4th argument. A google search turns out this SO answer that you probably received yesterday from Chris Loonam and he correctly says that in setindex your code should pass the pointer to the req as the 4th argument like so

rc = sqlite3_exec(db, "SELECT * FROM hosts;", callback, req, &zErrMsg);

since that function takes req as

static void sendindex(struct kreq *req)

in your code you are passing it in sqlite3_exec as &req therefore it ends up being a pointer to a pointer of a request and the casting in the callback of course ends up leading to an invalid dereference.

ramrunner
  • 1,362
  • 10
  • 20