/* ** Copyright (C) 2004-2007 by Carnegie Mellon University. ** ** @OPENSOURCE_HEADER_START@ ** ** Use of the SILK system and related source code is subject to the terms ** of the following licenses: ** ** GNU Public License (GPL) Rights pursuant to Version 2, June 1991 ** Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013 ** ** NO WARRANTY ** ** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER ** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY ** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN ** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY ** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT ** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE ** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT, ** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY ** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF ** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. ** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF ** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON ** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE ** DELIVERABLES UNDER THIS LICENSE. ** ** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie ** Mellon University, its trustees, officers, employees, and agents from ** all claims or demands made against them (and any related losses, ** expenses, or attorney's fees) arising out of, or relating to Licensee's ** and/or its sub licensees' negligent use or willful misuse of or ** negligent conduct or willful misconduct regarding the Software, ** facilities, or other rights or assistance granted by Carnegie Mellon ** University under this License, including, but not limited to, any ** claims of product liability, personal injury, death, damage to ** property, or violation of any laws or regulations. ** ** Carnegie Mellon University Software Engineering Institute authored ** documents are sponsored by the U.S. Department of Defense under ** Contract F19628-00-C-0003. Carnegie Mellon University retains ** copyrights in all material produced under this contract. The U.S. ** Government retains a non-exclusive, royalty-free license to publish or ** reproduce these documents, or allow others to do so, for U.S. ** Government purposes only pursuant to the copyright license under the ** contract clause at 252.227.7013. ** ** @OPENSOURCE_HEADER_END@ */ /* ** Implements sender and receiver servers. ** */ #include "silk.h" RCSIDENT("$SiLK: skt-server.c 6070 2007-01-22 18:03:49Z mthomas $"); #include "skt-private.h" #define TRANSFER_SYSTEM_ERROR(transfer, message) \ { \ int saveerrno = errno; \ if ((transfer)->logfn) { \ (transfer)->logfn((message)); \ } \ if ((transfer)->callback) { \ errno = saveerrno; \ (transfer)->callback(NULL, NULL, SK_TRANSFER_ESYSTEM, (transfer)); \ } \ goto end; \ } static void server_thread_callback(char *file, void *item, sktErr_t err, skTransfer_t transfer) { switch (err) { case SK_TRANSFER_ESUCCESS: case SK_TRANSFER_EFATAL: case SK_TRANSFER_ESHUTDOWN_EXTERNAL: /* Pass back to the user handler and continue */ if (transfer->subcallback) { transfer->subcallback(file, item, err, transfer); } return; case SK_TRANSFER_EFAILED: if (file && transfer->subcallback) { transfer->subcallback(file, item, err, transfer); } return; case SK_TRANSFER_EFATAL_CLOSE: /* Pass back to the user handler as non-closeing and stop */ if (transfer->subcallback) { transfer->subcallback(file, item, SK_TRANSFER_EFATAL, transfer); } break; case SK_TRANSFER_EFAILED_CLOSE: /* Pass back to the user handler as non-closeing and stop */ if (transfer->subcallback) { transfer->subcallback(file, item, SK_TRANSFER_EFAILED, transfer); } break; case SK_TRANSFER_ETIMEOUT: /* Pass back to the user handler and stop */ if (transfer->subcallback) { transfer->subcallback(file, item, err, transfer); } break; case SK_TRANSFER_ESHUTDOWN: /* Do nothing */ break; case SK_TRANSFER_EBADARG: assert(0); /* Programming error */ break; case SK_TRANSFER_ESYSTEM: case SK_TRANSFER_ESYSTEM_THREAD: /* Log and stop */ if (transfer->logfn) { transfer->logfn("System failure %s", strerror(errno)); } break; default: assert(0); } if (transfer->sock != -1) { close(transfer->sock); transfer->sock = -1; } transfer->quit_control = 1; } typedef sktErr_t(*server_thread_fn)(skTransfer_t *newt, skTransfer_t transfer, int sock); static void *server_thread_helper(skTransfer_t transfer, server_thread_fn fn) { static int on = 1; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); int flags; int sock; char ip[INET_ADDRSTRLEN]; skTransfer_t *clients; uint8_t num_connections = 0; int maxfd; assert(transfer->server); if ((clients = (skTransfer_t *)calloc(transfer->server->maxclients, sizeof(skTransfer_t))) == NULL) { TRANSFER_SYSTEM_ERROR(transfer, "Could not allocate memory for thread management"); } /* Get a socket */ transfer->newsession = 0; if ((transfer->sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { TRANSFER_SYSTEM_ERROR(transfer, "Could not allocate new socket for server"); } /* Allow quick restarts on crash */ setsockopt(transfer->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(transfer->server->port); /* Bind socket to port and address */ if (bind(transfer->sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { TRANSFER_SYSTEM_ERROR( transfer, "Could not bind port in receiver server"); } /* Set up listen queue */ if (listen(transfer->sock, transfer->server->maxclients) == -1) { TRANSFER_SYSTEM_ERROR(transfer, "Could not set up listen queue"); } /* Set socket nonblocking. See UNIX Network Programming V1 by Stevens, Chapter 15.6 ("Nonblocking accept") in the second edition. */ if ((flags = fcntl(transfer->sock, F_GETFL, 0)) == -1) { TRANSFER_SYSTEM_ERROR(transfer, "Could not get socket flags"); } if (fcntl(transfer->sock, F_SETFL, flags | O_NONBLOCK) == -1) { TRANSFER_SYSTEM_ERROR(transfer, "Could not set socket nonblocking"); } maxfd = ((transfer->sock > transfer->read_pipe) ? transfer->sock : transfer->read_pipe) + 1; /* Infloop */ while (1) { skTransfer_t subt; fd_set set; sktErr_t err; int i; /* Wait until we have space for a new connection */ pthread_mutex_lock(&transfer->mutex); while (transfer->quit_control == 0 && num_connections >= transfer->server->maxclients) { /* Clean up old finished connections */ for (i = 0; i < transfer->server->maxclients; i++) { assert(clients[i] != NULL); if (clients[i]->done) { pthread_mutex_unlock(&transfer->mutex); skDestroyTransfer(clients[i]); pthread_mutex_lock(&transfer->mutex); clients[i] = NULL; num_connections--; break; /* for */ } } /* If all connections are being used, wait until one frees up. */ if (transfer->quit_control == 0 && num_connections >= transfer->server->maxclients) { pthread_cond_wait(&transfer->cond, &transfer->mutex); } } pthread_mutex_unlock(&transfer->mutex); /* Here is where we exit if asked to*/ if (transfer->quit_control != 0) { break; /* Exits main loop */ } /* By getting here, we should be able to accept a new connection. */ FD_ZERO(&set); FD_SET(transfer->sock, &set); FD_SET(transfer->read_pipe, &set); /* Wait for either an incoming connection or a message to quit */ if (select(maxfd, &set, NULL, NULL, NULL) == -1) { if (errno == EWOULDBLOCK || errno == ECONNABORTED #if HAVE_DECL_EPROTO || errno == EPROTO #endif /* HAVE_DECL_EPROTO */ || errno == EINTR) { continue; /* main loop */ } TRANSFER_SYSTEM_ERROR(transfer, "Select failure"); } /* Check for quit condition */ if (FD_ISSET(transfer->read_pipe, &set)) { uint8_t c; if (read(transfer->read_pipe, &c, 1) == -1) { TRANSFER_SYSTEM_ERROR(transfer, "Read on control pipe failure"); } assert(c == SKT_NOTIFY_ABORT || c == SKT_ABORT); break; /* Exits main loop */ } /* Other fd must be set, so we have a connection request */ if ((sock = accept(transfer->sock, (struct sockaddr *)&addr, &addrlen)) == -1) { TRANSFER_SYSTEM_ERROR(transfer, "Accept failure"); } /* Get the name of the connection */ if (transfer->logfn) { #ifdef HAVE_INET_PTON if (!inet_ntop(AF_INET, &addr.sin_addr, ip, sizeof(ip))) { TRANSFER_SYSTEM_ERROR(transfer, "Inet_ntop failure"); } #else { struct in_addr inp; char *tip; inp.s_addr = htonl(addr.sin_addr.s_addr); if ((tip = inet_ntoa(inp)) == NULL) { TRANSFER_SYSTEM_ERROR(transfer, "Inet_ntoa failure"); } else { strncpy(ip, inet_ntoa(inp), sizeof(ip)); } } #endif } /* Check to see if the client is valid */ if (transfer->server->valid_clients) { uint16_t i; const in_addr_t *current; for (i = 0; i < transfer->server->num_valid_clients; i++) { current = &transfer->server->valid_clients[i]; if (memcmp(&addr.sin_addr.s_addr, current, sizeof(in_addr_t)) == 0) { break; /* for */ } } if (i == transfer->server->num_valid_clients) { if (transfer->logfn) { transfer->logfn("Invalid connection from %s", ip); } close(sock); continue; /* Main loop */ } } if (transfer->logfn) { transfer->logfn("Accepted connection from %s", ip); } /* Handle the new connection */ err = fn(&subt, transfer, sock); if (err == SK_TRANSFER_ESUCCESS) { err = skTransferStart(subt); } if (err != SK_TRANSFER_ESUCCESS) { if (transfer->logfn) { transfer->logfn("Error handling new connection"); transfer->logfn("Ending transfer session"); } if (transfer->callback) { transfer->callback(NULL, NULL, err, transfer); } break; } /* Stash the new connection */ for (i = 0; i < transfer->server->maxclients; i++) { if (clients[i] && clients[i]->done) { skDestroyTransfer(clients[i]); clients[i] = NULL; num_connections--; } if (clients[i] == NULL) { clients[i] = subt; num_connections++; break; /* for */ } } assert (i < transfer->server->maxclients); } end: /* All cleanup happens here. */ /* Shutdown all of the clients */ if (clients) { int i; for (i = 0; i < transfer->server->maxclients; i++) { if (clients[i]) { skDestroyTransfer(clients[i]); } } free(clients); } /* Close the socket */ if (transfer->sock != -1) { close(transfer->sock); } /* End the thread */ sk_end_thread(transfer); return NULL; } static sktErr_t receiver_server_creation_fn(skTransfer_t *subt, skTransfer_t transfer, int sock) { return skCreateTransfer(subt, receiver_thread, sock, transfer->queue, NULL, transfer->fn_xform_function, 0, transfer->minimum_baud, transfer->where, transfer->setup, server_thread_callback, transfer->callback, NULL, &transfer->mutex, &transfer->cond, transfer->logfn); } static void *receiver_server_thread(skTransfer_t transfer) { return server_thread_helper(transfer, receiver_server_creation_fn); } static sktErr_t sender_server_creation_fn(skTransfer_t *subt, skTransfer_t transfer, int sock) { return skCreateTransfer(subt, sender_thread, sock, transfer->queue, transfer->filename_function, NULL, transfer->ack_timeout, transfer->minimum_baud, NULL, transfer->setup, server_thread_callback, transfer->callback, NULL, &transfer->mutex, &transfer->cond, transfer->logfn); } static void *sender_server_thread(skTransfer_t transfer) { assert(transfer->server); assert(transfer->server->maxclients == 1); return server_thread_helper(transfer, sender_server_creation_fn); } /* Creates a receiver transfer session server. This function sets up its own threads to do the work and returns immediately. */ sktErr_t skCreateReceiverServer( skTransfer_t *rcvr, /* returns rcvr handle */ int port, /* Port to listen upon */ uint8_t maxclients, /* Max number of clients */ const char *where /* Where to place files */ ) { sktErr_t err = SK_TRANSFER_EBADARG; sk_transfer_server_t *server = (sk_transfer_server_t *)calloc(sizeof(sk_transfer_server_t), 1); if (server == NULL) { return SK_TRANSFER_ESYSTEM; } server->port = port; server->maxclients = maxclients; server->server = INADDR_ANY; err = skCreateTransfer(rcvr, receiver_server_thread, -1, NULL, NULL, skt_filename_xform_identity, SKT_DEFAULT_ACK, SKT_DEFAULT_BAUD, where, NULL, NULL, NULL, server, NULL, NULL, NULL); if (err != SK_TRANSFER_ESUCCESS) { if (server->valid_clients) { free(server->valid_clients); } free(server); } return err; } sktErr_t skCreateSenderServer( skTransfer_t *sender, /* returns sender handle */ int port) /* Port to listen upon */ { sktErr_t err = SK_TRANSFER_EBADARG; sk_transfer_server_t *server = (sk_transfer_server_t *)calloc(sizeof(sk_transfer_server_t), 1); if (server == NULL) { return SK_TRANSFER_ESYSTEM; } server->port = port; server->maxclients = 1; err = skCreateTransfer(sender, sender_server_thread, -1, NULL, skt_filename_identity, NULL, SKT_DEFAULT_ACK, SKT_DEFAULT_BAUD, NULL, NULL, NULL, NULL, server, NULL, NULL, NULL); if (err != SK_TRANSFER_ESUCCESS) { free(server); } return err; } /* Returns whether a transfer session object is a receiver. */ int skTransferIsReceiver(skTransfer_t transfer) { return ((transfer->thread_function == receiver_server_thread) || (transfer->thread_function == receiver_thread)); } /* Returns whether a transfer session object is a sender. */ int skTransferIsSender(skTransfer_t transfer) { return ((transfer->thread_function == sender_server_thread) || (transfer->thread_function == sender_thread)); } int skTransferIsClient(skTransfer_t transfer) { return ((transfer->thread_function == sender_thread) || (transfer->thread_function == receiver_thread)); } int skTransferIsServer(skTransfer_t transfer) { return ((transfer->thread_function == receiver_server_thread) || (transfer->thread_function == sender_server_thread)); } sktErr_t skTransferQueue(skTransfer_t transfer, skDeque_t *queue) { if (!transfer || queue == NULL) { return SK_TRANSFER_EBADARG; } *queue = transfer->queue; return SK_TRANSFER_ESUCCESS; } sktErr_t skServerValidClientAny(skTransfer_t server) { if (server == NULL || server->server == NULL || server->status != SKT_NOT_STARTED || !skTransferIsServer(server)) { return SK_TRANSFER_EBADARG; } if (server->server->valid_clients) { free(server->server->valid_clients); server->server->valid_clients = NULL; } return SK_TRANSFER_ESUCCESS; } /* This function isn't very efficient, but it should't get called enough to need to be. */ sktErr_t skServerValidClientAdd(skTransfer_t server, in_addr_t client) { in_addr_t *clients; uint16_t i; if (server == NULL || server->server == NULL || server->status != SKT_NOT_STARTED || !skTransferIsServer(server)) { return SK_TRANSFER_EBADARG; } for (i = 0; i < server->server->num_valid_clients; i++) { if (server->server->valid_clients[i] == client) { return SK_TRANSFER_ESUCCESS; } } clients = (in_addr_t *)realloc(server->server->valid_clients, sizeof(in_addr_t) * (server->server->num_valid_clients + 1)); if (clients == NULL) { return SK_TRANSFER_ESYSTEM; } clients[server->server->num_valid_clients++] = client; server->server->valid_clients = clients; return SK_TRANSFER_ESUCCESS; } /* This function isn't very efficient, but it should't get called enough to need to be. */ sktErr_t skServerValidClientRemove(skTransfer_t server, in_addr_t client) { uint16_t i; if (server == NULL || server->server == NULL || server->status != SKT_NOT_STARTED || !skTransferIsServer(server)) { return SK_TRANSFER_EBADARG; } for (i = 0; i < server->server->num_valid_clients; i++) { if (server->server->valid_clients[i] == client) { for (i++; i < server->server->num_valid_clients; i++) { server->server->valid_clients[i - 1] = server->server->valid_clients[i]; } break; } } return SK_TRANSFER_ESUCCESS; } sktErr_t skServerSetListeningAddress(skTransfer_t server, in_addr_t addr) { if (server == NULL || server->server == NULL || server->status != SKT_NOT_STARTED || !skTransferIsServer(server)) { return SK_TRANSFER_EBADARG; } server->server->server = addr; return SK_TRANSFER_ESUCCESS; } /* ** Local variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */