summaryrefslogtreecommitdiffstats
path: root/src/sockwrap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sockwrap.c')
-rw-r--r--src/sockwrap.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/sockwrap.c b/src/sockwrap.c
new file mode 100644
index 0000000..0d18fed
--- /dev/null
+++ b/src/sockwrap.c
@@ -0,0 +1,224 @@
+/* $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 <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <gnutls/gnutls.h>
+#include <string.h>
+#include <stdio.h>
+
+#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);
+}