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.

230 lines
4.5KB

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