Ejemplo n.º 1
0
static int init_ssl(void)
{
    char *server_key;
    ne_ssl_certificate *cert;
 
    /* take srcdir as argv[1]. */
    if (test_argc > 1) {
	server_key = ne_concat(test_argv[1], "/server.key", NULL);
    } else {
	server_key = ne_strdup("server.key");
    }

    ONN("sock_init failed", ne_sock_init());
    server_ctx = ne_ssl_context_create(1);
    ONN("SSL_CTX_new failed", server_ctx == NULL);

    ne_ssl_context_keypair(server_ctx, "server.cert", server_key);

    client_ctx = ne_ssl_context_create(0);
    ONN("SSL_CTX_new failed for client", client_ctx == NULL);
    
    cert = ne_ssl_cert_read("ca/cert.pem");
    ONN("could not load ca/cert.pem", cert == NULL);

    ne_ssl_context_trustcert(client_ctx, cert);
    ne_free(server_key);

    return OK;
}
Ejemplo n.º 2
0
static int owner_modify(void)
{
    char *tmp;
    ne_propname pname = { "http://webdav.org/neon/litmus/", "random" };
    ne_proppatch_operation pops[] = { 
	{ NULL, ne_propset, "foobar" },
	{ NULL }
    };
    PRECOND(gotlock);

    ONV(ne_put(i_session, res, i_foo_fd),
	("PUT on locked resource failed: %s", ne_get_error(i_session)));
    
    tmp = ne_concat(i_path, "whocares", NULL);
    ONN("COPY of locked resource", 
	ne_copy(i_session, 1, NE_DEPTH_ZERO, res, tmp) == NE_ERROR);
    
   if (STATUS(201))
	t_warning("COPY failed with %d not 201", GETSTATUS);
    
    ONN("DELETE of locked resource by owner", 
	ne_delete(i_session, tmp) == NE_ERROR);

    if (STATUS(204)) 
	t_warning("DELETE of %s failed with %d not 200", tmp, GETSTATUS);
    free(tmp);
    
    ONN("PROPPATCH of locked resource",
	ne_proppatch(i_session, res, pops) == NE_ERROR);
    
    if (STATUS(207))
	t_warning("PROPPATCH failed with %d", GETSTATUS);

    return OK;
}
Ejemplo n.º 3
0
static int notowner_lock(void)
{
    struct ne_lock dummy;

    PRECOND(gotlock);

    memcpy(&dummy, &reslock, sizeof(reslock));
    dummy.token = ne_strdup("opaquelocktoken:foobar");
    dummy.scope = ne_lockscope_exclusive;
    dummy.owner = ne_strdup("notowner lock");

    ONN("UNLOCK with bogus lock token",
	ne_unlock(i_session2, &dummy) != NE_ERROR);

    /* 2518 doesn't really say what status code that UNLOCK should
     * fail with. mod_dav gives a 400 as the locktoken is bogus.  */
    
    ONN("LOCK on locked resource",
	ne_lock(i_session2, &dummy) != NE_ERROR);
    
    if (dummy.token)  
        ne_free(dummy.token);

    if (STATUS2(423))
	t_warning("LOCK failed with %d not 423", GETSTATUS2);

    return OK;
}
Ejemplo n.º 4
0
static int cipher(void)
{
    ne_socket *sock;

#ifdef SOCKET_SSL
    char *ciph;

    CALL(begin(&sock, serve_cipher, NULL));

    ciph = ne_sock_cipher(sock);

    ONN("NULL/empty cipher", ciph == NULL || strlen(ciph) == 0);

    FULLREAD(ciph);
    
    ne_free(ciph);
    
#else
    CALL(begin(&sock, serve_cipher, NULL));

    ONN("non-NULL cipher for non-SSL socket", 
        ne_sock_cipher(sock) != NULL);

    FULLREAD("NULL");

#endif
    return finish(sock, 1);
}
Ejemplo n.º 5
0
static int addr_peer(void)
{
    ne_socket *sock = ne_sock_create();
    ne_inet_addr *ia, *ia2;
    unsigned int port = 9999, realport;
    int ret;

    ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
    ONN("ne_iaddr_make returned NULL", ia == NULL);
    
    CALL(new_spawn_server(1, serve_close, NULL, &realport));
    ONN("could not connect", ne_sock_connect(sock, ia, realport));

    ia2 = ne_sock_peer(sock, &port);
    ret = ne_iaddr_cmp(ia, ia2);
    ONV(ret != 0,
        ("comparison of peer with server address was %d", ret));

    ONV(port != realport, ("got peer port %u, expected %u", port, realport));
 
    ne_sock_close(sock);
    CALL(await_server());

    ne_iaddr_free(ia);
    ne_iaddr_free(ia2);
    return OK;
}
Ejemplo n.º 6
0
static int addr_reverse(void)
{
    ne_inet_addr *ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
    char buf[128], *syshost = NULL;

#ifdef HAVE_GETHOSTNAME
    char host[128];

    if (gethostname(host, sizeof host) == 0) {
        syshost = host;
    }
#endif

    ONN("ne_iaddr_make returned NULL", ia == NULL);

    ONN("reverse lookup for 127.0.0.1 failed",
        ne_iaddr_reverse(ia, buf, sizeof buf) != 0);

    ONV(!(strcmp(buf, "localhost.localdomain") == 0
          || strcmp(buf, "localhost") == 0
          || (syshost && strcmp(buf, syshost) == 0)),
        ("reverse lookup for 127.0.0.1 got %s", buf));

    ne_iaddr_free(ia);

    return OK;
}
Ejemplo n.º 7
0
static int to_end(ne_socket *sock)
{
    to_finish = time(NULL);
    reap_server(); /* hopefully it's hung. */
    ONN("timeout ignored, or very slow machine", to_finish - to_start > 3);
    ONN("close failed", ne_sock_close(sock));
    return OK;
}
Ejemplo n.º 8
0
static int expect100(void)
{
    ne_socket *sock = ne_sock_create();
    char req[BUFSIZ], buf[BUFSIZ];
    ne_status status = {0};
    const ne_inet_addr *ia;
    int success = 0;

    if (strcmp(ne_get_scheme(i_session), "https") == 0) {
        t_context("skipping for SSL server");
        return SKIP;
    }        

    for (ia = ne_addr_first(i_address); ia && !success; 
	 ia = ne_addr_next(i_address))
	success = ne_sock_connect(sock, ia, i_port) == 0;

    ONN("could not connect to server", !success);
    
    sprintf(req, 
	    "PUT %sexpect100 HTTP/1.1" EOL
	    "Host: %s" EOL
	    "Content-Length: 100" EOL
	    "Expect: 100-continue" EOL EOL,
	    i_path, ne_get_server_hostport(i_session));

    NE_DEBUG(NE_DBG_SOCKET, "Request:\n%s", req);

    ONS("sending request", ne_sock_fullwrite(sock, req, strlen(req)));

    switch (ne_sock_block(sock, 30)) {
    case NE_SOCK_TIMEOUT: 
	ONN("timeout waiting for interim response", FAIL);
	break;
    case 0:
	/* no error. */
	break;
    default:
	ONN("error reading from socket", FAIL);
	break;
    }

    ONS("reading status line", ne_sock_readline(sock, buf, BUFSIZ));

    NE_DEBUG(NE_DBG_HTTP, "[status] %s", buf);

    ONN("parse status line", ne_parse_statusline(buf, &status));

    if (status.code == 100) {
	char rbuf[100] = {0};
	
	ONN("write request body", ne_sock_fullwrite(sock, rbuf, 100));
    }

    ne_sock_close(sock);

    return OK;
}
Ejemplo n.º 9
0
static int addr_make_v6(void)
{
#ifdef TEST_IPV6   
    struct {
	const unsigned char *addr;
	const char *rep;
    } as[] = {
	{ raw6_cafe, "feed::cafe" },
	{ raw6_babe, "cafe:babe::" },
	{ raw6_nuls, "::" },
	{ NULL, NULL }
    };
    int n;
    
    for (n = 0; as[n].rep != NULL; n++) {
	ne_inet_addr *ia = ne_iaddr_make(ne_iaddr_ipv6, as[n].addr);
	char pr[128];
        unsigned char raw[17];

	ONV(ia == NULL, ("could not make address for '%s'", 
                         as[n].rep));

	ne_iaddr_print(ia, pr, sizeof pr);
	ONV(strcmp(pr, as[n].rep), 
	    ("address %d was '%s' not '%s'", n, pr, as[n].rep));
	
        ONN("bogus ne_iaddr_typeof return", ne_iaddr_typeof(ia) != ne_iaddr_ipv6);

        raw[16] = 'Z';
        ONN("ne_iaddr_raw gave bad retval", ne_iaddr_raw(ia, raw) != raw);
        ONN("raw address mismatch", memcmp(raw, as[n].addr, 4) != 0);
        ONN("ne_iaddr_raw buffer overflow", raw[16] != 'Z');
        
	ne_iaddr_free(ia);
        
        ia = ne_iaddr_parse(as[n].rep, ne_iaddr_ipv6);
        ONV(ia == NULL, ("ne_iaddr_parse failed for %s", as[n].rep));
        ONN("bogus ne_iaddr_typeof return", ne_iaddr_typeof(ia) != ne_iaddr_ipv6);
        ONN("ne_iaddr_raw gave bad retval", ne_iaddr_raw(ia, raw) != raw);
        ONN("raw address mismatch", memcmp(raw, as[n].addr, 4) != 0);
        ONN("ne_iaddr_raw buffer overflow", raw[16] != 'Z');

        ne_iaddr_free(ia);
   }

    return OK;
#else
    /* should fail when lacking IPv6 support. */
    ne_inet_addr *ia = ne_iaddr_make(ne_iaddr_ipv6, raw6_nuls);
    ONN("ne_iaddr_make did not return NULL", ia != NULL);
    ONN("ne_iaddr_parse did not return NULL", ne_iaddr_parse("127.0.0.1", ne_iaddr_ipv6));
#endif
    return OK;
}
Ejemplo n.º 10
0
/* Check the given inet addr is 127.0.0.1. */
static int check_is_raw127(const ne_inet_addr *ia)
{
    unsigned char raw[5];

    raw[4] = 'Z';
    ONN("bogus ne_iaddr_typeof return", ne_iaddr_typeof(ia) != ne_iaddr_ipv4);
    ONN("ne_iaddr_raw gave bad retval", ne_iaddr_raw(ia, raw) != raw);
    ONN("raw address mismatch", memcmp(raw, raw_127, 4) != 0);
    ONN("ne_iaddr_raw buffer overflow", raw[4] != 'Z');

    return OK;
}
Ejemplo n.º 11
0
/* make a sparse large file */
static int make_sparse_file(void)
{
    int fd = open64(SPARSE, O_CREAT | O_TRUNC | O_WRONLY, 0644);

    ONN("could not create large file " SPARSE, fd < 0);
    ONN("seek to point", lseek64(fd, point, SEEK_SET) != point);
    ONN("could not write to file", 
        write(fd, data, strlen(data)) != (ssize_t)strlen(data));
    ONN("close failed", close(fd));
    
    return OK;
}
Ejemplo n.º 12
0
/* echoes back lines. */
static int echo_server(ne_socket *sock, void *ud)
{
    ssize_t ret;

    while ((ret = ne_sock_readline(sock, buffer, sizeof(buffer))) > 0) {
	NE_DEBUG(NE_DBG_SOCKET, "Line: %s", buffer);
	ONN("write failed", ne_sock_fullwrite(sock, buffer, ret));
	NE_DEBUG(NE_DBG_SOCKET, "Wrote line.\n");
    }

    ONN("readline failed", ret != NE_SOCK_CLOSED);
    return 0;
}
Ejemplo n.º 13
0
static int resolve_numeric(void)
{
    ne_sock_addr *addr = ne_addr_resolve("127.0.0.1", 0);
    ONV(ne_addr_result(addr),
	("failed to resolve 127.0.0.1: %s",
	 ne_addr_error(addr, buffer, sizeof buffer)));
    ONN("ne_addr_first returned NULL", ne_addr_first(addr) == NULL);
    ONN("ne_iaddr_print didn't return buffer", 
	ne_iaddr_print(ne_addr_first(addr), buffer, sizeof buffer) != buffer);
    ONV(strcmp(buffer, "127.0.0.1"), ("ntop gave `%s' not 127.0.0.1", buffer));
    ne_addr_destroy(addr);
    return OK;
}
Ejemplo n.º 14
0
static int copy_overwrite(void)
{
    PRECOND(copy_ok);

    /* Do it again with Overwrite: F to check that fails. */
    ONN("COPY on existing resource with Overwrite: F should fail (RFC2518:S8.8.4)",
	ne_copy(i_session, 0, NE_DEPTH_INFINITE, src, dest) != NE_ERROR);

    if (STATUS(412)) {
	t_warning("COPY-on-existing fails with 412");
    }
    
    ONV(ne_copy(i_session, 1, NE_DEPTH_INFINITE, src, dest),
	("COPY-on-existing with 'Overwrite: T' should succeed (RFC2518:S8.8.4): %s", ne_get_error(i_session)));

    /* tricky one this, I didn't think it should work, but the spec
     * makes it look like it should. */
    ONV(ne_copy(i_session, 1, NE_DEPTH_INFINITE, src, coll),
	("COPY overwrites collection: %s", ne_get_error(i_session)));
    
    if (STATUS(204)) {
	t_warning("COPY to existing resource didn't give 204 (RFC2518:S8.8.5)");
    }

    return OK;
}
Ejemplo n.º 15
0
/* Run a request to 'path' and retrieve the redirect destination to
 * *redir. */
