PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/socket.c

https://github.com/booo/imapfilter
C | 438 lines | 334 code | 80 blank | 24 comment | 92 complexity | 410bc3697c4516e41e8e89c1310bd539 MD5 | raw file
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <strings.h>
  5. #include <errno.h>
  6. #include <netinet/in.h>
  7. #include <netdb.h>
  8. #include <sys/socket.h>
  9. #include <sys/types.h>
  10. #include <sys/time.h>
  11. #include <sys/select.h>
  12. #include <openssl/ssl.h>
  13. #include <openssl/err.h>
  14. #include "imapfilter.h"
  15. #include "session.h"
  16. /*
  17. * Connect to mail server.
  18. */
  19. int
  20. open_connection(session *ssn)
  21. {
  22. struct addrinfo hints, *res, *ressave;
  23. int n, sockfd;
  24. memset(&hints, 0, sizeof(struct addrinfo));
  25. hints.ai_family = AF_UNSPEC;
  26. hints.ai_socktype = SOCK_STREAM;
  27. n = getaddrinfo(ssn->server, ssn->port, &hints, &res);
  28. if (n < 0) {
  29. error("gettaddrinfo; %s\n", gai_strerror(n));
  30. return -1;
  31. }
  32. ressave = res;
  33. sockfd = -1;
  34. while (res) {
  35. sockfd = socket(res->ai_family, res->ai_socktype,
  36. res->ai_protocol);
  37. if (sockfd >= 0) {
  38. if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
  39. break;
  40. sockfd = -1;
  41. }
  42. res = res->ai_next;
  43. }
  44. if (ressave)
  45. freeaddrinfo(ressave);
  46. if (sockfd == -1) {
  47. error("error while initiating connection to %s at port %s\n",
  48. ssn->server, ssn->port);
  49. return -1;
  50. }
  51. ssn->socket = sockfd;
  52. if (ssn->sslproto) {
  53. if (open_secure_connection(ssn) == -1) {
  54. close_connection(ssn);
  55. return -1;
  56. }
  57. }
  58. return ssn->socket;
  59. }
  60. /*
  61. * Initialize SSL/TLS connection.
  62. */
  63. int
  64. open_secure_connection(session *ssn)
  65. {
  66. int r, e;
  67. SSL_CTX *ctx;
  68. #if OPENSSL_VERSION_NUMBER >= 0x1000000fL
  69. const SSL_METHOD *method;
  70. #else
  71. SSL_METHOD *method;
  72. #endif
  73. method = NULL;
  74. if (ssn->sslproto && (!strncasecmp(ssn->sslproto, "ssl3", 4) ||
  75. !strncasecmp(ssn->sslproto, "ssl2", 4)))
  76. method = SSLv23_client_method();
  77. else
  78. method = TLSv1_client_method();
  79. if (!(ctx = SSL_CTX_new(method)))
  80. goto fail;
  81. if (!(ssn->sslconn = SSL_new(ctx)))
  82. goto fail;
  83. SSL_set_fd(ssn->sslconn, ssn->socket);
  84. for (;;) {
  85. if ((r = SSL_connect(ssn->sslconn)) > 0)
  86. break;
  87. switch (SSL_get_error(ssn->sslconn, r)) {
  88. case SSL_ERROR_ZERO_RETURN:
  89. error("initiating SSL connection to %s; the "
  90. "connection has been closed cleanly\n",
  91. ssn->server);
  92. goto fail;
  93. case SSL_ERROR_NONE:
  94. case SSL_ERROR_WANT_CONNECT:
  95. case SSL_ERROR_WANT_ACCEPT:
  96. case SSL_ERROR_WANT_X509_LOOKUP:
  97. case SSL_ERROR_WANT_READ:
  98. case SSL_ERROR_WANT_WRITE:
  99. break;
  100. case SSL_ERROR_SYSCALL:
  101. e = ERR_get_error();
  102. if (e == 0 && r == 0)
  103. error("initiating SSL connection to %s; EOF in "
  104. "violation of the protocol\n", ssn->server);
  105. else if (e == 0 && r == -1)
  106. error("initiating SSL connection to %s; %s\n",
  107. ssn->server, strerror(errno));
  108. else
  109. error("initiating SSL connection to %s; %s\n",
  110. ssn->server, ERR_error_string(e, NULL));
  111. goto fail;
  112. case SSL_ERROR_SSL:
  113. error("initiating SSL connection to %s; %s\n",
  114. ssn->server, ERR_error_string(ERR_get_error(),
  115. NULL));
  116. goto fail;
  117. default:
  118. break;
  119. }
  120. }
  121. if (get_option_boolean("certificates") && get_cert(ssn) == -1)
  122. goto fail;
  123. SSL_CTX_free(ctx);
  124. return 0;
  125. fail:
  126. ssn->sslconn = NULL;
  127. SSL_CTX_free(ctx);
  128. return -1;
  129. }
  130. /*
  131. * Disconnect from mail server.
  132. */
  133. int
  134. close_connection(session *ssn)
  135. {
  136. int r;
  137. r = 0;
  138. close_secure_connection(ssn);
  139. if (ssn->socket != -1) {
  140. r = close(ssn->socket);
  141. ssn->socket = -1;
  142. if (r == -1)
  143. error("closing socket; %s\n", strerror(errno));
  144. }
  145. return r;
  146. }
  147. /*
  148. * Shutdown SSL/TLS connection.
  149. */
  150. int
  151. close_secure_connection(session *ssn)
  152. {
  153. if (ssn->sslconn) {
  154. SSL_shutdown(ssn->sslconn);
  155. SSL_free(ssn->sslconn);
  156. ssn->sslconn = NULL;
  157. }
  158. return 0;
  159. }
  160. /*
  161. * Read data from socket.
  162. */
  163. ssize_t
  164. socket_read(session *ssn, char *buf, size_t len, long timeout, int timeoutfail)
  165. {
  166. int s;
  167. ssize_t r;
  168. fd_set fds;
  169. struct timeval tv;
  170. struct timeval *tvp;
  171. r = 0;
  172. s = 1;
  173. tvp = NULL;
  174. memset(buf, 0, len + 1);
  175. if (timeout > 0) {
  176. tv.tv_sec = timeout;
  177. tv.tv_usec = 0;
  178. tvp = &tv;
  179. }
  180. FD_ZERO(&fds);
  181. FD_SET(ssn->socket, &fds);
  182. if (ssn->sslconn) {
  183. if (SSL_pending(ssn->sslconn) > 0 ||
  184. ((s = select(ssn->socket + 1, &fds, NULL, NULL, tvp)) > 0 &&
  185. FD_ISSET(ssn->socket, &fds))) {
  186. r = socket_secure_read(ssn, buf, len);
  187. if (r <= 0)
  188. goto fail;
  189. }
  190. } else {
  191. if ((s = select(ssn->socket + 1, &fds, NULL, NULL, tvp)) > 0 &&
  192. FD_ISSET(ssn->socket, &fds)) {
  193. r = read(ssn->socket, buf, len);
  194. if (r == -1) {
  195. error("reading data; %s\n", strerror(errno));
  196. goto fail;
  197. } else if (r == 0) {
  198. goto fail;
  199. }
  200. }
  201. }
  202. if (s == -1) {
  203. error("waiting to read from socket; %s\n", strerror(errno));
  204. goto fail;
  205. } else if (s == 0 && timeoutfail) {
  206. error("timeout period expired while waiting to read data\n");
  207. goto fail;
  208. }
  209. return r;
  210. fail:
  211. close_connection(ssn);
  212. return -1;
  213. }
  214. /*
  215. * Read data from a TLS/SSL connection.
  216. */
  217. ssize_t
  218. socket_secure_read(session *ssn, char *buf, size_t len)
  219. {
  220. int r, e;
  221. for (;;) {
  222. if ((r = (ssize_t) SSL_read(ssn->sslconn, buf, len)) > 0)
  223. break;
  224. switch (SSL_get_error(ssn->sslconn, r)) {
  225. case SSL_ERROR_ZERO_RETURN:
  226. error("reading data through SSL; the connection has "
  227. "been closed cleanly\n");
  228. goto fail;
  229. case SSL_ERROR_NONE:
  230. case SSL_ERROR_WANT_READ:
  231. case SSL_ERROR_WANT_WRITE:
  232. case SSL_ERROR_WANT_CONNECT:
  233. case SSL_ERROR_WANT_ACCEPT:
  234. case SSL_ERROR_WANT_X509_LOOKUP:
  235. break;
  236. case SSL_ERROR_SYSCALL:
  237. e = ERR_get_error();
  238. if (e == 0 && r == 0)
  239. error("reading data through SSL; EOF in "
  240. "violation of the protocol\n");
  241. else if (e == 0 && r == -1)
  242. error("reading data through SSL; %s\n",
  243. strerror(errno));
  244. else
  245. error("reading data through SSL; %s\n",
  246. ERR_error_string(e, NULL));
  247. goto fail;
  248. case SSL_ERROR_SSL:
  249. error("reading data through SSL; %s\n",
  250. ERR_error_string(ERR_get_error(), NULL));
  251. goto fail;
  252. default:
  253. break;
  254. }
  255. }
  256. return r;
  257. fail:
  258. SSL_set_shutdown(ssn->sslconn, SSL_SENT_SHUTDOWN |
  259. SSL_RECEIVED_SHUTDOWN);
  260. return -1;
  261. }
  262. /*
  263. * Write data to socket.
  264. */
  265. ssize_t
  266. socket_write(session *ssn, const char *buf, size_t len)
  267. {
  268. int s;
  269. ssize_t r, t;
  270. fd_set fds;
  271. r = t = 0;
  272. s = 1;
  273. FD_ZERO(&fds);
  274. FD_SET(ssn->socket, &fds);
  275. while (len) {
  276. if ((s = select(ssn->socket + 1, NULL, &fds, NULL, NULL) > 0 &&
  277. FD_ISSET(ssn->socket, &fds))) {
  278. if (ssn->sslconn) {
  279. r = socket_secure_write(ssn, buf, len);
  280. if (r <= 0)
  281. goto fail;
  282. } else {
  283. r = write(ssn->socket, buf, len);
  284. if (r == -1) {
  285. error("writing data; %s\n",
  286. strerror(errno));
  287. goto fail;
  288. } else if (r == 0) {
  289. goto fail;
  290. }
  291. }
  292. if (r > 0) {
  293. len -= r;
  294. buf += r;
  295. t += r;
  296. }
  297. }
  298. }
  299. if (s == -1) {
  300. error("waiting to write to socket; %s\n", strerror(errno));
  301. goto fail;
  302. } else if (s == 0) {
  303. error("timeout period expired while waiting to write data\n");
  304. goto fail;
  305. }
  306. return t;
  307. fail:
  308. close_connection(ssn);
  309. return -1;
  310. }
  311. /*
  312. * Write data to a TLS/SSL connection.
  313. */
  314. ssize_t
  315. socket_secure_write(session *ssn, const char *buf, size_t len)
  316. {
  317. int r, e;
  318. for (;;) {
  319. if ((r = (ssize_t) SSL_write(ssn->sslconn, buf, len)) > 0)
  320. break;
  321. switch (SSL_get_error(ssn->sslconn, r)) {
  322. case SSL_ERROR_ZERO_RETURN:
  323. error("writing data through SSL; the connection has "
  324. "been closed cleanly\n");
  325. goto fail;
  326. case SSL_ERROR_NONE:
  327. case SSL_ERROR_WANT_READ:
  328. case SSL_ERROR_WANT_WRITE:
  329. case SSL_ERROR_WANT_CONNECT:
  330. case SSL_ERROR_WANT_ACCEPT:
  331. case SSL_ERROR_WANT_X509_LOOKUP:
  332. break;
  333. case SSL_ERROR_SYSCALL:
  334. e = ERR_get_error();
  335. if (e == 0 && r == 0)
  336. error("writing data through SSL; EOF in "
  337. "violation of the protocol\n");
  338. else if (e == 0 && r == -1)
  339. error("writing data through SSL; %s\n",
  340. strerror(errno));
  341. else
  342. error("writing data through SSL; %s\n",
  343. ERR_error_string(e, NULL));
  344. goto fail;
  345. case SSL_ERROR_SSL:
  346. error("writing data through SSL; %s\n",
  347. ERR_error_string(ERR_get_error(), NULL));
  348. goto fail;
  349. default:
  350. break;
  351. }
  352. }
  353. return r;
  354. fail:
  355. SSL_set_shutdown(ssn->sslconn, SSL_SENT_SHUTDOWN |
  356. SSL_RECEIVED_SHUTDOWN);
  357. return -1;
  358. }