parent
76f34954a4
commit
e12c5bbdfb
@ -0,0 +1,7 @@ |
||||
#!/usr/bin/env bash |
||||
set -e |
||||
eval "$(bin/load-rbenv "$1")" |
||||
BASE_DIR="$(dirname "$(dirname "$0")")" |
||||
shift |
||||
LD_PRELOAD="$BASE_DIR/build/libfake.so" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$BASE_DIR/build/" \ |
||||
exec rspec "$@" |
@ -0,0 +1,46 @@ |
||||
require 'ffi' |
||||
|
||||
module Fake |
||||
extend FFI::Library |
||||
ffi_lib 'fake' |
||||
|
||||
def self.freeze(time) |
||||
self._freeze time.to_i |
||||
return yield |
||||
ensure |
||||
self._unfreeze |
||||
end |
||||
require 'amazing_print' |
||||
|
||||
def self.getaddrinfo(host, *ips) |
||||
args = ips.collect { |ip| [:string, ip] } |
||||
self._mock_getaddrinfo host, ips.size, *args.flatten |
||||
return yield |
||||
ensure |
||||
self._unmock_getaddrinfo |
||||
end |
||||
|
||||
private |
||||
|
||||
def self._freeze(_) |
||||
#This is a stub, used for indexing |
||||
end |
||||
|
||||
def self._unfreeze |
||||
#This is a stub, used for indexing |
||||
end |
||||
|
||||
attach_function :_freeze, [:time_t], :void |
||||
attach_function :_unfreeze, [], :void |
||||
|
||||
def self._mock_getaddrinfo(_, _, *_) |
||||
#This is a stub, used for indexing |
||||
end |
||||
|
||||
def self._unmock_getaddrinfo |
||||
#This is a stub, used for indexing |
||||
end |
||||
|
||||
attach_function :_mock_getaddrinfo, [:string, :size_t, :varargs], :void |
||||
attach_function :_unmock_getaddrinfo, [], :void |
||||
end |
@ -0,0 +1,216 @@ |
||||
#define _GNU_SOURCE |
||||
#include <dlfcn.h> |
||||
#include <stdbool.h> |
||||
#include <netdb.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdarg.h> |
||||
#include <stdio.h> |
||||
#include <sys/socket.h> |
||||
#include <arpa/inet.h> |
||||
#include "fake.h" |
||||
|
||||
bool frozen = false; |
||||
time_t frozen_time = 0; |
||||
static time_t (*libc_time)(time_t*); |
||||
|
||||
void _freeze(const time_t time) { |
||||
frozen_time = time; |
||||
frozen = true; |
||||
} |
||||
|
||||
void _unfreeze() { |
||||
frozen = false; |
||||
} |
||||
|
||||
time_t time(time_t *arg) { |
||||
if (frozen) { |
||||
if (arg) *arg = frozen_time; |
||||
return frozen_time; |
||||
} else { |
||||
time_t time = libc_time(arg); |
||||
return time; |
||||
} |
||||
} |
||||
|
||||
typedef struct ip_t { |
||||
int af; |
||||
void *addr; |
||||
} ip_t; |
||||
typedef struct host_t { |
||||
char *host; |
||||
size_t size; |
||||
ip_t **ips; |
||||
} host_t; |
||||
host_t *mocked_host = NULL; |
||||
|
||||
static int (*libc_getaddrinfo)(const char*, const char*, |
||||
const struct addrinfo *, struct addrinfo **); |
||||
|
||||
static void free_mocked_host() { |
||||
if (mocked_host == NULL) return; |
||||
for (size_t n =0; n < mocked_host->size; ++n ) { |
||||
ip_t *ip = mocked_host->ips[n]; |
||||
free(ip->addr); |
||||
free(ip); |
||||
} |
||||
free(mocked_host->ips); |
||||
free(mocked_host->host); |
||||
free(mocked_host); |
||||
mocked_host = NULL; |
||||
} |
||||
|
||||
void _mock_getaddrinfo(const char *host, const size_t size, ...) { |
||||
free_mocked_host(); |
||||
|
||||
host_t *mocked = malloc(sizeof(*mocked)); |
||||
mocked->host = strdup(host); |
||||
mocked->size = size; |
||||
ip_t **ips = malloc(size * sizeof(*ips)); |
||||
|
||||
va_list args; |
||||
va_start(args, size); |
||||
|
||||
for (size_t n = 0; n < size; ++n) { |
||||
char *arg = va_arg(args, char*); |
||||
ip_t *ip = malloc(sizeof(*ip)); |
||||
int family; |
||||
void *addr, *saddr; |
||||
if (strstr(arg, ".") != NULL) { |
||||
family = AF_INET; |
||||
struct sockaddr_in *s4 = malloc(sizeof(*s4)); |
||||
s4->sin_family = family; |
||||
s4->sin_port = 0; |
||||
saddr = s4; |
||||
addr = &(s4->sin_addr); |
||||
} else if (strstr(arg, ":") != NULL) { |
||||
family = AF_INET6; |
||||
struct sockaddr_in6 *s6 = malloc(sizeof(*s6)); |
||||
s6->sin6_family = family; |
||||
s6->sin6_port = 0; |
||||
s6->sin6_flowinfo = 0; |
||||
s6->sin6_scope_id = 0; |
||||
saddr = s6; |
||||
addr = &(s6->sin6_addr); |
||||
} else { |
||||
fprintf(stderr, "Invalid IP format: %s\n", arg); |
||||
exit(-1); |
||||
} |
||||
inet_pton(family, arg, addr); |
||||
|
||||
ip->af = family; |
||||
ip->addr = saddr; |
||||
|
||||
ips[n] = ip; |
||||
} |
||||
|
||||
va_end(args); |
||||
|
||||
mocked->ips = ips; |
||||
mocked_host = mocked; |
||||
} |
||||
|
||||
void _unmock_getaddrinfo() { |
||||
free_mocked_host(); |
||||
} |
||||
|
||||
typedef struct socktype_t { |
||||
int type; |
||||
int protocol; |
||||
} socktype_t; |
||||
const socktype_t SOCK_TYPES[] = { |
||||
{ .type = SOCK_STREAM, .protocol = IPPROTO_TCP }, |
||||
{ .type = SOCK_DGRAM, .protocol = IPPROTO_UDP }, |
||||
{ .type = SOCK_RAW, .protocol = IPPROTO_IP } |
||||
}; |
||||
const size_t SOCK_TYPES_SIZE = sizeof(SOCK_TYPES)/sizeof(socktype_t); |
||||
|
||||
int get_port_from_service(const char *service) { |
||||
if (service == NULL) return 0; |
||||
|
||||
struct servent* servent = getservbyname(service, NULL); |
||||
int port; |
||||
if (servent == NULL) { |
||||
port = htons(atoi(service)); |
||||
} else { |
||||
port = servent->s_port; |
||||
free(servent); |
||||
} |
||||
|
||||
return port; |
||||
} |
||||
|
||||
int getaddrinfo(const char *node, const char *service, |
||||
const struct addrinfo *hints, struct addrinfo **res) { |
||||
const int port = get_port_from_service(service); |
||||
|
||||
if (mocked_host == NULL || strcmp(node, mocked_host->host) != 0) |
||||
return libc_getaddrinfo(node, service, hints, res); |
||||
|
||||
size_t size = mocked_host->size; |
||||
struct addrinfo *addr = NULL, *previous = NULL; |
||||
for (size_t n = 0; n < size; ++n) { |
||||
const ip_t *ip = mocked_host->ips[n]; |
||||
|
||||
for ( size_t m = 0; m < SOCK_TYPES_SIZE; ++m) { |
||||
const socktype_t type = SOCK_TYPES[m]; |
||||
|
||||
if ( (hints->ai_socktype != 0 && hints->ai_socktype != type.type) || |
||||
(hints->ai_protocol != 0 && hints->ai_protocol != type.protocol) ) { |
||||
continue; |
||||
} |
||||
|
||||
addr = malloc(sizeof(*addr)); |
||||
addr->ai_flags = hints->ai_flags; |
||||
addr->ai_family = ip->af; |
||||
addr->ai_socktype = type.type; |
||||
addr->ai_protocol = type.protocol; |
||||
|
||||
size_t len; |
||||
void *ai_addr; |
||||
switch (addr->ai_family) { |
||||
case AF_INET:; |
||||
struct sockaddr_in *ai_addr4; |
||||
len = sizeof(*ai_addr4); |
||||
ai_addr4 = malloc(len); |
||||
ai_addr = ai_addr4; |
||||
memcpy(ai_addr4, ip->addr, len); |
||||
ai_addr4->sin_port = port; |
||||
break; |
||||
case AF_INET6:; |
||||
struct sockaddr_in6 *ai_addr6; |
||||
len = sizeof(*ai_addr6); |
||||
ai_addr6 = malloc(len); |
||||
ai_addr = ai_addr6; |
||||
memcpy(ai_addr6, ip->addr, len); |
||||
ai_addr6->sin6_port = port; |
||||
break; |
||||
} |
||||
addr->ai_addrlen = len; |
||||
addr->ai_addr = ai_addr; |
||||
addr->ai_canonname = NULL; |
||||
addr->ai_next = previous; |
||||
previous = addr; |
||||
} |
||||
} |
||||
|
||||
*res = addr; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void __attribute__((constructor)) setup() { |
||||
char *error = dlerror(); |
||||
*(void **) (&libc_time) = dlsym(RTLD_NEXT, "time"); |
||||
if ((error = dlerror()) != NULL) { |
||||
fprintf(stderr, "%s\n", error); |
||||
} |
||||
*(void **) (&libc_getaddrinfo) = dlsym(RTLD_NEXT, "getaddrinfo"); |
||||
if ((error = dlerror()) != NULL) { |
||||
fprintf(stderr, "%s\n", error); |
||||
} |
||||
} |
||||
|
||||
static void __attribute__((destructor)) teardown() { |
||||
free_mocked_host(); |
||||
} |
@ -0,0 +1,7 @@ |
||||
#include <time.h> |
||||
|
||||
void _freeze(const time_t time); |
||||
void _unfreeze(); |
||||
|
||||
void _mock_getaddrinfo(const char *host, const size_t size, ...); |
||||
void _unmock_getaddrinfo(); |
@ -0,0 +1,82 @@ |
||||
#include <netdb.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <stdarg.h> |
||||
#include <stdio.h> |
||||
#include <sys/socket.h> |
||||
#include <arpa/inet.h> |
||||
|
||||
int debug(const char *fmt, ...) { |
||||
// return 0;
|
||||
va_list arg; |
||||
va_start(arg, fmt); |
||||
int result = vfprintf(stderr, fmt, arg); |
||||
va_end(arg); |
||||
fflush(stderr); |
||||
return result; |
||||
} |
||||
|
||||
void debug_saddr(const int family, const void *saddr) { |
||||
char buff[INET6_ADDRSTRLEN]; |
||||
const void *addr; |
||||
switch (family) { |
||||
case AF_INET:; |
||||
const struct sockaddr_in *s4 = saddr; |
||||
debug(" family: %d\n", s4->sin_family); |
||||
debug(" port: %d\n", s4->sin_port); |
||||
addr = &(s4->sin_addr); |
||||
break; |
||||
case AF_INET6:; |
||||
const struct sockaddr_in6 *s6 = saddr; |
||||
debug(" family: %d\n", s6->sin6_family); |
||||
debug(" port: %d\n", s6->sin6_port); |
||||
debug(" flowinfo: %d\n", s6->sin6_flowinfo); |
||||
debug(" scope: %d\n", s6->sin6_scope_id); |
||||
addr = &(s6->sin6_addr); |
||||
break; |
||||
} |
||||
|
||||
inet_ntop(family, addr, buff, INET6_ADDRSTRLEN); |
||||
debug(" addr: %p, %s\n", saddr, buff); |
||||
} |
||||
|
||||
void debug_addr(const struct addrinfo *addr) { |
||||
debug("addrinfo: %p\n", addr); |
||||
debug(" flags: %d\n", addr->ai_flags); |
||||
debug(" family: %d\n", addr->ai_family); |
||||
debug(" type: %d\n", addr->ai_socktype); |
||||
debug(" protocol: %d\n", addr->ai_protocol); |
||||
debug(" len: %d\n", addr->ai_addrlen); |
||||
if (addr->ai_addrlen > 0) debug_saddr(addr->ai_family, addr->ai_addr); |
||||
debug(" name: %s\n", addr->ai_canonname); |
||||
debug(" next: %p\n", addr->ai_next); |
||||
} |
||||
|
||||
void debug_addrinfo(const struct addrinfo *addr) { |
||||
for (const struct addrinfo *rp = addr; rp != NULL; rp = rp->ai_next) { |
||||
debug_addr(rp); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
struct addrinfo hints; |
||||
memset(&hints, 0, sizeof(struct addrinfo)); |
||||
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ |
||||
hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ |
||||
hints.ai_flags = 0; /* For wildcard IP address */ |
||||
hints.ai_protocol = 0; /* Any protocol */ |
||||
hints.ai_canonname = NULL; |
||||
hints.ai_addr = NULL; |
||||
hints.ai_next = NULL; |
||||
|
||||
struct addrinfo *result; |
||||
debug("Before result: result=%p\n", &result); |
||||
const int s = getaddrinfo("localhost", NULL, &hints, &result); |
||||
if (s != 0) { |
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
debug("The result: result=%p\n", result); |
||||
debug_addrinfo(result); |
||||
} |
@ -1,27 +0,0 @@ |
||||
require 'ffi' |
||||
|
||||
module FakeTime |
||||
extend FFI::Library |
||||
ffi_lib 'faketime' |
||||
|
||||
def self._freeze(_) |
||||
#This is a stub, used for indexing |
||||
end |
||||
def self.unfreeze |
||||
#This is a stub, used for indexing |
||||
end |
||||
|
||||
attach_function :_freeze, [:ulong], :void |
||||
attach_function :unfreeze, [], :void |
||||
|
||||
def self.freeze(time) |
||||
self._freeze time.to_i |
||||
if block_given? |
||||
begin |
||||
return yield |
||||
ensure |
||||
self.unfreeze |
||||
end |
||||
end |
||||
end |
||||
end |
@ -1,35 +0,0 @@ |
||||
#define _GNU_SOURCE |
||||
#include <dlfcn.h> |
||||
#include "time.h" |
||||
#include "faketime.h" |
||||
|
||||
char frozen = 0; |
||||
time_t frozen_time = 0; |
||||
|
||||
typedef time_t (*orig_time_f_type)(time_t*); |
||||
orig_time_f_type orig_time = NULL; |
||||
|
||||
void _freeze(unsigned long time) { |
||||
frozen_time = (time_t)time; |
||||
frozen = 1; |
||||
} |
||||
|
||||
void unfreeze() { |
||||
frozen = 0; |
||||
} |
||||
|
||||
time_t time(time_t *arg) { |
||||
if (orig_time == NULL) { |
||||
orig_time = (orig_time_f_type) dlsym(RTLD_NEXT, "time"); |
||||
} |
||||
|
||||
if (frozen) { |
||||
if (arg) { |
||||
*arg = frozen_time; |
||||
} |
||||
return frozen_time; |
||||
} else { |
||||
time_t time = orig_time(arg); |
||||
return time; |
||||
} |
||||
} |
@ -1,2 +0,0 @@ |
||||
void _freeze(unsigned long time); |
||||
void unfreeze(); |
Binary file not shown.
Loading…
Reference in new issue