PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/selene_client.c

https://github.com/rphillips/selene
C | 350 lines | 274 code | 56 blank | 20 comment | 51 complexity | b4577d4b68d0cf95de600f8b68c116b8 MD5 | raw file
  1. /*
  2. * Licensed to Selene developers ('Selene') under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * Selene licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. #include "selene.h"
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <sys/socket.h>
  22. #include <netinet/in.h>
  23. #include <netdb.h>
  24. #include <unistd.h>
  25. #include <errno.h>
  26. #include <arpa/inet.h>
  27. #include <fcntl.h>
  28. #include <sys/time.h>
  29. #include <sys/types.h>
  30. #include <sys/select.h>
  31. #define SELENE_CLIENT_DEFAULT_HOST "localhost"
  32. #define SELENE_CLIENT_DEFAULT_PORT 4433
  33. /**
  34. * 'Simple' TLS Client, connects to a port, pipes stdin to it
  35. */
  36. #define SERR(exp) do { \
  37. selene_error_t *SERR__err = NULL; \
  38. SERR__err = (exp); \
  39. if (SERR__err != SELENE_SUCCESS) { \
  40. fprintf(stderr, "[%s:%d] Selene Error: (%d) %s\n Caught at: [%s:%d] %s\n", \
  41. SERR__err->file, SERR__err->line, SERR__err->err, SERR__err->msg, \
  42. __FILE__, __LINE__, \
  43. # exp ); \
  44. selene_error_clear(SERR__err); \
  45. return EXIT_FAILURE; \
  46. } \
  47. } while (0);
  48. typedef struct {
  49. int sock;
  50. int write_err;
  51. int read_err;
  52. } client_t;
  53. static void
  54. setblocking(int fd)
  55. {
  56. int opts;
  57. opts = fcntl(fd, F_GETFL);
  58. opts = (opts ^ O_NONBLOCK);
  59. fcntl(fd, F_SETFL, opts);
  60. }
  61. static void
  62. setnonblocking(int fd)
  63. {
  64. int opts;
  65. opts = fcntl(fd, F_GETFL);
  66. opts = (opts | O_NONBLOCK);
  67. fcntl(fd, F_SETFL, opts);
  68. }
  69. static selene_error_t*
  70. have_logline(selene_t *s, selene_event_e event, void *baton)
  71. {
  72. const char *p = NULL;
  73. size_t len = 0;
  74. selene_log_msg_get(s, &p, &len);
  75. if (len > 0) {
  76. fwrite(p, len, 1, stderr);
  77. fflush(stderr);
  78. }
  79. return SELENE_SUCCESS;
  80. }
  81. static selene_error_t*
  82. have_cleartext(selene_t *s, selene_event_e event, void *baton)
  83. {
  84. char buf[8096];
  85. size_t blen = 0;
  86. size_t remaining = 0;
  87. do {
  88. SELENE_ERR(selene_io_out_clear_bytes(s,
  89. &buf[0], sizeof(buf),
  90. &blen, &remaining));
  91. if (blen > 0) {
  92. fwrite(buf, blen, 1, stdout);
  93. fflush(stdout);
  94. }
  95. } while(remaining > 0);
  96. return SELENE_SUCCESS;
  97. }
  98. static selene_error_t*
  99. want_pull(selene_t *s, selene_event_e event, void *baton)
  100. {
  101. int rv = 0;
  102. char buf[8096];
  103. size_t blen = 0;
  104. size_t remaining = 0;
  105. client_t *c = (client_t*) baton;
  106. do {
  107. SELENE_ERR(selene_io_out_enc_bytes(s,
  108. &buf[0], sizeof(buf),
  109. &blen, &remaining));
  110. if (blen > 0) {
  111. setblocking(c->sock);
  112. rv = write(c->sock, buf, blen);
  113. if (rv < 0) {
  114. c->write_err = errno;
  115. break;
  116. }
  117. }
  118. } while(remaining > 0);
  119. return SELENE_SUCCESS;
  120. }
  121. static int
  122. read_from_sock(client_t *c, selene_t *s)
  123. {
  124. int err;
  125. ssize_t rv = 0;
  126. do {
  127. char buf[8096];
  128. setnonblocking(c->sock);
  129. rv = read(c->sock, &buf[0], sizeof(buf));
  130. if (rv == -1) {
  131. err = errno;
  132. if (err != EAGAIN) {
  133. c->read_err = err;
  134. break;
  135. }
  136. }
  137. if (rv == 0) {
  138. break;
  139. }
  140. if (rv > 0) {
  141. SERR(selene_io_in_enc_bytes(s, buf, rv));
  142. }
  143. } while(rv > 0);
  144. return 0;
  145. }
  146. static int
  147. connect_to(selene_t *s, const char *host, int port, FILE *fp)
  148. {
  149. fd_set readers;
  150. int rv = 0;
  151. client_t client;
  152. char buf[8096];
  153. char port_str[16];
  154. char *p = NULL;
  155. struct addrinfo hints, *res, *res0;
  156. memset(&client, 0, sizeof(client));
  157. SERR(selene_subscribe(s, SELENE_EVENT_IO_OUT_ENC,
  158. want_pull, &client));
  159. SERR(selene_subscribe(s, SELENE_EVENT_IO_OUT_CLEAR,
  160. have_cleartext, &client));
  161. snprintf(port_str, sizeof(port_str), "%i", port);
  162. memset(&hints, 0, sizeof(hints));
  163. hints.ai_family = PF_UNSPEC;
  164. hints.ai_socktype = SOCK_STREAM;
  165. rv = getaddrinfo(host, port_str, &hints, &res0);
  166. if (rv != 0) {
  167. fprintf(stderr, "TCP getaddrinfo(%s:%d) failed: (%d) %s\n",
  168. host, port,
  169. rv, gai_strerror(rv));
  170. exit(EXIT_FAILURE);
  171. }
  172. client.sock = -1;
  173. for (res = res0; res; res = res->ai_next) {
  174. client.sock = socket(res->ai_family, res->ai_socktype,
  175. res->ai_protocol);
  176. if (client.sock < 0) {
  177. continue;
  178. }
  179. rv = connect(client.sock, res->ai_addr, res->ai_addrlen);
  180. if (rv != 0) {
  181. close(client.sock);
  182. client.sock = -1;
  183. continue;
  184. }
  185. break;
  186. }
  187. freeaddrinfo(res0);
  188. if (client.sock == -1) {
  189. fprintf(stderr, "TCP connect(%s:%d) failed\n", host, port);
  190. exit(EXIT_FAILURE);
  191. }
  192. SERR(selene_start(s));
  193. while (client.write_err == 0)
  194. {
  195. FD_ZERO(&readers);
  196. FD_SET(client.sock, &readers);
  197. FD_SET(fileno(fp), &readers);
  198. rv = select(FD_SETSIZE, &readers, NULL, NULL, NULL);
  199. if (rv > 0) {
  200. if (FD_ISSET(fileno(fp), &readers)) {
  201. p = fgets(buf, sizeof(buf), fp);
  202. if (p == NULL) {
  203. break;
  204. }
  205. SERR(selene_io_in_clear_bytes(s, p, strlen(p)));
  206. }
  207. else if (FD_ISSET(client.sock, &readers)) {
  208. read_from_sock(&client, s);
  209. }
  210. }
  211. }
  212. if (client.write_err != 0) {
  213. fprintf(stderr, "TCP write to %s:%d failed: (%d) %s\n",
  214. host, port,
  215. client.write_err, strerror(client.write_err));
  216. exit(EXIT_FAILURE);
  217. }
  218. if (client.read_err != 0) {
  219. fprintf(stderr, "TCP read from %s:%d failed: (%d) %s\n",
  220. host, port,
  221. client.read_err, strerror(client.read_err));
  222. exit(EXIT_FAILURE);
  223. }
  224. return 0;
  225. }
  226. void usage()
  227. {
  228. fprintf(stderr, "usage: selene_client args\n");
  229. fprintf(stderr, "\n");
  230. fprintf(stderr, " -host host\n");
  231. fprintf(stderr, " -port port\n");
  232. fprintf(stderr, " -connect host:port\n");
  233. exit(EXIT_SUCCESS);
  234. }
  235. int main(int argc, char* argv[])
  236. {
  237. const char *host = SELENE_CLIENT_DEFAULT_HOST;
  238. int port = SELENE_CLIENT_DEFAULT_PORT;
  239. selene_t *s = NULL;
  240. selene_conf_t *conf = NULL;
  241. selene_error_t *err = NULL;
  242. int rv = 0;
  243. int i;
  244. for (i = 1; i < argc; i++) {
  245. /* TODO: s_client compat */
  246. if (!strcmp("-host", argv[i]) && argc > i + 1) {
  247. host = argv[i+1];
  248. i++;
  249. }
  250. else if (!strcmp("-port", argv[i]) && argc > i + 1) {
  251. port = atoi(argv[i+1]);
  252. i++;
  253. }
  254. else if (!strcmp("-connect", argv[i]) && argc > i + 1) {
  255. char *p;
  256. host = argv[i+1];
  257. if ((p = strstr(host,":")) == NULL) {
  258. fprintf(stderr, "no port found\n");
  259. exit(EXIT_FAILURE);
  260. }
  261. *(p++) = '\0';
  262. port = atoi(p);
  263. i++;
  264. }
  265. else {
  266. selene_destroy(s);
  267. fprintf(stderr, "Invalid args\n");
  268. usage();
  269. exit(EXIT_FAILURE);
  270. }
  271. }
  272. if (host == NULL) {
  273. fprintf(stderr, "-host must be set\n");
  274. exit(EXIT_FAILURE);
  275. }
  276. if (port <= 0) {
  277. fprintf(stderr, "-port must be set\n");
  278. exit(EXIT_FAILURE);
  279. }
  280. SERR(selene_conf_create(&conf));
  281. SERR(selene_conf_use_reasonable_defaults(conf));
  282. SERR(selene_conf_name_indication(conf, host));
  283. err = selene_client_create(conf, &s);
  284. if (err != SELENE_SUCCESS) {
  285. fprintf(stderr, "Failed to create client instance: (%d) %s [%s:%d]\n",
  286. err->err, err->msg, err->file, err->line);
  287. exit(EXIT_FAILURE);
  288. }
  289. selene_subscribe(s, SELENE_EVENT_LOG_MSG, have_logline, NULL);
  290. rv = connect_to(s, host, port, stdin);
  291. selene_destroy(s);
  292. selene_conf_destroy(conf);
  293. return rv;
  294. }