/* 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; version 2 dated June, 1991. 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., 675 Mass Ave., Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_OPENSSL_SSL_H #include #include #endif #ifdef __QNX__ #include /* for select() et. al. */ #endif #include "config.h" #include "socklib.h" #define S_LIBRARY #define MG_SOCKLIB_C /* This source code was originally based on "The Linux A-Z" by Phil Cornes, chapter 18 - "Tiny Socket Library" ISBN 0-13-234709-1 The original source has been modified by Martin Domig */ SOCKET *Sopen(void) { SOCKET *sp; if ((sp = (SOCKET *)malloc(sizeof(SOCKET))) == NULL) return 0; /* Create a socket and store its descriptor in sp->sd. Declarations: AF_INET.......ARPA Internet protocol SOCK_STREAM...TCP-type connection 0.............IP Protocol Type See socket(2) for details. */ if ((sp->sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { free(sp); /* Oops, we got no socket! Free the mem and return error */ return 0; } sp->sinlen = sizeof(sp->sin); sp->bindflag = SOCKET_RESET; #ifdef HAVE_OPENSSL_SSL_H /* ssl is initialized after the tcp connection is opened */ sp->ssl = NULL; sp->sslBufSize = 0; #endif return sp; } int Sclose(SOCKET *sp) { int sd; if (!sp) return -1; sd = sp->sd; #ifdef HAVE_OPENSSL_SSL_H if (sp->ssl) { SSL_shutdown(sp->ssl); SSL_free(sp->ssl); } if (sp->sslBufSize) { free(sp->sslBufMalloc); sp->sslBufSize = 0; } #endif free(sp); return close(sd); } int Sserver(SOCKET *sp, int port, int sync) { int flags; struct hostent *hostent; /* struct hostent { char *h_name; official name of host char **h_aliases; alias list int h_addrtype; host address type int h_length; length of address char **h_addr_list; list of addresses } */ char localhost[HOST_NAMELEN+1]; /* We have to bind an address to the socket and then set up a queue to listen for connection requests. This should only be done the first time sserver() is called, and this is what sp->bindflag is used for. */ if (sp->bindflag == SOCKET_RESET) { /* Bind server to localhost */ if (gethostname(localhost, HOST_NAMELEN) == -1 || (hostent = gethostbyname(localhost)) == 0) return -1; sp->sin.sin_family = (short)hostent->h_addrtype; sp->sin.sin_port = htons((unsigned short)port); sp->sin.sin_addr.s_addr = *(unsigned long *)hostent->h_addr; if (bind(sp->sd, (struct sockaddr *)&sp->sin, sp->sinlen) == -1 || listen(sp->sd, 5) == -1) return -1; sp->bindflag = SOCKET_SET; /* All done and OK. Never bind this one again. */ } switch (sync) { case SOCKET_DELAY: if ((flags = fcntl(sp->sd, F_GETFL)) == -1 || fcntl(sp->sd, F_SETFL, flags & ~O_NDELAY) == -1) return -1; break; case SOCKET_NDELAY: if ((flags = fcntl(sp->sd, F_GETFL)) == -1 || fcntl(sp->sd, F_SETFL, flags | O_NDELAY) == -1) return -1; break; default: return -1; } return accept(sp->sd, (struct sockaddr *)&sp->sin, &sp->sinlen); } /* sclient tries to connect to a specified server on a given machine. The function waits until a connection with the server is established and then returns a socket descriptor connected to the server, -1 on error. */ int Sclient(SOCKET *sp, char *name, int port) { struct hostent *hostent; if ((hostent = gethostbyname(name)) == 0) return -1; sp->sin.sin_family = (short)hostent->h_addrtype; sp->sin.sin_port = htons((unsigned short)port); sp->sin.sin_addr.s_addr = *(unsigned long *)hostent->h_addr; if (connect(sp->sd, (struct sockaddr *)&sp->sin, sp->sinlen) == -1) return -1; return sp->sd; } #ifdef HAVE_OPENSSL_SSL_H /* modify an open socket to use SSL */ int Sslclient(SOCKET *sp, char *trustedCaDir) { SSL_METHOD *meth; SSL_CTX *ctx; BIO *sbio; X509 *peerCert; /* assert(sp contains an open socket) */ SSL_library_init(); meth = TLSv1_method(); ctx = SSL_CTX_new(meth); if (!ctx) return -1; sp->ssl = SSL_new(ctx); if (sp->ssl == NULL) return -1; /* don't check return value * in case of error, verification will fail later on */ SSL_CTX_load_verify_locations(ctx, NULL, trustedCaDir); sbio = BIO_new_socket(sp->sd, BIO_NOCLOSE); if (!sbio) { return -1; } SSL_set_bio(sp->ssl, sbio, sbio); /* read, write bio */ if (SSL_connect(sp->ssl) != 1) { return -1; } peerCert = SSL_get_peer_certificate(sp->ssl); if (!peerCert) { return -1; } X509_free(peerCert); if(SSL_get_verify_result(sp->ssl) != X509_V_OK) { printf("asmail warning: could not verify server certificate\n"); } return sp->sd; } #endif /* Read a maximum of n-1 characters from sd until a newline or a '\0' is received. string stores the input WITHOUT a ter- minating newline. Returns number of read characters, without terminating \0. HINT: pass the SIZE of the array as parameter for n. */ size_t Sread(int sd, char *string, int n, int timeout) { fd_set rfds; struct timeval tv; int ret, rcd, i; /* See man 3 select for details ;) NOTES Some code calls select with all three sets empty, n zero, and a non-null timeout as a fairly portable way to sleep with subsecond precision. On Linux, timeout is modified to reflect the amount of time not slept; most other implementations do not do this. This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple selects in a loop without reinitializing it. Consider timeout to be undefined after select returns. */ FD_ZERO(&rfds); FD_SET(sd, &rfds); i = 0; n--; do { tv.tv_sec = timeout; tv.tv_usec = 0; ret = select(sd+1, &rfds, NULL, NULL, &tv); if (ret > 0 && (!(rcd = read(sd, &string[i++], 1)))) { string[i] = '\0'; return 0; } } while (ret > 0 && rcd > 0 && string[i-1] != 13 && string[i-1] && i < n); if (ret < 0 || rcd < 0) { string[--i] = '\0'; return -1; } if (string[i-1] == 13) read(sd, &string[--i], 1); /* read 10 from sd */ string[i] = '\0'; return i; } size_t Swrite(int sd, char *string) { return write(sd, string, strlen(string)); } #ifdef HAVE_OPENSSL_SSL_H /* behaves exactly like Sread() above */ size_t Sslread(SOCKET *s, char *string, int n, int timeout) { size_t sizeRead; fd_set rfds; struct timeval tv; int ret; if (s->ssl == NULL) return -1; if (!s->sslBufSize) { /* empty buffer: read from SSL */ FD_ZERO(&rfds); FD_SET(s->sd, &rfds); tv.tv_sec = timeout; tv.tv_usec = 0; ret = select((s->sd)+1, &rfds, NULL, NULL, &tv); if (ret > 0) { s->sslBufMalloc = malloc(BUFSIZ * sizeof(char)); s->sslBuf = s->sslBufMalloc; if (!s->sslBuf) return -1; s->sslBufSize = SSL_read(s->ssl, s->sslBuf, BUFSIZ); if (s->sslBufSize <= 0) { free(s->sslBufMalloc); return -1; } } else return 0; } /* read from buffer */ sizeRead = 0; while (s->sslBufSize-- && (sizeRead++ < n-1)) { if ( ((*string++ = *(s->sslBuf)++) == 0x0d) && (*(s->sslBuf)++ == 0x0a) ) { s->sslBufSize--; /* for the 0x0a we just read */ string--; /* undo 0x0d in target string */ sizeRead--; break; } } if (!s->sslBufSize) free(s->sslBufMalloc); *string = 0x0; return sizeRead; } size_t Sslwrite(SOCKET *s, char *string) { if (s->ssl == NULL) return -1; return SSL_write(s->ssl, string, strlen(string)); } #endif