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.

233 lines
4.6KB

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