#ifdef AC_HAVE_ACETCP #include #include #include struct acetcp_descriptor { int fd; struct incoming_transaction_rec *in; struct outgoing_transaction_rec *out; char *server_challenge; }; static unsigned char *acetcp_response = 0, *acetcp_rp; static int acetcp_response_length; /* * This callback processes a single transaction that was just * received. In this case, we save it in some global variables * so that ac_partial_command_acetcp() can look at the value. * * incoming_transaction() cannot just return the data that it * received because in some contexts it may receive more than * one command. In this case, we limit it to just one, so we * know this trick of storing the result in a global is safe. */ void acetcp_process_incoming( char *s, int n, void *callback_cookie ) { acetcp_response_length = n-3; acetcp_response = malloc(n+1); if (! acetcp_response) messcrash("malloc failed"); acetcp_rp = acetcp_response + 3; /* skip length, point at type */ memcpy(acetcp_response, s, n); } /* * send a command that expects no response */ static void ac_lazy_command_acetcp(AC_DB db, char *command) { struct acetcp_descriptor *d; d = (struct acetcp_descriptor *) db->db_access; outgoing_transaction_send( d->out, 'n', command, strlen(command)+1); } /* * acetcp variant of ac_partial_command - this is compatible with * all the other transport's partial_command functions */ static void ac_partial_command_acetcp(AC_DB db, char *command, unsigned char **response, int *response_length, unsigned char **response_free, int *encore) { struct acetcp_descriptor *d; d = (struct acetcp_descriptor *) db->db_access; * encore = 0; /* * recognize the special command "encore" and make it ask for an * 'encore' transaction instead of a command transaction. This is a * yucky way to recognize it, but it is compatible with all the other * transports. */ if (strcmp(command,"encore") == 0) outgoing_transaction_send( d->out, 'e', command, strlen(command)+1); else outgoing_transaction_send( d->out, 't', command, strlen(command)+1); acetcp_response = 0; /* * eat up some data - keep trying to read responses until we get either * EOF or some actual data */ while (( incoming_transaction(d->in, d->fd, 1, acetcp_process_incoming, db->db_access ) == 0) && (acetcp_response == 0)) ; if (! acetcp_response) messcrash("no response from server - dropped connection?"); /* * if you implement authentication, you would check it here and * move acetcp_rp past the authentication block. */ /* * switch on transaction type */ switch (* acetcp_rp) { case 'C': /* * We do not expect the server to issue multiple challenges. */ messcrash("server challenged us again"); case 'a': /* * The server sends us an auth type message when it changes the * type of authentication it uses. If you implement authentication, * you would switch the auth type here and go back to process more * messages. */ messcrash("server selected auth type - not implememented in preliminary library "); case 'E': /* * This is a major error about the transport, not something like * "found 0 objects" or "unrecognized command". This is no * continuing after this. */ messcrash("server sent back error - %s",acetcp_rp+1); case 'R': /* * a real response. check it for validity and */ acetcp_rp++; /* skip transaction type */ *encore = *acetcp_rp++; /* fetch encore value (only R has encore) */ acetcp_response_length -= 2; *response = acetcp_rp; *response_length = acetcp_response_length; *response_free = acetcp_response; acetcp_response = 0 ; return; default: messcrash("unrecognized transaction type %02x %c",*acetcp_rp, *acetcp_rp); } return; /* not reached, but compiler doesn't know about messcrash */ } static void ac_close_acetcp(AC_DB db) { struct acetcp_descriptor *d; d = db->db_access; messfree(d->server_challenge); incoming_transaction_free(d->in); outgoing_transaction_free(d->out); close(d->fd); messfree (db->db_access) ; if (acetcp_response) free (acetcp_response) ; } /* * connect to the acetcp server */ static char *ac_open_acetcp(AC_DB db, char *protocol, char *host, int port ) { struct acetcp_descriptor *d; static char dyn_error[200]; int one = 1; int timeout = 0; while (! isdigit((int)*protocol) ) protocol++; if (*protocol == '/') protocol++; timeout = atoi(protocol); if ( timeout == 0 ) timeout = 300; /* unfortunately, we don't use the timeout in acetcp */ d = halloc(sizeof(struct acetcp_descriptor), 0); /* * the database specifier is "acetcp:host:port" or "a:host:port" * but thanks to HTTP, people want to write "acetcp://host:port". * I will let them. */ while (*host == '/') host++; /* * a blank host name means the local host */ if (*host == '\0') host = "localhost"; d->fd = connect_socket(host, port); if (d->fd < 0) return "000 connect to database server failed"; /* * it is important to set TCP_NODELAY on the socket. if you * do not, transactions will be VERY slow. Without TCP_NODELAY, * the system hopes you will write more data to the socket * very soon, so it does NOT transmit the data immediately -- it * wants to put the results of your next write in the same * packet. * * 6 is what getprotobyname returns for "tcp" - technically, I'm * cheating but TCP is _always_ protocol number 6 because that * is the IP protocol number for TCP. */ if (setsockopt( d->fd, 6, TCP_NODELAY , &one, sizeof(int)) < 0) { /* * this setsockopt can never fail, so it is ok to crash if it does. */ messcrash("could not set TCP_NODELAY"); } d->in = incoming_transaction_create(); d->out = outgoing_transaction_create(d->fd); db->db_access = d; db->close_transport = ac_close_acetcp; db->ac_partial_command = ac_partial_command_acetcp; db->lazy_command = ac_lazy_command_acetcp; /* * consume the challenge that the server will send us on connect. if * you implement authentication, store the challenge to use in later * packets. */ acetcp_response = 0; while (( incoming_transaction(d->in, d->fd, 1, acetcp_process_incoming, db->db_access ) == 0) && (acetcp_response == 0)) ; if (! acetcp_rp) return "server disconnect"; switch (* acetcp_rp) { case 'C': d->server_challenge = strnew((char *)acetcp_rp,0); break; case 'E': strncpy(dyn_error,(char *)acetcp_rp+1, 199); dyn_error[199] = 0; return dyn_error; default: return "server sent back unrecognizable response"; } return 0; } #else static char *ac_open_acetcp(AC_DB db, char *protocol, char *host, int port ) { return "000 acetcp not supported in this library"; } #endif