/* tclink.c - Library code for the TCLink client API. * * TCLink Copyright (c) 2003 TrustCommerce. * http://www.trustcommerce.com * developer@trustcommerce.com * (626) 744-7700 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "tclink.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OPENSSL_NO_KRB5 1 #include #include #include #include #include #include #define DEFAULT_HOST "gateway2048.trustcommerce.com" #define TIMEOUT 40 /* seconds */ #define TC_BUFF_MAX 16000 #define TC_LINE_MAX ((PARAM_MAX_LEN * 2) + 2) char *tclink_version = TCLINK_VERSION; /* TCLINK_VERSION is defined in Makefile */ char *tclink_host = DEFAULT_HOST; int tclink_port = 443; /*************************************************/ /* Data structures used only within this module. */ /*************************************************/ /* Variables used for transaction data. */ typedef struct param_data { char *name; char *value; struct param_data *next; } param; typedef struct _TCLinkCon { /* Connection data */ int *ip; int num_ips; int sd; /* SSL encryption */ X509 *tc_cert; SSL_METHOD *meth; SSL_CTX *ctx; SSL *ssl; /* Transaction parameters, sent and received */ param *send_param_list, *send_param_tail; param *recv_param_list; /* Connection status */ int is_error; int pass; time_t start_time; int dns; } TCLinkCon; /* The TrustCommerce certificate. */ enum { TC_CERT_SIZE = 952 }; unsigned char cert_data[TC_CERT_SIZE]={ 0x30,0x82,0x03,0xb4,0x30,0x82,0x02,0x9c,0x02,0x01,0x00,0x30,0x0d,0x06,0x09,0x2a, 0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x04,0x05,0x00,0x30,0x81,0x9f,0x31,0x0b,0x30, 0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06,0x03, 0x55,0x04,0x08,0x13,0x0a,0x43,0x61,0x6c,0x69,0x66,0x6f,0x72,0x6e,0x69,0x61,0x31, 0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x07,0x13,0x0b,0x4c,0x6f,0x73,0x20,0x41,0x6e, 0x67,0x65,0x6c,0x65,0x73,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0a,0x13,0x0d, 0x54,0x72,0x75,0x73,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x72,0x63,0x65,0x31,0x26,0x30, 0x24,0x06,0x03,0x55,0x04,0x03,0x13,0x1d,0x67,0x61,0x74,0x65,0x77,0x61,0x79,0x32, 0x30,0x34,0x38,0x2e,0x74,0x72,0x75,0x73,0x74,0x63,0x6f,0x6d,0x6d,0x65,0x72,0x63, 0x65,0x2e,0x63,0x6f,0x6d,0x31,0x25,0x30,0x23,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7, 0x0d,0x01,0x09,0x01,0x16,0x16,0x69,0x6e,0x66,0x6f,0x40,0x74,0x72,0x75,0x73,0x74, 0x63,0x6f,0x6d,0x6d,0x65,0x72,0x63,0x65,0x2e,0x63,0x6f,0x6d,0x30,0x1e,0x17,0x0d, 0x30,0x33,0x30,0x33,0x31,0x32,0x30,0x30,0x35,0x35,0x35,0x37,0x5a,0x17,0x0d,0x30, 0x33,0x30,0x34,0x31,0x31,0x30,0x30,0x35,0x35,0x35,0x37,0x5a,0x30,0x81,0x9f,0x31, 0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11, 0x06,0x03,0x55,0x04,0x08,0x13,0x0a,0x43,0x61,0x6c,0x69,0x66,0x6f,0x72,0x6e,0x69, 0x61,0x31,0x14,0x30,0x12,0x06,0x03,0x55,0x04,0x07,0x13,0x0b,0x4c,0x6f,0x73,0x20, 0x41,0x6e,0x67,0x65,0x6c,0x65,0x73,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x0a, 0x13,0x0d,0x54,0x72,0x75,0x73,0x74,0x43,0x6f,0x6d,0x6d,0x65,0x72,0x63,0x65,0x31, 0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x13,0x1d,0x67,0x61,0x74,0x65,0x77,0x61, 0x79,0x32,0x30,0x34,0x38,0x2e,0x74,0x72,0x75,0x73,0x74,0x63,0x6f,0x6d,0x6d,0x65, 0x72,0x63,0x65,0x2e,0x63,0x6f,0x6d,0x31,0x25,0x30,0x23,0x06,0x09,0x2a,0x86,0x48, 0x86,0xf7,0x0d,0x01,0x09,0x01,0x16,0x16,0x69,0x6e,0x66,0x6f,0x40,0x74,0x72,0x75, 0x73,0x74,0x63,0x6f,0x6d,0x6d,0x65,0x72,0x63,0x65,0x2e,0x63,0x6f,0x6d,0x30,0x82, 0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05, 0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xb7, 0x9e,0x49,0xa6,0xf2,0x34,0xc2,0x1c,0xbc,0x23,0xee,0x66,0x52,0x10,0x8a,0xba,0xf6, 0x6c,0x31,0x78,0x58,0x4e,0x44,0xce,0x40,0x0f,0x90,0x52,0x66,0x3e,0xa7,0x34,0xef, 0x31,0x24,0x13,0x73,0x20,0x33,0x4b,0xe2,0x3a,0x4d,0x4c,0xdd,0x48,0x8b,0xf4,0xb9, 0xdd,0x4e,0x54,0xc8,0x79,0xd3,0x13,0xac,0x73,0x93,0x3d,0x47,0x99,0x1a,0x5c,0x4a, 0xdb,0x0f,0x2d,0xf9,0xb5,0x5d,0x3e,0x91,0x15,0x4e,0x7d,0x7e,0x21,0x47,0xf2,0xb3, 0xcc,0xd3,0x7c,0xc4,0x2f,0x9e,0xfe,0xec,0xd5,0x0f,0xe7,0xc8,0x7e,0x7f,0xf6,0x32, 0xb8,0xac,0xff,0xac,0x8e,0xbf,0xb2,0xc1,0xaa,0xf3,0x66,0x4d,0x86,0x9a,0xd9,0xd4, 0x04,0x97,0x5a,0xc3,0xa3,0x33,0xc8,0x2e,0x4f,0x07,0xca,0x6f,0x1c,0x58,0xbd,0x00, 0x9e,0xe9,0xd8,0x3c,0xe2,0x87,0x80,0xb5,0x99,0x40,0x37,0x2f,0x24,0x05,0x4b,0xc2, 0xf9,0xe7,0x25,0x8e,0xfc,0x76,0xb1,0x76,0xde,0xf2,0xe6,0x5f,0x06,0x44,0x31,0x59, 0x1c,0xed,0x8b,0x1c,0x1c,0xb1,0x2f,0x48,0xf7,0xeb,0x78,0x73,0xa5,0x57,0xac,0xf0, 0x30,0x7f,0xdd,0x09,0xa8,0x7b,0xb5,0xc5,0x30,0x2c,0xcc,0x6a,0xaf,0x6e,0x98,0x6d, 0xa3,0x08,0xf8,0x94,0x0e,0x7a,0xad,0xe6,0xe5,0x27,0x01,0xc8,0x2f,0x01,0x0f,0x59, 0x87,0x3f,0x5f,0x6f,0x5f,0x2c,0x27,0x3d,0xe8,0x33,0x2e,0xc1,0x69,0xbb,0xe9,0xa0, 0xea,0xea,0x19,0x8b,0x9d,0x0f,0xa0,0x88,0x51,0x53,0xc1,0xf9,0x3d,0x41,0x9f,0xe2, 0xa8,0x66,0x0c,0xb3,0x21,0x64,0xd3,0x4b,0x81,0x91,0xd7,0x7d,0xce,0xc7,0xb5,0x02, 0x03,0x01,0x00,0x01,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01, 0x04,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x12,0x8d,0xa7,0x9b,0x9f,0x0b,0x8e,0x26, 0x8f,0xf2,0xf5,0x77,0xe2,0x71,0x85,0xd0,0x82,0x30,0x3f,0x44,0x68,0x6e,0xc6,0x4a, 0xe2,0x19,0x35,0x00,0x5e,0x6b,0xb1,0xd1,0xd4,0xd4,0x62,0x92,0xbe,0xc2,0x10,0x07, 0xbe,0x86,0x7b,0x8a,0xa5,0xe6,0xef,0x2b,0x6c,0x86,0x4d,0xc3,0x72,0x73,0xbf,0x68, 0x2a,0xaf,0xee,0x7d,0x97,0xad,0x20,0x3c,0xef,0x5c,0x83,0x78,0x01,0xf5,0xe5,0x08, 0x4f,0x33,0x52,0xaa,0xe6,0xb9,0x57,0xc9,0x26,0x48,0x59,0x20,0xe2,0x8b,0xdf,0xce, 0x49,0x6f,0x2f,0x7c,0x66,0x03,0x75,0x62,0x97,0x35,0xdf,0x23,0x8d,0x81,0xde,0x85, 0x9b,0x21,0x28,0x8b,0x74,0x35,0xa8,0x24,0x64,0x26,0x73,0xae,0xda,0xbd,0x81,0x2c, 0x87,0x97,0xd4,0xe6,0x0b,0x7e,0xcc,0x1b,0x1f,0xa9,0x6c,0x6a,0x70,0xbd,0x7f,0x1e, 0x9c,0x2d,0x7c,0xcc,0x62,0x7f,0x1a,0x93,0x12,0xaf,0xd9,0xd5,0xd8,0xe6,0x94,0xb7, 0x64,0x09,0x2c,0xc2,0xc5,0x5b,0x73,0x8f,0x4c,0x58,0x1b,0xbc,0x37,0x42,0x3c,0x4c, 0x37,0x7e,0x9e,0x79,0x1f,0xd1,0x0c,0xfe,0x54,0xc7,0x82,0x8f,0xde,0x87,0xf0,0xe2, 0x40,0x68,0x4f,0x47,0x03,0x0d,0x23,0x46,0xf2,0x67,0xb9,0x30,0x3b,0x42,0xe3,0x77, 0x40,0xa6,0xc2,0x9e,0x28,0xdf,0x28,0xda,0xfc,0xec,0xd0,0xc0,0x6a,0xd9,0x80,0x41, 0x60,0x5a,0xfb,0x24,0x23,0x2e,0x4d,0x50,0xb1,0xec,0xfb,0xb3,0x97,0xe2,0x4a,0x5a, 0xc7,0xda,0x12,0xbb,0x2a,0x6b,0x04,0xd6,0x90,0x91,0xe9,0xf7,0x5d,0x42,0x98,0xd7, 0x81,0x8a,0xd3,0x70,0x31,0xbd,0x11,0x4a}; /************************************* * Internal functions, not exported. * *************************************/ /* Random number from min to max. */ static int number(int min, int max) { return (rand() % (max - min + 1)) + min; } /* Safe string copy and append functions. */ #define SAFE_COPY(d, s) safe_copy((d), (s), sizeof(d)); #define SAFE_APPEND(d, s) safe_append((d), (s), sizeof(d)); void safe_copy(char *dst, const char *src, int size) { int len = strlen(src); if (len < size) strcpy(dst, src); else { strncpy(dst, src, size - 1); dst[size-1] = 0; } } void safe_append(char *dst, const char *src, int size) { int dlen = strlen(dst); int slen = strlen(src); int avail = size - dlen; if (avail < 1) return; if (slen < avail) strcpy(dst+dlen, src); else { strncpy(dst+dlen, src, avail - 1); dst[size-1] = 0; } } /* Add a parameter-value pair to the recieved list. */ static void AddRecvParam(TCLinkCon *c, const char *name, const char *value) { param *p; if (name[0] == 0 || value[0] == 0) return; p = (param *)malloc(sizeof(param)); p->name = strdup(name); p->value = strdup(value); p->next = c->recv_param_list; c->recv_param_list = p; } /* Add a string to the received list. */ static int AddRecvString(TCLinkCon *c, char *string) { char *ptr = strchr(string, '='); if (ptr == NULL) return 0; *ptr = 0; AddRecvParam(c, string, ptr+1); return 1; } /* Deallocate the send list. */ static void ClearSendList(TCLinkCon *c) { param *p, *next; for (p = c->send_param_list; p; p = next) { next = p->next; free(p->name); free(p->value); free(p); } c->send_param_list = c->send_param_tail = NULL; } /* Deallocate the recv list. */ static void ClearRecvList(TCLinkCon *c) { param *p, *next; for (p = c->recv_param_list; p; p = next) { next = p->next; free(p->name); free(p->value); free(p); } c->recv_param_list = NULL; } /* Open a socket to the host_ip specified. Returns the socket's file * descriptor on success (the open attempt is underway) or -1 for failure * (should never happen in practice). Note that this function DOES NOT block * and wait for the connection; you'll need to select() on the socket later to see * if it opened successfully. */ static int BeginConnection(TCLinkCon *c, int host_ip) { struct sockaddr_in sa; int sd; sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) return -1; fcntl(sd, F_SETFL, O_NONBLOCK); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = host_ip; sa.sin_port = htons(tclink_port); connect(sd, (struct sockaddr *) &sa, sizeof(sa)); return sd; } /* This function is called on a socket file descriptor once the connection has been * established and we're ready to negotiate SSL. If the SSL handshake fails for some * reason (such as the host on the other end not using SSL), it will return 0 for * failure. Success returns 1. */ static int FinishConnection(TCLinkCon *c, int sd) { int ssl_connected, is_error, errcode, res; X509 *server_cert; time_t start, remaining; fd_set in, out, err; struct timeval tv; /* check if socket has connected successfully */ int val; int /*socklen_t*/ size = 4; getsockopt(sd, SOL_SOCKET, SO_ERROR, &val, &size); if (val != 0) return 0; c->ssl = SSL_new(c->ctx); if (!c->ssl) return 0; FD_ZERO(&in); FD_SET((unsigned)sd, &in); FD_ZERO(&out); FD_SET((unsigned)sd, &out); FD_ZERO(&err); FD_SET((unsigned)sd, &err); SSL_set_fd(c->ssl, sd); ssl_connected = 0; is_error = 0; start = time(0); while (!ssl_connected && !is_error) { remaining = 5 - (time(0) - start); if (remaining <= 0) { is_error = 1; break; } res = SSL_connect(c->ssl); ssl_connected = ((res == 1) && SSL_is_init_finished(c->ssl)); if (!ssl_connected) { errcode = SSL_get_error(c->ssl, res); switch (errcode) { case SSL_ERROR_NONE: /* no error, we should have a connection, check again */ break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* no error, just wait for more data */ tv.tv_sec = remaining; tv.tv_usec = 0; if (select(sd+1, &in, &out, &err, &tv) < 0) is_error = 1; break; case SSL_ERROR_ZERO_RETURN: /* peer closed the connection */ case SSL_ERROR_SSL: /* error in SSL handshake */ default: is_error = 1; } } } if (is_error) { SSL_free(c->ssl); return 0; } fcntl(sd, F_SETFL, 0); /* make the socket blocking again */ /* verify that server certificate is authentic */ server_cert = SSL_get_peer_certificate(c->ssl); if (!server_cert || (X509_cmp(server_cert, c->tc_cert) != 0)) { SSL_free(c->ssl); return 0; } X509_free(server_cert); return 1; } /* This function should be called on list of socket file descriptors (sd) to determine * if any have opened successfully. If so, it will return which one (index into * the array). Otherwise it returns -1 if none have successfully opened. * This function will block for a maximum of 3 seconds. * As this function calls FinishConnection(), you shouldn't need to do anything special * after it returns success - the socket is set up and ready for use. */ static int CheckConnection(TCLinkCon *c, int *sd, int num_sd) { fd_set wr_set, err_set; struct timeval tv; int max_sd = -1, i; tv.tv_sec = 3; /* wait 3 seconds for soc->mething to happen */ tv.tv_usec = 0; /* build the fd_sets used for select() */ FD_ZERO(&wr_set); FD_ZERO(&err_set); for (i = 0; i < num_sd; i++) { if (sd[i] < 0) continue; FD_SET(sd[i], &wr_set); FD_SET(sd[i], &err_set); if (sd[i] > max_sd) max_sd = sd[i]; } /* run the select and see what we have waiting for us */ if (select(max_sd + 1, NULL, &wr_set, &err_set, &tv) < 1) return -1; /* I hope this never happens */ for (i = 0; i < num_sd; i++) if (sd[i] >= 0) { if (FD_ISSET(sd[i], &err_set)) { /* error - close the socket and mark it defunct */ close(sd[i]); sd[i] = -1; } else if (FD_ISSET(sd[i], &wr_set)) { /* socket has opened! try to negotiate SSL */ if (FinishConnection(c, sd[i])) { /* socket is ready to go, so return success */ c->sd = sd[i]; return i; } else { /* SSL handshake had errors, close the socket and mark it defunct */ close(sd[i]); sd[i] = -1; } } } /* if we get here, nothing much interesting happened during those 3 seconds */ return -1; } void do_SSL_randomize() { enum { RAND_VALS = 32 }; int randbuf[RAND_VALS]; char fname[512]; int use_rand_file; time_t t; int i, c; /* if they have a /dev/urandom we can skip this function */ if (RAND_status() != 0) return; t = time(0); RAND_seed((char *)&t, sizeof(time_t)); /* have they specified a random file with RANDFILE environment variable? */ use_rand_file = RAND_file_name(fname, sizeof(fname)) ? 1 : 0; if (use_rand_file) RAND_load_file(fname, 4096); /* stuff it with packets of random numbers until it is satisfied */ for (i = 0; i < 256 && RAND_status() == 0; i++) { for (c = 0; c < RAND_VALS; c++) randbuf[c] = rand(); RAND_seed((char *)randbuf, sizeof(int) * RAND_VALS); } } /* Open a connection to one of the TrustCommerce gateway servers. */ static int Connect(TCLinkCon *c, int host_hash) { struct hostent default_he; char *addr_list[4]; int addr[3]; struct hostent *he; unsigned int **gw; enum { MAX_HOSTS = 32 }; time_t last_connect[MAX_HOSTS]; int sd[MAX_HOSTS]; int num_sd = 0; int host; int i, j, sort, sort_val; unsigned char *cert_data_ptr = cert_data; c->sd = -1; c->is_error = 0; srand(time(0)); /* These are used as BACKUP ONLY if the DNS if offline. */ addr[0] = inet_addr("216.34.199.222"); addr[1] = inet_addr("216.120.83.124"); addr[2] = inet_addr("64.14.242.58"); addr_list[0] = (char *)&addr[0]; addr_list[1] = (char *)&addr[1]; addr_list[2] = (char *)&addr[2]; addr_list[3] = 0; default_he.h_addr_list = addr_list; /* determine IP addresses of gateway */ if (!c->ip) { he = gethostbyname(tclink_host); if (he) c->dns = 1; else { /* fall back to hardcoded IPs in an emergency */ c->dns = 0; he = &default_he; } for (c->num_ips = 0; he->h_addr_list[c->num_ips]; c->num_ips++) ; c->ip = (int *)malloc(c->num_ips * sizeof(int)); gw = (int unsigned **)he->h_addr_list; /* sort the IP address list before storing it */ for (i = 0; i < c->num_ips; i++) { sort = 0; sort_val = *gw[0]; for (j = 1; j < c->num_ips; j++) if (*gw[j] > sort_val) { sort = j; sort_val = *gw[j]; } c->ip[i] = sort_val; *gw[sort] = 0; } } /* do some SSL setup */ if (!c->meth) { do_SSL_randomize(); /* handle systems without /dev/urandom */ SSLeay_add_ssl_algorithms(); c->meth = SSLv3_client_method(); } if (!c->ctx) { c->ctx = SSL_CTX_new(c->meth); if (!c->ctx) return 0; } /* create the valid certificate */ if (c->tc_cert == NULL) { c->tc_cert = d2i_X509(NULL, &cert_data_ptr, TC_CERT_SIZE); if (!c->tc_cert) return 0; } /* This loop works as follows: * Grab the first host. Try to open a connection to it. If there was an * error (host down or unreachable) go to the next one. If nothing has happened * after 3 seconds, open a second socket (the first one is still open!) and try * with the next fail-over host. Continue to do this for a maximum of MAX_HOSTS * sockets, or until our TIMEOUT value runs out. We also keep track of how recently * we tried to connect to a given host, so that we avoid saturating the machines * in a heavy-load situation (which could be caused by anything from heavy internet * lag between the local host and the TrustCommerce servers, to heavy load on the * servers themselves due to half a million people trying to run credit card * transactions in the same half second - unlikely, but certainly possible.) */ c->start_time = time(0); c->pass = 1; memset(last_connect, 0, MAX_HOSTS * sizeof(time_t)); host = host_hash % c->num_ips; for ( ; time(0) < (c->start_time + TIMEOUT); c->pass++) { /* retry the first host at least once */ if (c->pass > 2) host += 1; if (host >= c->num_ips) host = 0; /* only connect if we haven't tried this host before, or it's been a little * while (note random modifier to help stagger network traffic) */ if (last_connect[host] == 0 || (time(0) - last_connect[host]) >= number(TIMEOUT / 4, TIMEOUT)) { if (num_sd < MAX_HOSTS) { /* fire up a new connection to this host */ if (c->pass != 1) last_connect[host] = time(0); sd[num_sd] = BeginConnection(c, c->ip[host]); if (sd[num_sd] >= 0) num_sd++; } /* scan all current sockets and see if we've made a successful connection * somewhere. note that this also includes SSL and all that sort of fun, * so once it returns success, we're all done. */ if (num_sd > 0) if (CheckConnection(c, sd, num_sd) >= 0) { /* Success: close all other file handles and return */ for (i = 0; i < num_sd; i++) if (sd[i] >= 0 && sd[i] != c->sd) close(sd[i]); return 1; } } } return 0; } /* Send a chunk of data through a connection previously opened with Connect(). */ static int Send(TCLinkCon *c, const char *string) { if (SSL_write(c->ssl, string, strlen(string)) < 0) return 0; return 1; } /* Peel a line off the current input. Note that this DOESN'T necessarily wait for all * input to come in, only up to a "\n". -1 is returned for a network error, otherwise * it returns the length of the line read. If there is not a complete line pending * for read this will block until there is, or an error occurs. */ static int ReadLine(TCLinkCon *c, char *buffer, char *destbuf) { struct timeval tv; fd_set read; fd_set error; while (1) /* we wait for a line to come in or an error to occur */ { char *eol = strchr(buffer, '\n'); if (eol != NULL) { /* peel off the line and return it */ *eol++ = 0; safe_copy(destbuf, buffer, TC_LINE_MAX); memmove(buffer, eol, strlen(eol)+1); return strlen(destbuf); } else { if (c->is_error == 1) return -1; /* do socket work to grab the most recent chunk of incoming data */ FD_ZERO(&read); FD_SET(c->sd, &read); FD_ZERO(&error); FD_SET(c->sd, &error); tv.tv_sec = TIMEOUT; tv.tv_usec = 0; if (select(c->sd + 1, &read, NULL, &error, &tv) < 1) c->is_error = 1; else if (FD_ISSET(c->sd, &error)) c->is_error = 1; else if (FD_ISSET(c->sd, &read)) { int buffer_end = strlen(buffer); int size = SSL_read(c->ssl, buffer + buffer_end, TC_BUFF_MAX-1 - buffer_end); if (size < 0) c->is_error = 1; else buffer[buffer_end + size] = 0; } } } } /* Closes a connection opened with Connect() and frees memory associated with it. * You ONLY need to Close() connections which opened successfully; those that don't * clean up after themselves before Connect() returns. */ static int Close(TCLinkCon *c) { if (c->ssl) SSL_shutdown(c->ssl); if (c->sd >= 0) { close(c->sd); c->sd = -1; } if (c->ssl) { SSL_free(c->ssl); c->ssl = NULL; } if (c->ctx) { SSL_CTX_free(c->ctx); c->ctx = NULL; } /* We DON'T free c->meth or c->tc_cert here, because they can be * * reused by other transactions run on this same TCLinkHandle. */ return 1; } /********************************************** * API functions exported to the user client. * **********************************************/ TCLinkHandle TCLinkCreate() { TCLinkCon *c = (TCLinkCon *)malloc(sizeof(TCLinkCon)); c->ip = NULL; c->num_ips = 0; c->sd = -1; c->tc_cert = NULL; c->meth = NULL; c->ctx = NULL; c->ssl = NULL; c->send_param_list = NULL; c->send_param_tail = NULL; c->recv_param_list = NULL; c->is_error = 0; c->pass = 0; c->start_time = 0; c->dns = -1; return (TCLinkHandle)c; } void TCLinkPushParam(TCLinkHandle handle, const char *name, const char *value) { param *p; char *ch; TCLinkCon *c = (TCLinkCon *)handle; if (name && value) { p = (param *)malloc(sizeof(param)); p->name = strdup(name); p->value = strdup(value); p->next = NULL; if (c->send_param_tail) c->send_param_tail->next = p; else c->send_param_list = p; c->send_param_tail = p; /* remove newlines and equals signs from the parameter name */ for (ch = p->name; *ch; ch++) if (*ch == '=' || *ch == '\n') *ch = ' '; /* remove newlines from the value */ for (ch = p->value; *ch; ch++) if (*ch == '\n') *ch = ' '; } } void TCLinkSend(TCLinkHandle handle) { param *p, *next; char buf[TC_BUFF_MAX], destbuf[TC_LINE_MAX]; char buf2[1024]; int host_hash = 1; int retval = 0; TCLinkCon *c = (TCLinkCon *)handle; ClearRecvList(c); /* build most of the string we will send to the processor */ sprintf(buf, "BEGIN\nversion=%s\n", tclink_version); for (p = c->send_param_list; p; p = next) { next = p->next; SAFE_COPY(buf2, p->name); SAFE_APPEND(buf2, "="); SAFE_APPEND(buf2, p->value); SAFE_APPEND(buf2, "\n"); SAFE_APPEND(buf, buf2); if (!strcasecmp(p->name, "custid")) { host_hash = atoi(p->value); host_hash = (host_hash / 100) + (host_hash % 100); } free(p->name); free(p->value); free(p); } c->send_param_list = c->send_param_tail = NULL; /* try to make the connection */ if (!Connect(c, host_hash)) { Close(c); /* clean up any memory Connect() may have left lying around */ AddRecvParam(c, "status", "error"); AddRecvParam(c, "errortype", "cantconnect"); return; } /* append some data about the connection */ sprintf(buf+strlen(buf), "pass=%d\ntime=%ld\n", c->pass, time(0) - c->start_time); if (c->dns != 1) SAFE_APPEND(buf, "dns=n\n"); SAFE_APPEND(buf, "END\n"); /* send the data */ if (Send(c, buf)) { int state = 0; buf[0] = destbuf[0] = 0; /* recycle buf */ c->is_error = 0; while (1) { int len = ReadLine(c, buf, destbuf); if (len == 0) continue; if (len < 0) break; if (strcasecmp(destbuf, "BEGIN") == 0) { if (state != 0) { state = -1; break; } state = 1; } else if (strcasecmp(destbuf, "END") == 0) { if (state != 1) state = -1; else state = 2; break; } else { if (state != 1 || !AddRecvString(c, destbuf)) { state = -1; break; } } } if (state == 2) retval = 1; } Close(c); if (!retval) { ClearRecvList(c); AddRecvParam(c, "status", "error"); AddRecvParam(c, "errortype", "linkfailure"); } } char *TCLinkGetResponse(TCLinkHandle handle, const char *name, char *value) { param *p; TCLinkCon *c = (TCLinkCon *)handle; for (p = c->recv_param_list; p; p = p->next) if (strcasecmp(name, p->name) == 0) { safe_copy(value, p->value, PARAM_MAX_LEN); return value; } return NULL; } static void stuff_string(char *buf, int *len, int size, const char *add) { int newlen = strlen(add); if ((*len + newlen) >= size) newlen = size - *len - 1; if (newlen < 1) return; strncpy(buf + *len, add, newlen); *len += newlen; buf[*len] = 0; } char *TCLinkGetEntireResponse(TCLinkHandle handle, char *buf, int size) { param *p; int len = 0; TCLinkCon *c = (TCLinkCon *)handle; for (p = c->recv_param_list; p; p = p->next) { stuff_string(buf, &len, size, p->name); stuff_string(buf, &len, size, "="); stuff_string(buf, &len, size, p->value); stuff_string(buf, &len, size, "\n"); } return buf; } void TCLinkDestroy(TCLinkHandle handle) { TCLinkCon *c = (TCLinkCon *)handle; if (!c) return; ClearSendList(c); ClearRecvList(c); Close(c); if (c->ip) free(c->ip); if (c->tc_cert) X509_free(c->tc_cert); free(c); } char *TCLinkGetVersion(char *buf) { strcpy(buf, tclink_version); return buf; }