static int process_redir(ne_session *sess, const char *path,
                         const ne_uri **redir)
{
    ONN("did not get NE_REDIRECT", any_request(sess, path) != NE_REDIRECT);
    *redir = ne_redirect_location(sess);
    return OK;
}
Ejemplo n.º 16
0
static int printing(void)
{
    struct {
        const char *in, *out;
        size_t pass, ret;
    } ts[] = {
        { "alpha", "alpha", 10, 5 },
        { "alpha", "alph", 5, 4 },
        { "foobar", "", 1, 0 },
        { NULL, NULL, 0, 0}
    };
    size_t n;

    for (n = 0; ts[n].in; n++) {
        char buf[512];
        size_t ret;

        memset(buf, 'A', sizeof buf);

        ret = ne_snprintf(buf, ts[n].pass, "%s", ts[n].in);
        
        ONCMP(buf, ts[n].out);
        ONV(ret != ts[n].ret, 
            ("got return value %" NE_FMT_SIZE_T " not %" NE_FMT_SIZE_T,
             ret, ts[n].ret));

        /* byte past the NUL must still be 'A' */
        ONN("buffer over-ran!", buf[ret + 1] != 'A');
    }
    
    return OK;
}
Ejemplo n.º 17
0
static int serve_response(ne_socket *s, const char *response)
{
    CALL(discard_request(s));
    CALL(discard_body(s));
    ONN("failed to send response", SEND_STRING(s, response));
    return OK;
}    
Ejemplo n.º 18
0
/* Connect to an address crafted using ne_iaddr_make rather than from
 * the resolver. */
