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.

243 lines
4.8KB

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