blob: 185ef87f0b725b660ea5de61b0f8e0c4817e4c39 [file] [log] [blame]
/* Copyright (C) 2016 Alexander Lamaison
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include "openssh_fixture.h"
#include "libssh2_config.h"
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int run_command(const char *command, char **output)
{
FILE *pipe;
char command_buf[BUFSIZ];
int ret;
if (output) {
*output = NULL;
}
/* Rewrite the command to redirect stderr to stdout to we can output it */
ret = snprintf(command_buf, sizeof(command_buf), "%s 2>&1", command);
if (ret < 0 || ret >= BUFSIZ) {
fprintf(stderr, "Unable to format command (%s)\n", command);
return -1;
}
fprintf(stdout, "Command: %s\n", command);
#ifdef WIN32
pipe = _popen(command_buf, "r");
#else
pipe = popen(command_buf, "r");
#endif
if (pipe) {
char buf[BUFSIZ];
char *p = buf;
while (fgets(p, sizeof(buf) - (p - buf), pipe) != NULL)
;
#ifdef WIN32
ret = _pclose(pipe);
#else
ret = pclose(pipe);
#endif
if (ret == 0) {
if (output) {
/* command output may contain a trailing newline, so we trim
* whitespace here */
size_t end = strlen(buf) - 1;
while (end > 0 && isspace(buf[end])) {
buf[end] = '\0';
}
*output = strdup(buf);
}
}
else {
fprintf(stderr, "Error running command '%s' (exit %d): %s\n",
command, ret, buf);
}
return ret;
}
else {
fprintf(stderr, "Unable to execute command '%s'\n", command);
return -1;
}
}
static int build_openssh_server_docker_image()
{
return run_command("docker build -t libssh2/openssh_server openssh_server",
NULL);
}
static int start_openssh_server(char **container_id_out)
{
return run_command("docker run --detach -P libssh2/openssh_server",
container_id_out);
}
static int stop_openssh_server(char *container_id)
{
char command_buf[BUFSIZ];
int rc = snprintf(command_buf, sizeof(command_buf), "docker stop %s",
container_id);
if (rc > -1 && rc < BUFSIZ) {
return run_command(command_buf, NULL);
}
else {
return rc;
}
}
static const char *docker_machine_name()
{
return getenv("DOCKER_MACHINE_NAME");
}
static int ip_address_from_container(char *container_id, char **ip_address_out)
{
const char *active_docker_machine = docker_machine_name();
if (active_docker_machine != NULL) {
// This can be flaky when tests run in parallel (see
// https://github.com/docker/machine/issues/2612), so we retry a few
// times with exponential backoff if it fails
int attempt_no = 0;
int wait_time = 500;
for (;;) {
char command_buf[BUFSIZ];
int rc = snprintf(command_buf, sizeof(command_buf),
"docker-machine ip %s", active_docker_machine);
if (rc > -1 && rc < BUFSIZ) {
return run_command(command_buf, ip_address_out);
}
if (attempt_no > 5) {
fprintf(
stderr,
"Unable to get IP from docker-machine after %d attempts\n",
attempt_no);
return -1;
}
else {
#ifdef WIN32
#pragma warning(push)
#pragma warning(disable : 4996)
_sleep(wait_time);
#pragma warning(pop)
#else
sleep(wait_time);
#endif
++attempt_no;
wait_time *= 2;
}
}
}
else {
char command_buf[BUFSIZ];
int rc = snprintf(
command_buf, sizeof(command_buf),
"docker inspect --format \"{{ index (index (index "
".NetworkSettings.Ports \\\"22/tcp\\\") 0) \\\"HostIp\\\" }}\" %s",
container_id);
if (rc > -1 && rc < BUFSIZ) {
return run_command(command_buf, ip_address_out);
}
else {
return rc;
}
}
}
static int port_from_container(char *container_id, char **port_out)
{
char command_buf[BUFSIZ];
int rc = snprintf(
command_buf, sizeof(command_buf),
"docker inspect --format \"{{ index (index (index "
".NetworkSettings.Ports \\\"22/tcp\\\") 0) \\\"HostPort\\\" }}\" %s",
container_id);
if (rc > -1 && rc < BUFSIZ) {
return run_command(command_buf, port_out);
}
else {
return rc;
}
}
static int open_socket_to_container(char *container_id)
{
char *ip_address = NULL;
int ret = ip_address_from_container(container_id, &ip_address);
if (ret == 0) {
char *port_string = NULL;
ret = port_from_container(container_id, &port_string);
if (ret == 0) {
unsigned long hostaddr = inet_addr(ip_address);
if (hostaddr != (unsigned long)(-1)) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock > -1) {
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons((short)strtol(port_string, NULL, 0));
sin.sin_addr.s_addr = hostaddr;
if (connect(sock, (struct sockaddr *)(&sin),
sizeof(struct sockaddr_in)) == 0) {
ret = sock;
}
else {
fprintf(stderr, "Failed to connect to %s:%s\n",
ip_address, port_string);
ret = -1;
}
}
else {
fprintf(stderr, "Failed to open socket (%d)\n", sock);
ret = -1;
}
}
else {
fprintf(stderr, "Failed to convert %s host address\n",
ip_address);
ret = -1;
}
free(port_string);
}
else {
fprintf(stderr, "Failed to get port for container %s\n",
container_id);
ret = -1;
}
free(ip_address);
}
else {
fprintf(stderr, "Failed to get IP address for container %s\n",
container_id);
ret = -1;
}
return ret;
}
static char *running_container_id = NULL;
int start_openssh_fixture()
{
int ret;
#ifdef HAVE_WINSOCK2_H
WSADATA wsadata;
ret = WSAStartup(MAKEWORD(2, 0), &wsadata);
if (ret != 0) {
fprintf(stderr, "WSAStartup failed with error: %d\n", ret);
return 1;
}
#endif
ret = build_openssh_server_docker_image();
if (ret == 0) {
return start_openssh_server(&running_container_id);
}
else {
fprintf(stderr, "Failed to build docker image\n");
return ret;
}
}
void stop_openssh_fixture()
{
if (running_container_id) {
stop_openssh_server(running_container_id);
free(running_container_id);
running_container_id = NULL;
}
else {
fprintf(stderr, "Cannot stop container - none started");
}
}
int open_socket_to_openssh_server()
{
return open_socket_to_container(running_container_id);
}