Pipe fitting
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.

221 lines
4.2KB

  1. /* See the LICENSE file for licensing information */
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <sys/socket.h>
  5. #include <openssl/ssl.h>
  6. #include <openssl/err.h>
  7. #include <errno.h>
  8. #include <fcntl.h>
  9. #include <netdb.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <syslog.h>
  14. #include <time.h>
  15. #include <unistd.h>
  16. #define PING_TIMEOUT 240
  17. static SSL_CTX *ctx;
  18. static SSL *ssl;
  19. static char host[128] = "chat.freenode.net";
  20. static char port[6] = "6667";
  21. static short use_ssl = 0;
  22. static unsigned int last_response;
  23. static int
  24. tube(char *direction)
  25. {
  26. int fd;
  27. char buf[512];
  28. snprintf(buf, sizeof(buf), "/tmp/%s.%s", host, direction);
  29. unlink(buf);
  30. mkfifo(buf, 0660);
  31. if ((fd = open(buf, O_RDWR)) < 0)
  32. perror("tube");
  33. return fd;
  34. }
  35. static int
  36. dial(char *host, char *port)
  37. {
  38. int sockfd, err;
  39. struct addrinfo hints, *serv;
  40. memset(&hints, 0, sizeof(hints));
  41. hints.ai_family = AF_INET;
  42. hints.ai_socktype = SOCK_STREAM;
  43. if ((err = getaddrinfo(host, port, &hints, &serv)) != 0) {
  44. fprintf(stderr, "dial: %s\n", gai_strerror(err));
  45. return -1;
  46. }
  47. if ((sockfd = socket(serv->ai_family, serv->ai_socktype,
  48. serv->ai_protocol)) < 0) {
  49. perror("dial");
  50. return -1;
  51. }
  52. if (connect(sockfd, serv->ai_addr, serv->ai_addrlen) < 0) {
  53. perror("dial");
  54. close(sockfd);
  55. return -1;
  56. }
  57. freeaddrinfo(serv);
  58. return sockfd;
  59. }
  60. static int
  61. sslify(int *sockfd)
  62. {
  63. int r;
  64. SSL_library_init();
  65. SSL_load_error_strings();
  66. ctx = SSL_CTX_new(SSLv23_client_method());
  67. SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3
  68. | SSL_OP_SINGLE_DH_USE);
  69. ssl = SSL_new(ctx);
  70. SSL_set_fd(ssl, *sockfd);
  71. SSL_set_connect_state(ssl);
  72. if ((r = SSL_connect(ssl)) < 1) {
  73. fprintf(stderr, "sslify: %s\n", strerror(SSL_get_error(ssl, r)));
  74. SSL_CTX_free(ctx);
  75. return -1;
  76. }
  77. return 0;
  78. }
  79. int
  80. main(int argc, char **argv)
  81. {
  82. int sockfd, in, out;
  83. int maxfd;
  84. fd_set rd;
  85. struct timeval tv;
  86. char buf[512];
  87. int i, r, status;
  88. while ((i = getopt(argc, argv, "Sh:p:")) != -1) {
  89. switch (i) {
  90. case 'S':
  91. use_ssl = 1;
  92. strncpy(port, "6697", sizeof(port));
  93. break;
  94. case 'h':
  95. strncpy(host, optarg, sizeof(host));
  96. break;
  97. case 'p':
  98. strncpy(port, optarg, sizeof(port));
  99. break;
  100. default:
  101. fprintf(stderr, "usage: tubes [-S] [-h host] [-p port]\n");
  102. return 1;
  103. }
  104. }
  105. if ((sockfd = dial(host, port)) < 0)
  106. return 1;
  107. if (use_ssl && sslify(&sockfd) < 0)
  108. return 1;
  109. if ((in = tube("in")) < 0)
  110. return 1;
  111. if ((out = tube("out")) < 0)
  112. return 1;
  113. if (daemon(0, 0) < 0) {
  114. perror("main");
  115. return 1;
  116. }
  117. openlog(argv[0], LOG_PID, LOG_DAEMON);
  118. for (status = 0, last_response = time(NULL);;) {
  119. FD_ZERO(&rd);
  120. maxfd = (out >= sockfd) ? out : sockfd;
  121. FD_SET(out, &rd);
  122. FD_SET(sockfd, &rd);
  123. tv.tv_sec = 10;
  124. tv.tv_usec = 0;
  125. r = select(maxfd+1, &rd, 0, 0, &tv);
  126. if (r < 0) {
  127. if (errno == EINTR)
  128. continue;
  129. syslog(LOG_ERR, strerror(errno));
  130. status = 1;
  131. break;
  132. } else if (r == 0 && time(NULL) - last_response >= PING_TIMEOUT) {
  133. syslog(LOG_ERR, "ping timeout");
  134. status = 1;
  135. break;
  136. }
  137. if (FD_ISSET(out, &rd)) {
  138. if ((i = read(out, buf, sizeof(buf))) < 0) {
  139. if (errno == EINTR)
  140. continue;
  141. syslog(LOG_ERR, "broken pipe");
  142. status = 1;
  143. break;
  144. } else if (i > 0) {
  145. buf[i] = 0;
  146. if (use_ssl)
  147. SSL_write(ssl, buf, strlen(buf));
  148. else
  149. send(sockfd, buf, strlen(buf), 0);
  150. }
  151. }
  152. if (FD_ISSET(sockfd, &rd)) {
  153. if (use_ssl) {
  154. do {
  155. r = 0;
  156. i = SSL_read(ssl, buf, sizeof(buf));
  157. if (SSL_get_error(ssl, i)
  158. == SSL_ERROR_WANT_READ)
  159. r = 1;
  160. } while (SSL_pending(ssl) && !r);
  161. } else
  162. i = recv(sockfd, buf, sizeof(buf), 0);
  163. if (i != -1) {
  164. if (i == 0) {
  165. syslog(LOG_NOTICE, "connection closed");
  166. break;
  167. }
  168. buf[i] = 0;
  169. if (write(in, buf, strlen(buf)) < 0) {
  170. if (errno == EINTR)
  171. continue;
  172. syslog(LOG_ERR, "broken pipe");
  173. status = 1;
  174. break;
  175. }
  176. last_response = time(NULL);
  177. }
  178. }
  179. }
  180. close(sockfd);
  181. close(in);
  182. close(out);
  183. closelog();
  184. if (use_ssl) {
  185. ERR_free_strings();
  186. EVP_cleanup();
  187. SSL_shutdown(ssl);
  188. SSL_free(ssl);
  189. SSL_CTX_free(ctx);
  190. }
  191. snprintf(buf, 512, "/tmp/%s.in", host);
  192. unlink(buf);
  193. snprintf(buf, 512, "/tmp/%s.out", host);
  194. unlink(buf);
  195. return status;
  196. }