/* $Id$ */ /*** This file is part of libnewmail libnewmail is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. libnewmail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libnewmail; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***/ #include #include #include #include #include #include #include #include #include "sockwrap.h" #include "util.h" struct sockwrap { int fd; gnutls_session tls; gnutls_certificate_credentials tls_cred; int use_tls; }; int _global_tls = 0; static void _atexit(void) { if (_global_tls) gnutls_global_deinit(); _global_tls = 0; } int sockwrap_readln(struct sockwrap *s, char *ln, int max) { char *p = ln; while (max > 1) { ssize_t r; if (!s->use_tls) { if ((r = read(s->fd, p, 1)) <= 0) return -1; } else { if ((r = gnutls_record_recv(s->tls, p, 1)) < 0) return -1; } max -= r; if (*p == '\n') { p += r; break; } p +=r; } *p = 0; //ßfprintf(stderr, "Read %i bytes\n", p-ln); return 0; } int sockwrap_writeln(struct sockwrap *s, char *ln) { char *p; ssize_t n; p = ln; n = strlen(p); while(n > 0) { ssize_t r; if (!s->use_tls) { if ((r = write(s->fd, p, n)) <= 0) return -1; } else { if ((r = gnutls_record_send(s->tls, ln, n)) <= 0) return -1; } n -= r; p += r; } return 0; } struct sockwrap* sockwrap(char *host, int port, int use_tls) { struct sockwrap* s = NULL; struct hostent *he; struct sockaddr_in sa; if (!(s = malloc(sizeof(struct sockwrap)))) { nm_error(NM_ERROR_MEMORY, NULL); goto finish; } s->fd = -1; s->tls = 0; s->tls_cred = 0; s->use_tls = use_tls; if ((s->fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) goto finish; if (!(he = gethostbyname(host))) { nm_error(NM_ERROR_SERVNOTFOUND|NM_ERROR_SYSTEM, NULL); goto finish; } sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr = *((struct in_addr *) he->h_addr); if (connect(s->fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { nm_error(NM_ERROR_SERVFAIL|NM_ERROR_SYSTEM, NULL); goto finish; } if (use_tls) { int ret; if (!_global_tls) { if ((ret = gnutls_global_init()) != 0) { nm_error(NM_ERROR_INTERNAL, gnutls_strerror(ret)); goto finish; } _global_tls = 1; atexit(_atexit); } if ((ret = gnutls_init(&s->tls, GNUTLS_CLIENT)) != 0) { nm_error(NM_ERROR_INTERNAL, gnutls_strerror(ret)); goto finish; } if ((ret = gnutls_set_default_priority(s->tls)) != 0) { nm_error(NM_ERROR_INTERNAL, gnutls_strerror(ret)); goto finish; } if ((ret = gnutls_certificate_allocate_sc(&s->tls_cred)) < 0) { nm_error(NM_ERROR_INTERNAL, gnutls_strerror(ret)); goto finish; } if ((ret = gnutls_cred_set(s->tls, GNUTLS_CRD_CERTIFICATE, s->tls_cred)) < 0) { nm_error(NM_ERROR_INTERNAL, gnutls_strerror(ret)); goto finish; } gnutls_transport_set_ptr(s->tls, (gnutls_transport_ptr) s->fd); if ((ret = gnutls_handshake(s->tls)) != 0) { nm_error(NM_ERROR_INTERNAL, gnutls_strerror(ret)); goto finish; } } return s; finish: if (s && s->use_tls) { if (s->tls && s->tls_cred) gnutls_bye(s->tls, GNUTLS_SHUT_RDWR); if (s->tls_cred) gnutls_certificate_free_credentials(s->tls_cred); if (s->tls) gnutls_deinit(s->tls); gnutls_global_deinit(); } if (s && s->fd >= 0) { shutdown(s->fd, SHUT_RDWR); close(s->fd); } free(s); return NULL; } void sockwrap_close(struct sockwrap *s) { if (!s) return; if (s->use_tls) { if (s->tls && s->tls_cred) gnutls_bye(s->tls, GNUTLS_SHUT_RDWR); if (s->tls_cred) gnutls_certificate_free_credentials(s->tls_cred); if (s->tls) gnutls_deinit(s->tls); } if (s->fd >= 0) { shutdown(s->fd, SHUT_RDWR); close(s->fd); } free(s); }