/* * Salamander (CCTL) Supplier/Client Interface Module * * G. Robert Malan (rmalan@eecs.umich.edu). * 2/18/97 * * This code represents the interface library bewteen the Corona suppliers * and clients, and a CCTL server. * * $Id: salamanderInterface.c,v 1.14 1997/10/11 02:21:46 rmalan Exp $ */ #include #include #include #include #include #include #include #include #include #include "propertyList.h" #include "salamanderInterface.h" #define DEBUG 0 #define disconnect_return(retval) { disconnectFromSalamanderServer();return(retval); } /***************************************************************************** * * Globals: * *****************************************************************************/ static int connected = 0; /* Are we currently connected? */ static int connection_fd; static u_short connection_port; static char thisInterfaceVersion[4] = SAL_INTERFACE_VERSION; /* * These are used to identify a server incarnations. */ static u_long server_key; /***************************************************************************** * * Internal Routines: * *****************************************************************************/ /** * waitForResponse: * * Wait for an I/O response. * * timeout is in seconds. * * returns 0 if timeout occurs; 1 if data ready to read on readfd. */ static int waitForResponse(int readfd, int timeout) { int ret; fd_set read_fds; struct timeval tv; /* We want to poll the descriptors, must use zeroed timeval. */ tv.tv_sec = timeout; tv.tv_usec = 0; FD_ZERO(&read_fds); FD_SET(readfd, &read_fds); ret = select(readfd + 1, &read_fds, 0, 0, &tv); if (ret < 0) { #if VERBOSE fprintf(stderr, "waitForResponse: select error\n"); #endif /* VERBOSE */ return ret; } /* No data ready. */ if (ret == 0) return 0; if (!FD_ISSET(readfd, &read_fds)) { #if VERBOSE fprintf(stderr, "waitForResponse: select returned invalid fd."); #endif /* VERBOSE */ return 0; } return 1; } /** * getCTLPort: * * Used to get the current CCTL TCP control port number via the well-known * UDP port. * */ static int getSalamanderPort(char * hostname, u_short * tcp_port) { int timeout; int ret; u_long * lptr; #define SALAMANDER_MAX_UDPMESG 128 char message_buf[SALAMANDER_MAX_UDPMESG]; int sock_fd; struct sockaddr_in serv_udp_addr, cli_udp_addr; struct hostent * host; /* * Create the UDP socket ports for the query. */ /* Setup the server's socket structure from argument hostinfo */ bzero((char *)&serv_udp_addr, sizeof(struct sockaddr_in)); if ((serv_udp_addr.sin_addr.s_addr = inet_addr(hostname)) == -1) { if ((host = gethostbyname(hostname)) == NULL) { return (SALAMANDER_HOST_UNKNOWN); } serv_udp_addr.sin_family = host->h_addrtype; bcopy(host->h_addr, (caddr_t) &serv_udp_addr.sin_addr, host->h_length); }else{ serv_udp_addr.sin_family = AF_INET; } serv_udp_addr.sin_port = htons(SALAMANDER_UDP_PORT); if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return(SALAMANDER_LOCAL_ERROR); bzero((char *) &cli_udp_addr, sizeof(cli_udp_addr)); cli_udp_addr.sin_family = AF_INET; cli_udp_addr.sin_addr.s_addr = htonl(INADDR_ANY); cli_udp_addr.sin_port = htons(0); if (bind(sock_fd, (struct sockaddr *) &cli_udp_addr, sizeof(cli_udp_addr)) < 0) { close (sock_fd); return(SALAMANDER_LOCAL_ERROR); } /* * Compose message: * * Query for current server's TCP connection port. */ bzero((char *)&message_buf[0], sizeof(message_buf)); lptr = (u_long *)&message_buf[0]; *lptr = htonl(SALAMANDER_QUERY_TCP_PORT); /* Tries ten times with a max timeout of 10 minutes. */ for (timeout = 1; timeout < 10*60; timeout *= 2) { /* Send the message. */ ret = sendto(sock_fd, message_buf, sizeof(message_buf), 0, (struct sockaddr *)&serv_udp_addr, sizeof(serv_udp_addr)); if (ret != sizeof(message_buf)) { perror("getSalamanderPort: error on sendto."); close(sock_fd); return (SALAMANDER_LOCAL_ERROR); } ret = waitForResponse(sock_fd, timeout); /* Timeout occurred. */ if (!ret) continue; if (ret < 0) { close (sock_fd); return(SALAMANDER_LOCAL_ERROR); } break; } /* Timed out */ if (timeout > 10*60) { close(sock_fd); return (SALAMANDER_SERVER_TIMEOUT); } /* We have something! */ ret = recvfrom(sock_fd, message_buf, sizeof(message_buf), 0, 0, 0); if (ret < 0) { perror("getSalamanderPort: recvfrom error"); close(sock_fd); return(SALAMANDER_LOCAL_ERROR); } /* We're done with UDP. Close it up. * XXX Look out on server side for dup messages that generate errors! */ close(sock_fd); ret = *((u_short *)(&message_buf[0])); *tcp_port = ntohs(ret); lptr = (u_long *)&message_buf[0]; lptr++; server_key = *lptr++; /* Don't bother converting from netorder */ #if DEBUG fprintf(stderr, "getSalamanderPort: serverkey %d, %d\n", ntohl(server_key1)); #endif DEBUG return SALAMANDER_OK; } /** * createTCPConnection: * */ static int createTCPConnection(char * hostname, u_short connect_port, int * return_fd) { int ret; struct sockaddr_in serv_addr; struct hostent * host; /* Setup the server's socket structure from argument hostinfo */ bzero((char *)&serv_addr, sizeof(struct sockaddr_in)); if ((serv_addr.sin_addr.s_addr = inet_addr(hostname)) == -1) { if ((host = gethostbyname(hostname)) == NULL) { return (SALAMANDER_HOST_UNKNOWN); } serv_addr.sin_family = host->h_addrtype; bcopy(host->h_addr, (caddr_t) &serv_addr.sin_addr, host->h_length); }else{ serv_addr.sin_family = AF_INET; } serv_addr.sin_port = htons(connect_port); if ((*return_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return(SALAMANDER_LOCAL_ERROR); if (connect(*return_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in)) < 0) { perror("createTCPConnection: connect failed"); close(*return_fd); return(SALAMANDER_REMOTE_ERROR); } return (SALAMANDER_OK); } /** * readPropertyHeader: * */ static plist_t readPropertyHeader(int readfd) { int len; int ret; u_long headerbuf; u_char salInterfaceVersion[4]; plist_t plist; char keybuf[MAX_PROP_LEN]; char valbuf[MAX_PROP_LEN]; u_long plist_length; char * marshalledPlist; /* * Read the KEY: */ len = sizeof(u_long); if (readn(readfd, (char *)&headerbuf, len) != len) { #if VERBOSE fprintf(stderr, "readPropertyHeader: couldn't read key.\n"); #endif /* VERBOSE */ close(readfd); return 0; } if (headerbuf != server_key) { #if VERBOSE fprintf(stderr, "readPropertyHeader: invalid key.\n"); #endif /* VERBOSE */ close(readfd); return 0; } /* * Read the TYPE: */ len = sizeof(salInterfaceVersion); if (readn(readfd, (char *)salInterfaceVersion, len) != len) { #if VERBOSE fprintf(stderr, "readPropertyHeader: couldn't read type.\n"); #endif VERBOSE close(readfd); return 0; } if (salInterfaceVersion[0] != thisInterfaceVersion[0]) { #if VERBOSE fprintf(stderr, "readPropertyHeader: interface mismatch %c %c\n",salInterfaceVersion[0], thisInterfaceVersion[0]); #endif VERBOSE close(readfd); return 0; } /* * Read LENGTH: */ len = sizeof(plist_length); if (readn(readfd, (char *)&plist_length, len) != len) { #if VERBOSE fprintf(stderr, "readPropertyHeader: couldn't read length.\n"); #endif VERBOSE close(readfd); return 0; } plist_length = ntohl(plist_length); /* * Read VALUE: */ if (!(marshalledPlist = (char *)malloc(plist_length))) { #if VERBOSE fprintf(stderr, "readPropertyHeader: out of memory.\n"); #endif /* VERBOSE */ close(readfd); return 0; } if (readn(readfd, marshalledPlist, plist_length) != plist_length) { #if VERBOSE fprintf(stderr, "readPropertyHeader: couldn't read plist.\n"); #endif VERBOSE close(readfd); return 0; } if (!(plist = unmarshallPlist(marshalledPlist, plist_length))) { #if VERBOSE fprintf(stderr, "readPropertyHeader: couldn't unmarshall plist.\n"); #endif /* VERBOSE */ close(readfd); return 0; } free(marshalledPlist); return (plist); } /** * readDataObject: * */ static int readDataObject(plist_t plist, void ** data, u_long * data_length) { int ret; char *curkey; char *curval; *data = NULL; *data_length = 0; if (curval = getProperty(plist, OBJ_SIZE_PROPERTY)) { *data_length = atoi(curval); #if DEBUG fprintf(stderr, "readDataObject: datalength = %d\n", *data_length); #endif DEBUG if (*data_length > 0) { *data = (char *)malloc(*data_length); if (readn(connection_fd, *data, *data_length) != *data_length) { #if VERBOSE fprintf(stderr, "readDataObject: error reading data.\n"); #endif /* VERBOSE */ close(connection_fd); free(curval); return (SALAMANDER_REMOTE_ERROR); } } } else { #if DEBUG fprintf(stderr, "readDataObject: no OBJ_SIZE_PROPERTY in plist.\n"); #endif DEBUG } return (SALAMANDER_OK); } /***************************************************************************** * * Exported Routines for BOTH Suppliers and Clients: * *****************************************************************************/ /* * connectToSalamanderServer: * * An exported routine. * */ int connectToSalamanderServer(char * hostname, u_long sskey) { int len; int ret; u_short tcp_port; struct sockaddr_in serv_addr; if (connected) return(SALAMANDER_ALREADY_CONNECTED); /* * Figure out which TCP port the server is listening on. */ ret = getSalamanderPort(hostname, &connection_port); if (ret != SALAMANDER_OK) return(ret); /* * Setup the TCP control connection. */ ret = createTCPConnection(hostname, connection_port, &connection_fd); if (ret != SALAMANDER_OK) return(ret); #if DEBUG fprintf(stderr, "connectToSalamanderServer: connected to connection_port (%d)\n", connection_port); #endif DEBUG /* Write the sskey. */ sskey = htonl(sskey); len = sizeof(u_long); if (writen(connection_fd, (char *)&sskey, len) != len) { close(connection_fd); return(SALAMANDER_SEND_ERROR); } connected = 1; return (SALAMANDER_OK); } void disconnectFromSalamanderServer(void) { if (!connected) return; if (connection_fd) close(connection_fd); connected = 0; } /***************************************************************************** * * External I/O Routines: * *****************************************************************************/ /** * salamanderSendServerData: * * Takes a data object and its associated property list and sends it to the * connected salamander server. * * This is VERY simple blocking send interface to the salamander server. * If your application needs more control over it's data delivery, please * use an alternate interface. */ int salamanderSendServerData(plist_t plist, void * data, u_long data_length) { int ret, len; plist_entry_t cookie; char lenbuf[20]; char * curkey; char * curvalue; char * packagedPlist; unsigned long plist_length; unsigned long aLong; if (!plist) return(SALAMANDER_INVALID_PLIST); /* Add or override the transport layer property of object length */ sprintf(lenbuf, "%d", data_length); if (!(ret = updateProperty(plist, OBJ_SIZE_PROPERTY, lenbuf))) disconnect_return(SALAMANDER_MEMORY_ERROR); /* * Write the TYPE: */ len = sizeof(server_key); if (writen(connection_fd, (char *)&server_key, len) != len) disconnect_return(SALAMANDER_SEND_ERROR); len = sizeof(thisInterfaceVersion); if (writen(connection_fd, (char *)thisInterfaceVersion, len) != len) disconnect_return(SALAMANDER_SEND_ERROR); if (!(packagedPlist = marshallPlist(plist, &plist_length))) disconnect_return(SALAMANDER_MEMORY_ERROR); /* * Write the LENGTH (length reflects ONLY the plist length): */ len = sizeof(plist_length); aLong = htonl(plist_length); if (writen(connection_fd, (char *)&aLong, len) != len) disconnect_return(SALAMANDER_SEND_ERROR); /* * Write the VALUE: */ if (writen(connection_fd, packagedPlist, plist_length) != plist_length) disconnect_return(SALAMANDER_SEND_ERROR); free(packagedPlist); /* * Only write data, if we have any. */ if (data_length) { /* * Write the actual object data... */ if (writen(connection_fd, (char *)data, data_length) != data_length) disconnect_return(SALAMANDER_SEND_ERROR); } return (SALAMANDER_OK); } /** * salamanderReadServerData: * */ int salamanderReadServerData(plist_t *plist, void **data, u_long *data_length) { int ret; plist_t ret_plist; char *curkey; char *curval; plist_entry_t cookie; if (!(*plist = readPropertyHeader(connection_fd))) return (SALAMANDER_REMOTE_ERROR); /*XXX aren't sure. */ if (readDataObject(*plist, data, data_length) != SALAMANDER_OK) { destroyPropertyList(*plist); return(SALAMANDER_REMOTE_ERROR); /*XXX aren't sure. */ } return (SALAMANDER_OK); }