/* Monetra 5.x C API For any questions please contact support@mainstreetsoftworks.com Copyright (c) 2005, Main Street Softworks, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Main Street Softworks, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "libmonetra_main.h" #include "monetra.h" #ifndef DISABLE_IP int M_InitSockets () { #ifdef _WIN32 WSADATA wsaData; if (WSAStartup (0x202, &wsaData) == SOCKET_ERROR) { fprintf (stderr, "WSAStartup failed with error %d\n", WSAGetLastError ()); WSACleanup (); return 0; } #endif // _WIN32 return (1); } void M_CloseSocket (int fd) { #ifdef _WIN32 closesocket (fd); #else close (fd); #endif } int M_SetNonBlock (int fd, int tf) { #ifdef O_NONBLOCK int flags; if ((flags = fcntl (fd, F_GETFL, 0)) == -1) return (0); if (tf) flags |= O_NONBLOCK; else flags &= ~(O_NONBLOCK); if (fcntl (fd, F_SETFL, flags) == -1) return (0); #else if (ioctl (fd, FIONBIO, &tf) == -1) return (0); #endif return (1); } int M_Real_Connect (int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, M_CONN * myconn) { _M_CONN *conn = (_M_CONN *) (*myconn); int rc; int arglen; fd_set fds; struct timeval timeout; if (!conn->max_conn_time) { // Blocking mode, let OS decide when // to stop if (connect (sockfd, serv_addr, addrlen) == -1) { M_Set_Conn_Error (myconn, strerror (errno)); // Set // Error // Text return (0); } else { return (1); } } else { // Non-blocking mode, stop after maxtime // (seconds) if (!M_SetNonBlock (sockfd, 1)) { // Set non-block mode M_Set_Conn_Error (myconn, "Could not set Non-Blocking mode"); // Set // Error // Text return (0); } rc = connect (sockfd, serv_addr, addrlen); if (rc >= 0) { // connect() completed immediately M_SetNonBlock (sockfd, 0); // Set blocking mode return (1); #ifdef _WIN32 } else if (WSAGetLastError () != WSAEWOULDBLOCK) { M_Set_Conn_Error (myconn, "WinSock32 Unknown Error"); return (0); #else } else if (errno != EINPROGRESS) { // Error occurred M_Set_Conn_Error (myconn, strerror (errno)); // Set // Error // Text return (0); #endif } else { // Connect in progress FD_ZERO (&fds); FD_SET (sockfd, &fds); timeout.tv_sec = conn->max_conn_time; timeout.tv_usec = 0; rc = select (sockfd + 1, NULL, &fds, NULL, &timeout); if (rc == 0) { // Connection taking too long M_Set_Conn_Error (myconn, "Connection Timed Out"); // Set // Error // Text return (0); } else if (rc < 0) { // Error Occurred M_Set_Conn_Error (myconn, "Unknown error occurred"); // Set // Error // Text return (0); } else { // rc > 0 .. good so far... rc = 0; arglen = sizeof (int); if (getsockopt (sockfd, SOL_SOCKET, SO_ERROR, &rc, &arglen) < 0) rc = errno; if (rc) { M_Set_Conn_Error (myconn, strerror (rc)); // Set // Error // Text return (0); // Connect Failed } } } M_SetNonBlock (sockfd, 0); // Set blocking mode return (1); } return (0); // Should never get here... } long M_read (int fd, char *buf, long bytes) { #ifdef _WIN32 return (recv (fd, buf, bytes, 0)); #else return (read (fd, buf, bytes)); #endif } long M_write (int fd, char *buf, long bytes) { #ifdef _WIN32 return (send (fd, buf, bytes, 0)); #else return (write (fd, buf, bytes)); #endif } int M_IP_GetAddr (char *host, char **addr) { #if !defined(GETHOSTBYNAME_4) struct hostent hostbuf; #endif struct hostent *hp = NULL; size_t hstbuflen; char *tmphstbuf = NULL; int len; int ret; #if defined(GETHOSTBYNAME_3) struct hostent_data hostbuf_data; #elif !defined(GETHOSTBYNAME_4) int herr; #endif addr[0] = NULL; hstbuflen = 10000; tmphstbuf = malloc (hstbuflen); #if defined(GETHOSTBYNAME_1) gethostbyname_r (host, &hostbuf, tmphstbuf, hstbuflen, &hp, &herr); #elif defined(GETHOSTBYNAME_2) hp = gethostbyname_r (host, &hostbuf, tmphstbuf, hstbuflen, &herr); #elif defined(GETHOSTBYNAME_3) if (gethostbyname_r (host, &hostbuf, &hostbuf_data) == 0) { hp = &hostbuf; } else { hp = NULL; } #elif defined(GETHOSTBYNAME_4) hp = gethostbyname (host); #else #error NO GETHOSTBYNAME DEFINITION SPECIFIED #endif if (hp == NULL) { free (tmphstbuf); ret = -1; } else { len = hp->h_length; addr[0] = malloc ((len + 1) * sizeof (char)); memset (addr[0], 0, len + 1); memcpy (addr[0], hp->h_addr_list[0], len); free (tmphstbuf); ret = len; } return (ret); } int M_ip_connect (M_CONN * myconn) { _M_CONN *conn = (_M_CONN *) (*myconn); struct sockaddr_in peer; int soct; int len; char *addr = NULL; memset (&peer, 0, sizeof (peer)); peer.sin_family = AF_INET; peer.sin_port = htons (conn->port); len = M_IP_GetAddr (conn->location, &addr); if (len != -1) { memcpy ((char *) &peer.sin_addr, addr, len); free (addr); } else if (strcasecmp (conn->location, "localhost") == 0) { free (addr); peer.sin_addr.s_addr = inet_addr ("127.0.0.1"); } else { free (addr); peer.sin_addr.s_addr = inet_addr (conn->location); if (peer.sin_addr.s_addr == -1) { M_Set_Conn_Error (myconn, "DNS Lookup Failed"); return (-1); } } soct = socket (AF_INET, SOCK_STREAM, 0); M_Set_Conn_Error (myconn, strerror (errno)); // Set Error Text if (soct == -1) { return (-1); } if (!M_Real_Connect (soct, (struct sockaddr *) &peer, sizeof (peer), myconn)) { M_CloseSocket (soct); return (-1); } return (soct); } int M_VerifyPing (M_CONN * myconn) { _M_CONN *conn = (_M_CONN *) (*myconn); M_uintptr identifier = -1; time_t t, lastt; int status = 0; int blocking = 0; blocking = conn->blocking; M_SetBlocking (myconn, 0); identifier = M_Ping (myconn); time (&lastt); t = lastt; status = 1; while (M_CheckStatus (myconn, identifier) != M_DONE) { if (!M_Monitor (myconn)) { status = 0; break; } time (&t); if ((t - lastt) > 3) { // Response Timeout status = 0; break; } M_uwait (10000); } M_SetBlocking (myconn, blocking); M_DeleteResponse (myconn, identifier); if (!status) { return (0); } return (1); } // Let's see if there's anything in the buffer to read! int M_CheckRead (int ptr, long delay) { fd_set readfs; struct timeval timeout; int res; FD_ZERO (&readfs); FD_SET (ptr, &readfs); timeout.tv_sec = 0; timeout.tv_usec = delay; res = select (ptr + 1, &readfs, NULL, NULL, &timeout); if (res > 0) { if (FD_ISSET (ptr, &readfs)) { return (1); } } return (0); } // Let's make sure that the buffer isn't too full to write to // I'd assume this will only happen in dial-up scenerios int M_CheckWrite (int ptr, long delay) { fd_set sendfs; struct timeval timeout; int res; FD_ZERO (&sendfs); FD_SET (ptr, &sendfs); timeout.tv_sec = 0; timeout.tv_usec = delay; res = select (ptr + 1, NULL, &sendfs, NULL, &timeout); if (res > 0) { if (FD_ISSET (ptr, &sendfs)) { return (1); } } return (0); } #ifndef _WIN32 static FILE *M_openlog() { char debug_file[255]; FILE *fp=NULL; M_snprintf(debug_file, sizeof(debug_file), "/tmp/libmonetra-%d.log", getpid()); fp = fopen(debug_file, "ab"); return(fp); } static void M_output_buffer(FILE *fp, const char *prefix, const char *buf, int len) { int i,c; if (fp == NULL) return; for (i=0; i 126) c=32; fprintf(fp, "%s: %05d : %c -- HEX : 0x%0x DEC: %02d\n", prefix, len, c, buf[i], buf[i]); } } #endif #define IP_tempbuf_size 64*1024 int M_Monitor_IP (M_CONN * myconn) { _M_CONN *conn = (_M_CONN *) (*myconn); char *temp_buffer = NULL; #ifndef _WIN32 FILE *fp = NULL; #endif int bytes_read = 0; int bytes_written = 0; int status = 1; int outlen; size_t len; len = 0; #ifndef _WIN32 if (conn->do_debug) { fp = M_openlog(); } if (conn->do_debug && fp != NULL) { fprintf (fp, "Looking to read\n"); } #endif while (M_CheckRead (conn->ptr, 100) #ifdef ENABLE_SSL || ((conn->conn_method == M_SSL) && (SSL_pending ((SSL *) conn->ssl))) #endif ) { temp_buffer = (char *) malloc (IP_tempbuf_size + 1); if (conn->conn_method == M_SOCKETS) { bytes_read = M_read (conn->ptr, temp_buffer, IP_tempbuf_size); } else if (conn->conn_method == M_SSL) { #ifdef ENABLE_SSL bytes_read = SSL_read ((SSL *) conn->ssl, temp_buffer, IP_tempbuf_size); #endif } if (bytes_read > 0) { temp_buffer[bytes_read] = 0; } else { temp_buffer[0] = 0; } #ifndef _WIN32 if (conn->do_debug && fp != NULL) { fprintf (fp, "Read %d bytes\n", bytes_read); M_output_buffer(fp, "READ", temp_buffer, bytes_read); } #endif if (bytes_read <= 0) { // connection closed status = 0; free (temp_buffer); temp_buffer = NULL; break; } M_lock (myconn); while (conn->inbuf_alloc < (conn->inbuf_cnt + bytes_read + 1)) { conn->inbuf = (char *) realloc (conn->inbuf, conn->inbuf_alloc + IP_BLOCK_SIZE); memset (conn->inbuf + conn->inbuf_cnt, 0, IP_BLOCK_SIZE); conn->inbuf_alloc += IP_BLOCK_SIZE; } memcpy (conn->inbuf + conn->inbuf_cnt, temp_buffer, bytes_read); conn->inbuf_cnt += bytes_read; conn->inbuf[conn->inbuf_cnt] = 0; #ifndef _WIN32 if (conn->do_debug && fp != NULL) { fprintf(fp, "Full inbuffer size: %d\n", conn->inbuf_cnt); M_output_buffer(fp, "FULL INBUF", conn->inbuf, conn->inbuf_cnt); } #endif free (temp_buffer); temp_buffer = NULL; M_unlock (myconn); /* Don't loop if we didn't fill the buffer */ if (bytes_read < IP_tempbuf_size) break; } #ifndef _WIN32 if (conn->do_debug && fp != NULL) { fprintf (fp, "Looking to write\n"); } #endif M_lock (myconn); if (status && conn->outbuf_cnt != 0) { if (M_CheckWrite (conn->ptr, 100)) { if (IP_BLOCK_SIZE > conn->outbuf_cnt) outlen = conn->outbuf_cnt; else outlen = IP_BLOCK_SIZE; if (conn->conn_method == M_SOCKETS) { bytes_written = M_write (conn->ptr, conn->outbuf, outlen); } else if (conn->conn_method == M_SSL) { #ifdef ENABLE_SSL bytes_written = SSL_write ((SSL *) conn->ssl, conn->outbuf, outlen); if (bytes_written <= 0) { // connection closed status = 0; } #endif } #ifndef _WIN32 if (conn->do_debug && fp != NULL) { fprintf (fp, "Wrote %d bytes\n", outlen); M_output_buffer(fp, "WROTE", conn->outbuf, conn->outbuf_cnt); } #endif if (bytes_written < conn->outbuf_cnt) { memmove (conn->outbuf, conn->outbuf + bytes_written, conn->outbuf_cnt - bytes_written); conn->outbuf_cnt -= bytes_written; conn->outbuf[conn->outbuf_cnt] = 0; } else { free (conn->outbuf); conn->outbuf = NULL; conn->outbuf_cnt = 0; conn->outbuf_alloc = 0; } } } M_unlock (myconn); #ifndef _WIN32 if (conn->do_debug) { fclose (fp); } #endif return (status); } int M_SendTransaction_IP (M_CONN * myconn, char *identifier, char *transaction) { _M_CONN *conn = (_M_CONN *) (*myconn); int size; int ident_len = strlen (identifier); int msg_len = strlen (transaction); char *ptr = NULL; size = ident_len + msg_len + 3; M_lock (myconn); while (conn->outbuf_alloc < (conn->outbuf_cnt + size + 1)) { conn->outbuf = (char *) realloc (conn->outbuf, conn->outbuf_alloc + IP_BLOCK_SIZE); memset (conn->outbuf + conn->outbuf_cnt, 0, IP_BLOCK_SIZE); conn->outbuf_alloc += IP_BLOCK_SIZE; } ptr = conn->outbuf + conn->outbuf_cnt; *ptr = 0x02; ptr++; memcpy (ptr, identifier, ident_len); ptr += ident_len; *ptr = 0x1c; ptr++; memcpy (ptr, transaction, msg_len); ptr += msg_len; *ptr = 0x03; ptr++; conn->outbuf_cnt += size; conn->outbuf[conn->outbuf_cnt] = 0; M_unlock (myconn); return (1); } #endif /* DISABLE_IP */