static int addr_connect(void)
{
    ne_socket *sock = ne_sock_create();
    ne_inet_addr *ia;

    ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
    ONN("ne_iaddr_make returned NULL", ia == NULL);

    CALL(spawn_server(7777, serve_close, NULL));
    ONN("could not connect", ne_sock_connect(sock, ia, 7777));
    ne_sock_close(sock);
    CALL(await_server());

    ne_iaddr_free(ia);
    return OK;
}
Ejemplo n.º 19
0
/* This runs as the child process. */
static int server_child(int readyfd, struct in_addr addr, int port,
			server_fn callback, void *userdata)
{
    ne_socket *s = ne_sock_create();
    int ret, listener;

    in_child();

    listener = do_listen(addr, port);
    if (listener < 0)
	return FAIL;

#ifdef USE_PIPE
    /* Tell the parent we're ready for the request. */
    if (write(readyfd, "a", 1) != 1) abort();
#endif

    ONN("accept failed", ne_sock_accept(s, listener));

    ret = callback(s, userdata);

    close_socket(s);

    return ret;
}
Ejemplo n.º 20
0
static int notowner_modify(void)
{
    char *tmp;
    ne_propname pname = { "http://webdav.org/neon/litmus/", "random" };
    ne_proppatch_operation pops[] = { 
	{ NULL, ne_propset, "foobar" },
	{ NULL }
    };

    PRECOND(gotlock);

    pops[0].name = &pname;

    ONN("DELETE of locked resource should fail", 
	ne_delete(i_session2, res) != NE_ERROR);

    if (STATUS2(423)) 
	t_warning("DELETE failed with %d not 423", GETSTATUS2);

    tmp = ne_concat(i_path, "whocares", NULL);
    ONN("MOVE of locked resource should fail", 
	ne_move(i_session2, 0, res, tmp) != NE_ERROR);
    free(tmp);
    
    if (STATUS2(423))
	t_warning("MOVE failed with %d not 423", GETSTATUS2);
    
    ONN("COPY onto locked resource should fail",
	ne_copy(i_session2, 1, NE_DEPTH_ZERO, res2, res) != NE_ERROR);

    if (STATUS2(423))
	t_warning("COPY failed with %d not 423", GETSTATUS2);

    ONN("PROPPATCH of locked resource should fail",
	ne_proppatch(i_session2, res, pops) != NE_ERROR);
    
    if (STATUS2(423))
	t_warning("PROPPATCH failed with %d not 423", GETSTATUS2);

    ONN("PUT on locked resource should fail",
	ne_put(i_session2, res, i_foo_fd) != NE_ERROR);

    if (STATUS2(423))
	t_warning("PUT failed with %d not 423", GETSTATUS2);

    return OK;    
}
Ejemplo n.º 21
0
static int addr_canonical(void)
{
    ne_sock_addr *sa;
    const char *h;

    sa = ne_addr_resolve("localhost", NE_ADDR_CANON);
    ONN("could not resolve localhost", sa == NULL);
    
    h = ne_addr_canonical(sa);
    ONN("no canonical name for localhost", h == NULL);

    NE_DEBUG(NE_DBG_SOCKET, "canonical name: %s\n", h);

    ne_addr_destroy(sa);
    
    return OK;
}
Ejemplo n.º 22
0
/* Test that just does a connect then a close (but gets the close via
 * ne_sock_peek). */
