#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static int somaxconn;
static int
socket_get_backlog(int sock, int *backlogp, const char *testclass,
const char *test, const char *testfunc)
{
socklen_t len;
int i;
len = sizeof(i);
if (getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, &len) < 0) {
warn("%s: %s: %s: socket_get_backlog: getsockopt("
"SOL_SOCKET, SO_LISTENQLIMIT)", testclass, test,
testfunc);
return (-1);
}
if (len != sizeof(i)) {
warnx("%s: %s: %s: socket_get_backlog: getsockopt("
"SOL_SOCKET, SO_LISTENQLIMIT): returned size %d",
testclass, test, testfunc, len);
return (-1);
}
*backlogp = i;
return (0);
}
static int
socket_listen(int domain, int type, int protocol, int backlog,
int create_backlog_assertion, int listen_backlog_assertion, int *sockp,
const char *domainstring, const char *typestring, const char *testclass,
const char *test)
{
int backlog_retrieved, sock;
sock = socket(domain, type, protocol);
if (sock < 0) {
warn("%s: %s: socket_listen: socket(%s, %s)", testclass,
test, domainstring, typestring);
close(sock);
return (-1);
}
if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
"socket_listen") < 0) {
close(sock);
return (-1);
}
if (backlog_retrieved != create_backlog_assertion) {
warnx("%s: %s: socket_listen: create backlog is %d not %d",
testclass, test, backlog_retrieved,
create_backlog_assertion);
close(sock);
return (-1);
}
if (listen(sock, backlog) < 0) {
warn("%s: %s: socket_listen: listen(, %d)", testclass, test,
backlog);
close(sock);
return (-1);
}
if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
"socket_listen") < 0) {
close(sock);
return (-1);
}
if (backlog_retrieved != listen_backlog_assertion) {
warnx("%s: %s: socket_listen: listen backlog is %d not %d",
testclass, test, backlog_retrieved,
listen_backlog_assertion);
close(sock);
return (-1);
}
*sockp = sock;
return (0);
}
static void
test_defaults(void)
{
int sock;
if (socket_listen(PF_INET, SOCK_STREAM, 0, 0, 0, 0, &sock, "PF_INET",
"SOCK_STREAM", "test_defaults", "default_0_listen_0") < 0)
exit(-1);
close(sock);
if (socket_listen(PF_INET, SOCK_STREAM, 0, -1, 0, somaxconn, &sock,
"PF_INET", "SOCK_STREAM", "test_defaults", "default_0_listen_-1")
< 0)
exit(-1);
close(sock);
if (socket_listen(PF_INET, SOCK_STREAM, 0, 1, 0, 1, &sock, "PF_INET",
"SOCK_STREAM", "test_defaults", "default_0_listen_1") < 0)
exit(-1);
close(sock);
if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn, 0, somaxconn,
&sock, "PF_INET", "SOCK_STREAM", "test_defaults",
"default_0_listen_somaxconn") < 0)
exit(-1);
close(sock);
if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn+1, 0, somaxconn,
&sock, "PF_INET", "SOCK_STREAM", "test_defaults",
"default_0_listen_somaxconn+1") < 0)
exit(-1);
close(sock);
}
static int
socket_listen_update(int domain __unused, int type __unused,
int protocol __unused, int backlog,
int update_backlog, int listen_backlog_assertion,
int update_backlog_assertion, int *sockp, const char *domainstring,
const char *typestring, const char *testclass, const char *test)
{
int backlog_retrieved, sock;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
warn("%s: %s: socket_listen_update: socket(%s, %s)",
testclass, test, domainstring, typestring);
return (-1);
}
if (listen(sock, backlog) < 0) {
warn("%s: %s: socket_listen_update: initial listen(, %d)",
testclass, test, backlog);
close(sock);
return (-1);
}
if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
"socket_listen_update") < 0) {
close(sock);
return (-1);
}
if (backlog_retrieved != listen_backlog_assertion) {
warnx("%s: %s: socket_listen_update: initial backlog is %d "
"not %d", testclass, test, backlog_retrieved,
listen_backlog_assertion);
close(sock);
return (-1);
}
if (listen(sock, update_backlog) < 0) {
warn("%s: %s: socket_listen_update: update listen(, %d)",
testclass, test, update_backlog);
close(sock);
return (-1);
}
if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
"socket_listen_update") < 0) {
close(sock);
return (-1);
}
if (backlog_retrieved != update_backlog_assertion) {
warnx("%s: %s: socket_listen_update: updated backlog is %d "
"not %d", testclass, test, backlog_retrieved,
update_backlog_assertion);
close(sock);
return (-1);
}
*sockp = sock;
return (0);
}
static void
test_listen_update(void)
{
int sock;
if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, -1, 5, somaxconn,
&sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
"update_5,-1") < 0)
exit(-1);
close(sock);
if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 0, 5, 0, &sock,
"PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,0")
< 0)
exit(-1);
close(sock);
if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 1, 5, 1, &sock,
"PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,1")
< 0)
exit(-1);
close(sock);
if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn, 5,
somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
"update_5,somaxconn") < 0)
exit(-1);
close(sock);
if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn+1, 5,
somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
"update_5,somaxconn+1") < 0)
exit(-1);
close(sock);
}
static void
test_set_qlimit(void)
{
int i, ret, sock;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0)
err(-1, "test_set_qlimit: socket(PF_INET, SOCK_STREAM)");
i = 0;
ret = setsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, sizeof(i));
if (ret < 0 && errno != ENOPROTOOPT) {
warn("test_set_qlimit: setsockopt(SOL_SOCKET, "
"SO_LISTENQLIMIT, 0): unexpected error");
close(sock);
}
if (ret == 0) {
warnx("test_set_qlimit: setsockopt(SOL_SOCKET, "
"SO_LISTENQLIMIT, 0) succeeded");
close(sock);
exit(-1);
}
close(sock);
}
int
main(void)
{
size_t len;
len = sizeof(somaxconn);
if (sysctlbyname("kern.ipc.soacceptqueue", &somaxconn, &len, NULL, 0)
< 0)
err(-1, "sysctlbyname(kern.ipc.soacceptqueue)");
test_defaults();
test_listen_update();
test_set_qlimit();
return (0);
}