blob: 6d921d25123c4c24ed1222d5f5c478a73eca326c [file] [edit]
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/mman.h>
#define SYS_SOCKETCALL_I386 102
#define SYS_SOCKET 1
#define SYS_CONNECT 3
#ifndef SOCK_FAMILY
#error "define SOCK_FAMILY via -DSOCK_FAMILY=..."
#endif
#ifndef SOCK_TYPE
#error "define SOCK_TYPE via -DSOCK_TYPE=..."
#endif
#ifdef VSOCK_CONNECT
#ifndef PORT
#error "define PORT via -DPORT=..."
#endif
#ifndef CLIENT_PAYLOAD
#error "define CLIENT_PAYLOAD via -DCLIENT_PAYLOAD=..."
#endif
#ifndef EXPECTED_SERVER_PAYLOAD
#error "define EXPECTED_SERVER_PAYLOAD via -DEXPECTED_SERVER_PAYLOAD=..."
#endif
#ifndef CONNECT_DENIED_EXIT_CODE
#error "define CONNECT_DENIED_EXIT_CODE via -DCONNECT_DENIED_EXIT_CODE=..."
#endif
static void timeout(int signal) {
(void)signal;
static const char message[] = "socketcall client timed out\n";
write(STDERR_FILENO, message, sizeof(message) - 1);
_exit(124);
}
static int write_full(int fd, const char *buf, size_t len) {
while (len > 0) {
ssize_t n = write(fd, buf, len);
if (n < 0 && errno == EINTR)
continue;
if (n <= 0) {
if (n == 0)
errno = EIO;
perror("write");
return -1;
}
buf += n;
len -= (size_t)n;
}
return 0;
}
static int read_full(int fd, char *buf, size_t len) {
while (len > 0) {
ssize_t n = read(fd, buf, len);
if (n < 0 && errno == EINTR)
continue;
if (n < 0) {
perror("read");
return -1;
}
if (n == 0) {
fprintf(stderr, "unexpected EOF while reading server payload\n");
return -1;
}
buf += n;
len -= (size_t)n;
}
return 0;
}
#endif
int main() {
/*
* The int $0x80 ia32 compat path truncates all registers to 32 bits.
* Every pointer used by socketcall must live below 4 GB, so keep its
* arguments and address in the MAP_32BIT allocation.
*/
unsigned char *mapping = mmap(NULL, 4096,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT,
-1, 0);
if (mapping == MAP_FAILED) {
perror("mmap");
return 2;
}
unsigned int *args = (unsigned int *)mapping;
args[0] = SOCK_FAMILY;
args[1] = SOCK_TYPE;
args[2] = 0;
int ret;
asm volatile (
"int $0x80"
: "=a"(ret)
: "a"(SYS_SOCKETCALL_I386), "b"(SYS_SOCKET), "c"(args)
: "memory"
);
if (ret < 0) {
errno = -ret;
perror("socket");
return 1;
}
#ifdef VSOCK_CONNECT
signal(SIGALRM, timeout);
alarm(10);
int fd = ret;
struct sockaddr_vm *address = (struct sockaddr_vm *)(mapping + 64);
memset(address, 0, sizeof(*address));
address->svm_family = AF_VSOCK;
address->svm_cid = VMADDR_CID_LOCAL;
address->svm_port = PORT;
args[0] = (unsigned int)fd;
args[1] = (unsigned int)(uintptr_t)address;
args[2] = sizeof(*address);
asm volatile (
"int $0x80"
: "=a"(ret)
: "a"(SYS_SOCKETCALL_I386), "b"(SYS_CONNECT), "c"(args)
: "memory"
);
if (ret < 0) {
int error = -ret;
errno = error;
perror("connect");
close(fd);
if (error == EPERM || error == EACCES)
return CONNECT_DENIED_EXIT_CODE;
return 1;
}
static const char client_payload[] = CLIENT_PAYLOAD;
static const char server_payload[] = EXPECTED_SERVER_PAYLOAD;
char reply[sizeof(server_payload) - 1];
if (write_full(fd, client_payload, sizeof(client_payload) - 1) < 0 ||
read_full(fd, reply, sizeof(reply)) < 0) {
close(fd);
return 1;
}
if (memcmp(reply, server_payload, sizeof(reply)) != 0) {
fprintf(stderr, "server payload mismatch\n");
close(fd);
return 1;
}
alarm(0);
printf("AF_VSOCK connect via socketcall succeeded\n");
close(fd);
return 0;
#else
printf("socket(%d, %d, 0) via socketcall succeeded\n", SOCK_FAMILY, SOCK_TYPE);
close(ret);
return 0;
#endif
}