static int peek_close(void)
{
    ne_socket *sock;

    CALL(begin(&sock, serve_close, NULL));
    CALL(expect_peek_close(sock));    
    ONN("close failed", ne_sock_close(sock));
    return await_server();
}
Ejemplo n.º 23
0
int spawn_server_addr(int bind_local, int port, server_fn fn, void *ud)
{
    int fds[2];
    struct in_addr addr;

    addr = bind_local?lh_addr:hn_addr;

#ifdef USE_PIPE
    if (pipe(fds)) {
	perror("spawn_server: pipe");
	return FAIL;
    }
#else
    /* avoid using uninitialized variable. */
    fds[0] = fds[1] = 0;
#endif

    child = fork();

    ONN("fork server", child == -1);

    if (child == 0) {
	/* this is the child. */
	int ret;

	ret = server_child(fds[1], addr, port, fn, ud);

#ifdef USE_PIPE
	close(fds[0]);
	close(fds[1]);
#endif

	/* print the error out otherwise it gets lost. */
	if (ret) {
	    printf("server child failed: %s\n", test_context);
	}

	/* and quit the child. */
        NE_DEBUG(NE_DBG_HTTP, "child exiting with %d\n", ret);
	exit(ret);
    } else {
	char ch;

#ifdef USE_PIPE
	if (read(fds[0], &ch, 1) < 0)
	    perror("parent read");

	close(fds[0]);
	close(fds[1]);
#else
	minisleep();
#endif

	return OK;
    }
}
Ejemplo n.º 24
0
int serve_sstring(ne_socket *sock, void *ud)
{
    struct string *str = ud;

    NE_DEBUG(NE_DBG_SOCKET, "Serving string: [[[%.*s]]]\n",
	     (int)str->len, str->data);

    ONN("write failed", ne_sock_fullwrite(sock, str->data, str->len));
    
    return 0;
}
Ejemplo n.º 25
0
static int try_prebind(int addr, int port)
{
    ne_socket *sock = ne_sock_create();
    ne_inet_addr *ia;
    char buf[128], line[256];
    unsigned int srvport;

    ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
    ONN("ne_iaddr_make returned NULL", ia == NULL);
    
    CALL(new_spawn_server(1, serve_ppeer, NULL, &srvport));

    ne_sock_prebind(sock, addr ? ia : NULL, port ? 7778 : 0);

    ONN("could not connect", ne_sock_connect(sock, ia, srvport));

    ne_snprintf(line, sizeof line,
                "%s@%d\n", ne_iaddr_print(ia, buf, sizeof buf),
                7778);
    
    if (!port) {
        /* Don't know what port will be chosen, so... */
        ssize_t ret = ne_sock_readline(sock, buffer, BUFSIZ);
        
        ONV(ret < 0, ("socket error `%s'", ne_sock_error(sock)));

        ONV(strncmp(line, buffer, strchr(line, '@') - line) != 0,
            ("bad address: '%s', expecting '%s'",
             buffer, line));
    }
    else {
        LINE(line);
    }

    ne_sock_close(sock);
    CALL(await_server());

    ne_iaddr_free(ia);
    return OK;
}
Ejemplo n.º 26
0
static int parse_v4(void)
{
    ne_inet_addr *ia;

    ia = ne_iaddr_parse("127.0.0.1", ne_iaddr_ipv4);
    ONN("parse failed", ia == NULL);

    CALL(check_is_raw127(ia));

    ne_iaddr_free(ia);

    return OK;
}
Ejemplo n.º 27
0
static int str_errors(void)
{
    char expect[200], actual[200];
    
    strncpy(expect, strerror(ENOENT), sizeof(expect));
    ONN("ne_strerror did not return passed-in buffer",
	ne_strerror(ENOENT, actual, sizeof(actual)) != actual);
    
    ONV(strcmp(expect, actual), 
	("error from ENOENT was `%s' not `%s'", actual, expect));

    /* Test truncated error string is still NUL-terminated. */
    ne_strerror(ENOENT, actual, 6);
    NE_DEBUG(NE_DBG_HTTP, "error: %s\n", actual);
    ONN("truncated string had wrong length", strlen(actual) != 5);

    ne_strerror(-1, actual, 6);
    ONN("truncated string for bad error had wrong length", 
        strlen(actual) != 5);

    return OK;
}
Ejemplo n.º 28
0
static int addr_reverse(void)
{
    ne_inet_addr *ia = ne_iaddr_make(ne_iaddr_ipv4, raw_127);
    char buf[128], *syshost = NULL;
    int match;

#ifdef HAVE_GETHOSTNAME
    char host[128];

    if (gethostname(host, sizeof host) == 0) {
        syshost = host;
    }
#endif

    ONN("ne_iaddr_make returned NULL", ia == NULL);

    ONN("reverse lookup for 127.0.0.1 failed",
        ne_iaddr_reverse(ia, buf, sizeof buf) != 0);

    NE_DEBUG(NE_DBG_SOCKET, "Reverse lookup for 127.0.0.1 => %s\n", buf);

    match = strcmp(buf, "localhost.localdomain") == 0
        || strcmp(buf, "localhost") == 0;

    if (!match && syshost)
        /* If the returned name has the system hostname as a prefix, that's
         * good enough. */
        match = strncmp(buf, syshost, strlen(syshost)) == 0;

    if (!match)
        t_warning("reverse lookup for 127.0.0.1 got '%s'", buf);
    
    ONN("reverse lookup for 127.0.0.1 got empty string", strlen(buf) == 0);

    ne_iaddr_free(ia);

    return OK;
}
Ejemplo n.º 29
0
int double_serve_sstring(ne_socket *s, void *userdata)
{
    struct double_serve_args *args = userdata;
    struct string *str;

    CALL(discard_request(s));
    CALL(discard_body(s));
    
    str = &args->first;
    NE_DEBUG(NE_DBG_SOCKET, "Serving string: [[[%.*s]]]\n",
	     (int)str->len, str->data);
    ONN("write failed", ne_sock_fullwrite(s, str->data, str->len));

    CALL(discard_request(s));
    CALL(discard_body(s));

    str = &args->second;
    NE_DEBUG(NE_DBG_SOCKET, "Serving string: [[[%.*s]]]\n",
	     (int)str->len, str->data);
    ONN("write failed", ne_sock_fullwrite(s, str->data, str->len));

    return OK;
}
Ejemplo n.º 30
0
/* could implement failure on self-referential redirects, but
 * realistically, the application must implement a max-redirs count
 * check, so it's kind of redundant.  Mozilla takes this approach. */
static int fail_loop(void)
{
    ne_session *sess;

    CALL(make_session(&sess, serve_redir, "http://localhost:7777/foo/bar"));

    ne_redirect_register(sess);

    ONN("followed looping redirect",
	any_request(sess, "/foo/bar") != NE_ERROR);

    ne_session_destroy(sess);
    return OK;
}