/* $Header: /CVSROOT/sslconnect/sslconnect.c,v 1.7 2007-10-09 20:55:15 tino Exp $ * * Connect to destination via SSL * * Copyright (C)2006 Valentin Hilbig * This shall be independent of tinolib. * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * $Log: sslconnect.c,v $ * Revision 1.7 2007-10-09 20:55:15 tino * Secondary bugfix: * Error message is not appended to output if there was some. * * Revision 1.6 2007-10-09 20:41:45 tino * Timeout bug correction * * Revision 1.5 2007-04-23 22:24:05 tino * Timeout calculation in select changed to reset to full value after successful select. * * Revision 1.4 2007/04/23 22:16:46 tino * Timeout added * * Revision 1.3 2006/11/30 23:23:00 tino * Error message can span multiple command line args. * * Revision 1.2 2006/11/30 23:22:07 tino * New version with error replacement string */ #include #include #include #include #include #include #include #include #include #include #include #define OPENSSL_NO_KRB5 #include /* SSLeay stuff */ #include #include #include #include #include #include "sslconnect_version.h" #if 0 #define DP(X) do { tino_debug_printf X; } while (0) static void tino_debug_printf(const char *s, ...) { va_list list; int e; e = errno; fprintf(stderr, "[["); va_start(list, s); vfprintf(stderr, s, list); va_end(list); fprintf(stderr, "]]\n"); errno = e; } #else #define DP(X) do { ; } while (0) #endif #define xDP(X) do { ; } while (0) static SSL *ssl; static SSL_CTX *ssl_ctx; static int sock; static int errorstrs, timeout; static char **errorstr; static void pes(void) { int i; for (i=0; ++i<=errorstrs; ) printf("%s%c", errorstr[i-1], (isin_family = AF_INET; sin->sin_addr.s_addr = htonl(INADDR_ANY); sin->sin_port = htons(port); if (host && !inet_aton(host, &sin->sin_addr)) { struct hostent *he; if ((he=gethostbyname(host))==0) ex(host); if (he->h_addrtype!=AF_INET || he->h_length!=sizeof sin->sin_addr) ex("unsupported host address"); memcpy(&sin->sin_addr, he->h_addr, sizeof sin->sin_addr); } } static void sockopen(const char *host, int port) { struct sockaddr_in sin; int on; struct linger linger; sock = socket( AF_INET, SOCK_STREAM, 0 ); if (sock<0) ex("socket"); on = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on)) ex("setsockopt reuse"); linger.l_onoff = 1; linger.l_linger = 65535; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof linger)) ex("setsockopt linger"); on = 102400; if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &on, sizeof on)) ex("setsockopt sndbuf"); on = 102400; if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &on, sizeof on)) ex("setsockopt rcvbuf"); #if 0 /* 2002-05-09 set the MTU * The kernel takes a lower value if it is not supported ;) * We add some bytes for the tcp packet overhead. */ if (maxblock<1500) { mss = maxblock+48; if(setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss))) ex("setsockopt maxseg"); } #endif getaddr(&sin, host, port); if (bind(sock, (struct sockaddr *)&sin, sizeof sin)) ex("bind"); #if 0 if (fcntl(sock, F_SETFL, O_NDELAY)) ex("O_NDELAY"); #endif } static void do_connect(const char *host, int port) { struct sockaddr_in sin; do_timeout(1); sockopen(NULL, 0); do_timeout(1); getaddr(&sin, host, port); do_timeout(1); if (connect(sock, (struct sockaddr *)&sin, sizeof sin)) ex("connect"); do_timeout(0); } static int sslerror(int n) { int e; e = errno; DP(("sslerror(%d)", n)); n = SSL_get_error(ssl, n); DP(("sslerror %d(%d)", n, errno)); switch (n) { case SSL_ERROR_SYSCALL: if (e==EAGAIN || e==EINTR) return 0; if (e) { perror("sslerror"); DP(("sslerror syscall")); return -1; } case SSL_ERROR_ZERO_RETURN: DP(("sslerror EOF")); return -1; case SSL_ERROR_NONE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: return 0; } DP(("sslerror unknown")); return -1; } static int doread(int fd, void *ptr, int len) { int n; DP(("doread(%d,%p,%d)", fd, ptr, len)); if (fd!=sock) { n = read(fd, ptr, len); if (!n) n = -1; else if (n<0 && (errno==EINTR || errno==EAGAIN)) n = 0; } else { n = SSL_read(ssl, ptr, len); if (n<=0) n = sslerror(n); } DP(("doread %d", n)); return n; } static int dowrite(int fd, const void *ptr, int len) { int n; DP(("dowrite(%d,%p,%d)", fd, ptr, len)); if (fd!=sock) { n = write(fd, ptr, len); if (!n) n = -1; else if (n<0 && (errno==EINTR || errno==EAGAIN)) n = 0; } else { n = SSL_write(ssl, ptr, len); if (n<=0) n = sslerror(n); } DP(("dowrite %d", n)); return n; } struct iobuf { int pos, fill, max; const char *name; char ptr[1]; }; static void buf_clr(struct iobuf *buf) { buf->pos = 0; buf->fill = 0; } static struct iobuf * buf_new(const char *name, int max) { struct iobuf *tmp; tmp = malloc(sizeof(tmp)+max); if (!tmp) ex("out of memory"); buf_clr(tmp); tmp->max = max; tmp->name = name; return tmp; } static int buf_free(struct iobuf *buf) { int n; if (!buf) return 0; n = buf->max-buf->fill; xDP(("buf_free(%s) %d", buf->name, n)); return n; } static int buf_fill(struct iobuf *buf) { int n; if (!buf) return 0; n = buf->fill-buf->pos; xDP(("buf_fill(%s) %d", buf->name, n)); return n; } static char * buf_head(struct iobuf *buf) { if (!buf) ex("buf_head"); xDP(("buf_head(%s)", buf->name)); return buf->ptr+buf->pos; } static char * buf_end(struct iobuf *buf) { if (!buf) ex("buf_end"); xDP(("buf_end(%s)", buf->name)); return buf->ptr+buf->fill; } static int buf_shift(struct iobuf *buf) { xDP(("buf_shift(%s[%d,%d,%d])", buf->name, buf->pos, buf->fill, buf->max)); if (buf->pos>buf->fill || buf->fill>buf->max) ex("buf_shift"); if (!buf->pos || (buf->fillmax && buf->posfill)) return 0; /* We hit the end of the buffer * but there is free room at the beginning. * Move it down. */ if (buf->pos < buf->fill) memmove(buf->ptr, buf->ptr+buf->pos, buf->fill-buf->pos); buf->fill -= buf->pos; buf->pos = 0; xDP(("buf_shift %s[%d,%d,%d]", buf->name, buf->pos, buf->fill, buf->max)); return 0; } static int buf_read(struct iobuf *buf, int fd) { int n; if (!buf) return -1; DP(("buf_read(%s,%d)", buf->name, fd)); n = doread(fd, buf_end(buf), buf_free(buf)); if (n<0) return -1; nuke_error(); buf->fill += n; return buf_shift(buf); } static int buf_write(struct iobuf *buf, int fd) { int n; if (!buf) return -1; DP(("buf_write(%s,%d)", buf->name, fd)); n = dowrite(fd, buf_head(buf), buf_fill(buf)); if (n<0) return -1; nuke_error(); buf->pos += n; return buf_shift(buf); } static void loop(int in, int out) { fd_set rd; fd_set wr; int fds; struct iobuf *ibuf, *obuf; int cur; fds = sock; if (fds=0 && (in>=0 || buf_fill(obuf) || (sock>=0 && buf_fill(ibuf)))) { int n; struct timeval tv; DP(("loop %d,%d,%d,%p,%p", in, out, sock, ibuf, obuf)); FD_ZERO(&rd); FD_ZERO(&wr); if (in>=0 && buf_free(ibuf)) { DP(("rd %d", in)); FD_SET(in, &rd); } if (out>=0 && buf_fill(obuf)) { DP(("wr %d", out)); FD_SET(out, &wr); } if (sock>=0 && buf_fill(ibuf)) { DP(("wr %d", sock)); FD_SET(sock, &wr); } if (sock>=0 && buf_free(obuf)) { DP(("rd %d", sock)); FD_SET(sock, &rd); } tv.tv_sec = cur; tv.tv_usec= 0; DP(("select")); if ((n=select(fds, &rd, &wr, NULL, timeout ? &tv : NULL))<0) { if (errno==EINTR || errno==EAGAIN) continue; ex("select"); } if (!n) { cur /= 2; if (!cur) ex("timeout"); continue; } cur = timeout; DP(("rd %d", sock)); if (sock>=0 && FD_ISSET(sock, &rd) && buf_read(obuf, sock)) { close(sock); sock = -1; } DP(("wr %d", sock)); if (sock<0) buf_clr(ibuf); else if (FD_ISSET(sock, &wr) && buf_write(ibuf, sock)) { close(sock); sock = -1; } if (sock<0 && in>=0) { shutdown(in, SHUT_RD); close(in); in = -1; ibuf = 0; } DP(("in %d", in)); if (in>=0 && FD_ISSET(in, &rd) && buf_read(ibuf, in)) { close(in); in = -1; } DP(("out %d", out)); if (out<0) buf_clr(obuf); else if (FD_ISSET(out, &wr) && buf_write(obuf, out)) { close(out); out = -1; obuf = 0; } } } int main(int argc, char **argv) { if (argc>1 && argv[1][0]=='-') { argc--; timeout = strtoul(*++argv+1, NULL, 0); } if (argc<3) { fprintf(stderr, "Usage: sslconnect [-timeout] host port [error message]\n" "\t\tVersion " SSLCONNECT_VERSION " compiled " __DATE__ "\n" "\tstdin must be socket! Idle timeout is twice the timeout.\n"); return 1; } errorstrs = argc-3; errorstr = argv+3; if (timeout) signal(SIGALRM, timeout_exit); do_connect(argv[1], atoi(argv[2])); tino_ssl_client(); loop(0, 1); tino_ssl_close(); return 0; }