/* tandemnw.c Tandem platform support for gSOAP. See README.txt and tandem.h for usage instructions. gSOAP XML Web services tools Copyright (C) 2000-2010, Robert van Engelen, Genivia Inc., All Rights Reserved. This part of the software is released under ONE of the following licenses: GPL or Genivia's license for commercial use. -------------------------------------------------------------------------------- GPL license. 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 Author contact information: engelen@genivia.com / engelen@acm.org This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. -------------------------------------------------------------------------------- A commercial use license is available from Genivia, Inc., contact@genivia.com -------------------------------------------------------------------------------- */ #include "stdsoap2.h" #include "tandem.h" /* process name on the Tandem machine, used to set up sockets */ static const char *procname = NULL; static int fsend(struct soap*, const char*, size_t); static size_t frecv(struct soap*, char*, size_t); static int tcp_connect(struct soap*, const char *endpoint, const char *host, int port); static int tcp_disconnect(struct soap*); static int tcp_closesocket(struct soap*, SOAP_SOCKET); static int tcp_shutdownsocket(struct soap*, SOAP_SOCKET, int); /** @fn void tandem_init(struct soap *soap, const char *name) @brief Initialize soap context for use on the Tandem platform. Immediately use after creating a new soap context. @param soap context @param[in] name of process or NULL */ void tandem_init(struct soap *soap, const char *name) { procname = name; /* set callbacks */ /* soap->fresolve = tcp_gethost; not needed */ soap->fopen = tcp_connect; soap->fclose = tcp_disconnect; soap->fclosesocket = tcp_closesocket; soap->fshutdownsocket = tcp_shutdownsocket; soap->fsend = fsend; soap->frecv = frecv; /* soap->fpoll = soap_poll; enable when needed */ #ifdef SOAP_DEBUG /* in the new gsoap 2.7.16 we override this here rather than in stdsoap2.c: */ soap_set_test_logfile(soap, "TESTlog"); soap_set_sent_logfile(soap, "SENTlog"); soap_set_recv_logfile(soap, "RECVlog"); #endif } /** @fn void tandem_done(struct soap *soap) @brief Disengages soap context for use on the Tandem platform. @param soap context */ void tandem_done(struct soap *soap) { /* TODO: cleanup as needed */ } /** @fn SOAP_SOCKET soap_bind(struct soap *soap, const char *host, int port, int backlog) @brief Binds to port. @param soap context @param[in] host name of machine (optional NULL) @param[in] port to bind @param[in] backlog queue size @return socket fd or -1 */ SOAP_SOCKET soap_bind(struct soap *soap, const char *host, int port, int backlog) { struct sockaddr_in sin; int retries = 50; short count; short err; int set = 1; long tag = 1; /* TODO: any reason to pick a special tag value? */ if (soap->master != -1) { soap->fclosesocket(soap, soap->master); soap->master = -1; } soap->socket = -1; /* TODO: check socket_nw params 0, 1, 0 */ if ((soap->master = socket_nw(AF_INET, SOCK_STREAM, 0, 1, 0)) < 0) { soap->errnum = errno; soap_set_receiver_error(soap, "", "TCP socket failed in soap_bind()", SOAP_TCP_ERROR); return -1; } AWAITIOX(&soap->master); /* optionally reuse address immediately */ if (setsockopt_nw(soap->master, SOL_SOCKET, SO_REUSEADDR, (char*)&set, sizeof(set), tag)) { soap->errnum = errno; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n")); soap_closesock(soap); soap_set_receiver_error(soap, "", "setsockopt failed in soap_bind()", SOAP_TCP_ERROR); } AWAITIOX(&soap->master); memset((void*)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = port; do { if (bind_nw(soap->master, (struct sockaddr*)&sin, (int)sizeof(sin), tag)) { soap->errnum = errno; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n")); soap_closesock(soap); soap_set_receiver_error(soap, "", "TCP bind failed in soap_bind()", SOAP_TCP_ERROR); return -1; } AWAITIOX(&soap->master, , &count, &tag); FILEINFO(soap->master, &err); /* error 4114 then retry */ if (err == 4114 && retries--) DELAY(1000); else if (err) { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not bind to host\n")); soap_closesock(soap); soap_set_receiver_error(soap, "", "TCP bind failed in soap_bind()", SOAP_TCP_ERROR); return -1; } } while (err); if (listen(soap->master, backlog)) { soap_closesock(soap); soap_set_receiver_error(soap, "", "TCP listen failed in soap_bind()", SOAP_TCP_ERROR); return -1; } /* TODO: AWAITIOX() needed here ?? */ return soap->master; } /** @fn SOAP_SOCKET soap_accept(struct soap *soap) @brief Accepts new socket when bound to port. @param soap context @return socket fd or -1 */ SOAP_SOCKET soap_accept(struct soap *soap) { struct sockaddr_in sin; int flen = sizeof(sin); short count; short err; long tag = 1; int val = SOAP_BUFLEN; soap->errnum = 0; memset((void*)&sin, 0, sizeof(sin)); /* retry until timeout, accept error, or success */ do { if (accept_nw(soap->master, (struct sockaddr*)&sin, &flen, tag) < 0) { soap_set_sender_error(soap, "", "TCP accept failed in soap_accept()", SOAP_TCP_ERROR); return -1; } /* TODO: need SETMODE(soap->master, 30, 3) here ?? */ if (soap->accept_timeout > 0) AWAITIOX(&soap->master, , &count, &tag, 100*soap->accept_timeout); else if (soap->accept_timeout < 0) AWAITIOX(&soap->master, , &count, &tag, (5000-soap->accept_timeout)/10000); else AWAITIOX(&soap->master, , &count, &tag); FILE_GETINFO_(soap->master, &err); if (err == 40) { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "TCP accept timed out\n")); return -1; } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "soap_accept() FILE_GETINFO result = %d\n", err)); } while (err); /* TODO: check socket_nw params 0, 1, 0 */ if ((soap->socket = socket_nw(AF_INET, SOCK_STREAM, 0, 1, 0)) >= 0) { /* TODO: AWAITIOX() ?? */ accept_nw2(soap->socket, (struct sockaddr*)&sin, tag); AWAITIOX(&soap->socket); setsockopt_nw(soap->socket, SOL_SOCKET, SO_RCVBUF, (char*)&val, sizeof(val), tag); AWAITIOX(&soap->socket); setsockopt_nw(soap->socket, SOL_SOCKET, SO_SNDBUF, (char*)&val, sizeof(val), tag); AWAITIOX(&soap->socket); /* need to check timeouts here too, as above? */ } return soap->socket; } /** @fn static size_t fsend(struct soap *soap, char *s, size_t n) @brief Callback override for soap->fsend to write data. @param soap context @param[in] s points to buffer to send @param[in] n size of buffer content @return SOAP_OK or error */ static int fsend(struct soap *soap, const char *s, size_t n) { if (soap->socket == -1) return SOAP_EOF; soap->errnum = 0; while (n) { short nwritten; short err; long tag = 1; if (send_nw2(soap->socket, (char*)s, n, 0, tag)) return SOAP_EOF; if (soap->send_timeout > 0) AWAITIOX(&soap->socket, , &nwritten, &tag, 100*soap->send_timeout); else if (soap->send_timeout < 0) AWAITIOX(&soap->socket, , &nwritten, &tag, (5000-soap->send_timeout)/10000); else AWAITIOX(&soap->socket, , &nwritten, &tag); FILE_GETINFO_(soap->socket, &err); if (err == 40) return SOAP_EOF; nwritten = socket_get_len(soap->socket); if (err > 0) { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "send() error %s\n", errno)); soap->errnum = errno; return SOAP_EOF; } n -= nwritten; s += nwritten; } return SOAP_OK; } /** @fn static size_t frecv(struct soap *soap, char *s, size_t n) @brief Callback override for soap->frecv to read data. @param soap context @param s points to buffer to populate @param[in] n size of buffer space @return number of bytes (less or equal to n) written into s */ static size_t frecv(struct soap *soap, char *s, size_t n) { if (soap->socket == -1) return 0; soap->errnum = 0; /* The loop is to retry as long as timout is not exceeded */ for (;;) { long tag = 1; short err; short r; if (recv_nw(soap->socket, s, n, 0, tag)) return 0; if (soap->recv_timeout > 0) AWAITIOX(&soap->socket, , &r, &tag, 100*soap->recv_timeout); else if (soap->recv_timeout < 0) AWAITIOX(&soap->socket, , &r, &tag, (5000-soap->recv_timeout)/10000); else AWAITIOX(&soap->socket, , &r, &tag); FILE_GETINFO_(soap->socket, &err); if (err == 40) break; if (r >= 0) return (size_t)r; if (err > 0) { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "recv() error %s\n", errno)); soap->errnum = errno; break; } } return 0; } /** @fn static int tcp_connect(struct soap *soap, const char *endpoint, const char *host, int port) @brief Callback override for soap->fopen to open a new connection. Also starts other communication-related objects such as SSL (when used). @param soap context @param[in] endpoint string @param[in] host name @param[in] port number @return fd of socket */ static int tcp_connect(struct soap *soap, const char *endpoint, const char *host, int port) { struct sockaddr_in sin; struct hostent *hostent; int val = SOAP_BUFLEN; short count; short err; long tag = 1; if (soap->socket != -1) soap->fclosesocket(soap, soap->socket); /* TODO: check socket_nw params 0, 1, 1 */ soap->socket = socket_nw(AF_INET, SOCK_STREAM, 0, 1, 1); soap->errnum = 0; if (soap->socket < 0) { soap->errnum = errno; soap_set_sender_error(soap, "", "TCP socket failed in tcp_connect()", SOAP_TCP_ERROR); return -1; } AWAITIOX(&soap->socket); memset((void*)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if ((sin.sin_addr.s_addr = inet_addr(host)) == INET_ERROR) { if (!(hostent = gethostbyname((char*)host))) { soap_set_sender_error(soap, "", "TCP get host by name failed in tcp_connect()", SOAP_TCP_ERROR); soap->fclosesocket(soap, soap->socket); return -1; } sin.sin_addr.s_addr = *(unsigned long*) (*(hostent->h_addr_list)); } else sin.sin_addr.s_addr = inet_addr(host); sin.sin_port = port; /* retry until timeout, connect error, or success */ do { if (connect_nw(soap->socket, (struct sockaddr*)&sin, (int)(sizeof(sin)), tag) < 0) { soap_set_sender_error(soap, "", "TCP connect failed in tcp_connect()", SOAP_TCP_ERROR); soap->fclosesocket(soap, soap->socket); return -1; } if (soap->connect_timeout > 0) AWAITIOX(&soap->socket, , , &tag, 100*soap->connect_timeout); else if (soap->connect_timeout < 0) AWAITIOX(&soap->socket, , , &tag, (5000-soap->connect_timeout)/10000); else AWAITIOX(&soap->socket); FILE_GETINFO_(soap->socket, &err); /* +++ More to do - what if there is an err? */ if (err == 40) { soap_set_sender_error(soap, "", "TCP connect timed out", SOAP_TCP_ERROR); soap->fclosesocket(soap, soap->socket); return -1; } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "tcp_connect() FILE_GETINFO result = %d\n", err)); } while (err); setsockopt_nw(soap->socket, SOL_SOCKET, SO_RCVBUF, (char*)&val, sizeof(val), tag); AWAITIOX(&soap->socket); setsockopt_nw(soap->socket, SOL_SOCKET, SO_SNDBUF, (char*)&val, sizeof(val), tag); AWAITIOX(&soap->socket); return soap->socket; } /** @fn static int tcp_disconnect(struct soap *soap) @brief Callback override for soap->fclose that closes the socket only when it is currently open. Also closes other communication-related objects such as SSL (when used). @param soap context @return SOAP_OK when successful */ static int tcp_disconnect(struct soap *soap) { if (soap->socket != -1) { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Closing socket %d\n", soap->socket)); soap->fclosesocket(soap, soap->socket); soap->socket = -1; } return SOAP_OK; } /** @fn static int tcp_closesocket(struct soap *soap, SOAP_SOCKET fd) @brief Callback override for soap->fclosesocket that closes the socket. @param soap context @param[in] fd of the socket @return value of close, 0 = successful */ static int tcp_closesocket(struct soap *soap, SOAP_SOCKET fd) { return FILE_CLOSE_(fd); } /** @fn static int tcp_shutdownsocket(struct soap *soap, SOAP_SOCKET fd, int how) @brief Callback override for soap->fshutdownsocket that shuts down socket read or write. May return 0 when shutdown operation is not supported on this platform. @param soap context @param[in] fd of the socket @param[in] how to shutdown the socket @return value of shutdown, 0 = successful */ static int tcp_shutdownsocket(struct soap *soap, SOAP_SOCKET fd, int how) { long tag = 1; if (shutdown_nw(fd, how, tag) < 0) return -1; AWAITIOX(&fd); return 0; }