geom_gate userland utility improvements
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

324 lines
8.8 KiB

  1. /* Copyright (C) 2016 Alexander Lamaison
  2. * All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms,
  5. * with or without modification, are permitted provided
  6. * that the following conditions are met:
  7. *
  8. * Redistributions of source code must retain the above
  9. * copyright notice, this list of conditions and the
  10. * following disclaimer.
  11. *
  12. * Redistributions in binary form must reproduce the above
  13. * copyright notice, this list of conditions and the following
  14. * disclaimer in the documentation and/or other materials
  15. * provided with the distribution.
  16. *
  17. * Neither the name of the copyright holder nor the names
  18. * of any other contributors may be used to endorse or
  19. * promote products derived from this software without
  20. * specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  23. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  24. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  25. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  27. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  28. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  29. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  30. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  31. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  32. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  33. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  34. * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  35. * OF SUCH DAMAGE.
  36. */
  37. #include "openssh_fixture.h"
  38. #include "libssh2_config.h"
  39. #ifdef HAVE_WINSOCK2_H
  40. #include <winsock2.h>
  41. #endif
  42. #ifdef HAVE_SYS_SOCKET_H
  43. #include <sys/socket.h>
  44. #endif
  45. #ifdef HAVE_ARPA_INET_H
  46. #include <arpa/inet.h>
  47. #endif
  48. #ifdef HAVE_NETINET_IN_H
  49. #include <netinet/in.h>
  50. #endif
  51. #ifdef HAVE_UNISTD_H
  52. #include <unistd.h>
  53. #endif
  54. #include <ctype.h>
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <string.h>
  58. #include <stdarg.h>
  59. static int run_command_varg(char **output, const char *command, va_list args)
  60. {
  61. FILE *pipe;
  62. char redirect_stderr[] = "%s 2>&1";
  63. char command_buf[BUFSIZ];
  64. char buf[BUFSIZ];
  65. char *p;
  66. int ret;
  67. if(output) {
  68. *output = NULL;
  69. }
  70. /* Format the command string */
  71. ret = vsnprintf(command_buf, sizeof(command_buf), command, args);
  72. if(ret < 0 || ret >= BUFSIZ) {
  73. fprintf(stderr, "Unable to format command (%s)\n", command);
  74. return -1;
  75. }
  76. /* Rewrite the command to redirect stderr to stdout to we can output it */
  77. if(strlen(command_buf) + strlen(redirect_stderr) >= sizeof(buf)) {
  78. fprintf(stderr, "Unable to rewrite command (%s)\n", command);
  79. return -1;
  80. }
  81. ret = snprintf(buf, sizeof(buf), redirect_stderr, command_buf);
  82. if(ret < 0 || ret >= BUFSIZ) {
  83. fprintf(stderr, "Unable to rewrite command (%s)\n", command);
  84. return -1;
  85. }
  86. fprintf(stdout, "Command: %s\n", command);
  87. #ifdef WIN32
  88. pipe = _popen(buf, "r");
  89. #else
  90. pipe = popen(buf, "r");
  91. #endif
  92. if(!pipe) {
  93. fprintf(stderr, "Unable to execute command '%s'\n", command);
  94. return -1;
  95. }
  96. p = buf;
  97. while(fgets(p, sizeof(buf) - (p - buf), pipe) != NULL)
  98. ;
  99. #ifdef WIN32
  100. ret = _pclose(pipe);
  101. #else
  102. ret = pclose(pipe);
  103. #endif
  104. if(ret != 0) {
  105. fprintf(stderr, "Error running command '%s' (exit %d): %s\n",
  106. command, ret, buf);
  107. }
  108. if(output) {
  109. /* command output may contain a trailing newline, so we trim
  110. * whitespace here */
  111. size_t end = strlen(buf) - 1;
  112. while(end > 0 && isspace(buf[end])) {
  113. buf[end] = '\0';
  114. }
  115. *output = strdup(buf);
  116. }
  117. return ret;
  118. }
  119. static int run_command(char **output, const char *command, ...)
  120. {
  121. va_list args;
  122. int ret;
  123. va_start(args, command);
  124. ret = run_command_varg(output, command, args);
  125. va_end(args);
  126. return ret;
  127. }
  128. static int build_openssh_server_docker_image()
  129. {
  130. return run_command(NULL, "docker build -t libssh2/openssh_server "
  131. "openssh_server");
  132. }
  133. static int start_openssh_server(char **container_id_out)
  134. {
  135. return run_command(container_id_out,
  136. "docker run --detach -P libssh2/openssh_server");
  137. }
  138. static int stop_openssh_server(char *container_id)
  139. {
  140. return run_command(NULL, "docker stop %s", container_id);
  141. }
  142. static const char *docker_machine_name()
  143. {
  144. return getenv("DOCKER_MACHINE_NAME");
  145. }
  146. static int ip_address_from_container(char *container_id, char **ip_address_out)
  147. {
  148. const char *active_docker_machine = docker_machine_name();
  149. if(active_docker_machine != NULL) {
  150. /* This can be flaky when tests run in parallel (see
  151. https://github.com/docker/machine/issues/2612), so we retry a few
  152. times with exponential backoff if it fails */
  153. int attempt_no = 0;
  154. int wait_time = 500;
  155. for(;;) {
  156. int ret = run_command(ip_address_out, "docker-machine ip %s",
  157. active_docker_machine);
  158. if(ret == 0) {
  159. return 0;
  160. }
  161. else if(attempt_no > 5) {
  162. fprintf(
  163. stderr,
  164. "Unable to get IP from docker-machine after %d attempts\n",
  165. attempt_no);
  166. return -1;
  167. }
  168. else {
  169. #ifdef WIN32
  170. Sleep(wait_time);
  171. #else
  172. sleep(wait_time);
  173. #endif
  174. ++attempt_no;
  175. wait_time *= 2;
  176. }
  177. }
  178. }
  179. else {
  180. return run_command(ip_address_out,
  181. "docker inspect --format "
  182. "\"{{ index (index (index .NetworkSettings.Ports "
  183. "\\\"22/tcp\\\") 0) \\\"HostIp\\\" }}\" %s",
  184. container_id);
  185. }
  186. }
  187. static int port_from_container(char *container_id, char **port_out)
  188. {
  189. return run_command(port_out,
  190. "docker inspect --format "
  191. "\"{{ index (index (index .NetworkSettings.Ports "
  192. "\\\"22/tcp\\\") 0) \\\"HostPort\\\" }}\" %s",
  193. container_id);
  194. }
  195. static int open_socket_to_container(char *container_id)
  196. {
  197. char *ip_address = NULL;
  198. char *port_string = NULL;
  199. unsigned long hostaddr;
  200. int sock;
  201. struct sockaddr_in sin;
  202. int ret = ip_address_from_container(container_id, &ip_address);
  203. if(ret != 0) {
  204. fprintf(stderr, "Failed to get IP address for container %s\n",
  205. container_id);
  206. ret = -1;
  207. goto cleanup;
  208. }
  209. ret = port_from_container(container_id, &port_string);
  210. if(ret != 0) {
  211. fprintf(stderr, "Failed to get port for container %s\n",
  212. container_id);
  213. ret = -1;
  214. }
  215. /* 0.0.0.0 is returned by Docker for Windows, because the container
  216. is reachable from anywhere. But we cannot connect to 0.0.0.0,
  217. instead we assume localhost and try to connect to 127.0.0.1. */
  218. if(ip_address && strcmp(ip_address, "0.0.0.0") == 0) {
  219. free(ip_address);
  220. ip_address = strdup("127.0.0.1");
  221. }
  222. hostaddr = inet_addr(ip_address);
  223. if(hostaddr == (unsigned long)(-1)) {
  224. fprintf(stderr, "Failed to convert %s host address\n", ip_address);
  225. ret = -1;
  226. goto cleanup;
  227. }
  228. sock = socket(AF_INET, SOCK_STREAM, 0);
  229. if(sock <= 0) {
  230. fprintf(stderr, "Failed to open socket (%d)\n", sock);
  231. ret = -1;
  232. goto cleanup;
  233. }
  234. sin.sin_family = AF_INET;
  235. sin.sin_port = htons((short)strtol(port_string, NULL, 0));
  236. sin.sin_addr.s_addr = hostaddr;
  237. if(connect(sock, (struct sockaddr *)(&sin),
  238. sizeof(struct sockaddr_in)) != 0) {
  239. fprintf(stderr, "Failed to connect to %s:%s\n",
  240. ip_address, port_string);
  241. ret = -1;
  242. goto cleanup;
  243. }
  244. ret = sock;
  245. cleanup:
  246. free(ip_address);
  247. free(port_string);
  248. return ret;
  249. }
  250. static char *running_container_id = NULL;
  251. int start_openssh_fixture()
  252. {
  253. int ret;
  254. #ifdef HAVE_WINSOCK2_H
  255. WSADATA wsadata;
  256. ret = WSAStartup(MAKEWORD(2, 0), &wsadata);
  257. if(ret != 0) {
  258. fprintf(stderr, "WSAStartup failed with error: %d\n", ret);
  259. return 1;
  260. }
  261. #endif
  262. ret = build_openssh_server_docker_image();
  263. if(ret == 0) {
  264. return start_openssh_server(&running_container_id);
  265. }
  266. else {
  267. fprintf(stderr, "Failed to build docker image\n");
  268. return ret;
  269. }
  270. }
  271. void stop_openssh_fixture()
  272. {
  273. if(running_container_id) {
  274. stop_openssh_server(running_container_id);
  275. free(running_container_id);
  276. running_container_id = NULL;
  277. }
  278. else {
  279. fprintf(stderr, "Cannot stop container - none started");
  280. }
  281. }
  282. int open_socket_to_openssh_server()
  283. {
  284. return open_socket_to_container(running_container_id);
  285. }