123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /*
- * mini-slamd
- * (c) 2002 Eric Biederman
- */
-
- #include <string.h>
- #include <errno.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/poll.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <netinet/ip.h>
- #include <netinet/in.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <arpa/inet.h>
-
- /*
- * To specify the default interface for multicast packets use:
- * route add -net 224.0.0.0 netmask 240.0.0.0 dev eth1
- * This server is stupid and does not override the default.
- */
-
- /* Sever states.
- *
- * Waiting for clients.
- * Sending data to clients.
- * Pinging clients for data.
- *
- */
- #define SLAM_PORT 10000
- #define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
- #define SLAM_MULTICAST_PORT 10000
- #define SLAM_MULTICAST_TTL 1
- #define SLAM_MULTICAST_LOOPBACK 1
- #define SLAM_MAX_CLIENTS 10
-
- #define SLAM_PING_TIMEOUT 100 /* ms */
-
- /*** Packets Formats ***
- * Data Packet:
- * transaction
- * total bytes
- * block size
- * packet #
- * data
- *
- * Status Request Packet
- * transaction
- * total bytes
- * block packets
- *
- * Status Packet
- * received packets
- * requested packets
- * received packets
- * requested packets
- * ...
- * received packets
- * requested packtes
- * 0
- */
-
- #define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
- #define MIN_HDR (1 + 1 + 1) /* transaction, total size, block size */
-
- #define MAX_DATA_HDR (MAX_HDR + 7) /* header, packet # */
- #define MIN_DATA_HDR (MAX_HDR + 1) /* header, packet # */
-
- /* ETH_MAX_MTU 1500 - sizeof(iphdr) 20 - sizeof(udphdr) 8 = 1472 */
- #define SLAM_MAX_NACK (1500 - (20 + 8))
- /* ETH_MAX_MTU 1500 - sizeof(iphdr) 20 - sizeof(udphdr) 8 - MAX_HDR = 1451 */
- #define SLAM_BLOCK_SIZE (1500 - (20 + 8 + MAX_HDR))
-
-
- /* Define how many debug messages you want
- * 1 - sparse but useful
- * 2 - everything
- */
- #ifndef DEBUG
- #define DEBUG 0
- #endif
-
- static int slam_encode(
- unsigned char **ptr, unsigned char *end, unsigned long value)
- {
- unsigned char *data = *ptr;
- int bytes;
- bytes = sizeof(value);
- while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
- bytes--;
- }
- if (bytes <= 0) {
- bytes = 1;
- }
- if (data + bytes >= end) {
- return -1;
- }
- if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
- /* packed together */
- *data = (bytes << 5) | (value >> ((bytes -1)<<3));
- } else {
- bytes++;
- *data = (bytes << 5);
- }
- bytes--;
- data++;
- while(bytes) {
- *(data++) = 0xff & (value >> ((bytes -1)<<3));
- bytes--;
- }
- *ptr = data;
- return 0;
- }
-
- static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, int *err)
- {
- unsigned long value;
- unsigned bytes;
- if (*ptr >= end) {
- *err = -1;
- }
- bytes = ((**ptr) >> 5) & 7;
- if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
- *err = -1;
- return 0;
- }
- if ((*ptr) + bytes >= end) {
- *err = -1;
- }
- value = (**ptr) & 0x1f;
- bytes--;
- (*ptr)++;
- while(bytes) {
- value <<= 8;
- value |= **ptr;
- (*ptr)++;
- bytes--;
- }
- return value;
- }
-
-
- static struct sockaddr_in client[SLAM_MAX_CLIENTS];
- static int clients;
-
-
- void del_client(struct sockaddr_in *old)
- {
- int i;
- for(i = 0; i < clients; i++) {
- if ((client[i].sin_family == old->sin_family) &&
- (client[i].sin_addr.s_addr == old->sin_addr.s_addr) &&
- (client[i].sin_port == old->sin_port)) {
- memmove(&client[i], &client[i+1],
- (clients - (i+1))*sizeof(client[0]));
- clients--;
- }
- }
- }
-
- void add_client(struct sockaddr_in *new)
- {
- del_client(new);
- if (clients >= SLAM_MAX_CLIENTS)
- return;
- memcpy(&client[clients], new, sizeof(*new));
- clients++;
- }
-
- void push_client(struct sockaddr_in *new)
- {
- del_client(new);
- if (clients >= SLAM_MAX_CLIENTS) {
- clients--;
- }
- memmove(&client[1], &client[0], clients*sizeof(*new));
- memcpy(&client[0], new, sizeof(*new));
- clients++;
- }
-
-
- void next_client(struct sockaddr_in *next)
- {
- /* Find the next client we want to ping next */
- if (!clients) {
- next->sin_family = AF_UNSPEC;
- return;
- }
- /* Return the first client */
- memcpy(next, &client[0], sizeof(*next));
- }
-
- int main(int argc, char **argv)
- {
- char *filename;
- uint8_t nack_packet[SLAM_MAX_NACK];
- int nack_len;
- uint8_t request_packet[MAX_HDR];
- int request_len;
- uint8_t data_packet[MAX_DATA_HDR + SLAM_BLOCK_SIZE];
- int data_len;
- uint8_t *ptr, *end;
- struct sockaddr_in master_client;
- struct sockaddr_in sa_src;
- struct sockaddr_in sa_mcast;
- uint8_t mcast_ttl;
- uint8_t mcast_loop;
- int sockfd, filefd;
- int result;
- struct pollfd fds[1];
- int state;
- #define STATE_PINGING 1
- #define STATE_WAITING 2
- #define STATE_RECEIVING 3
- #define STATE_TRANSMITTING 4
- off_t size;
- struct stat st;
- uint64_t transaction;
- unsigned long packet;
- unsigned long packet_count;
- unsigned slam_port, slam_multicast_port;
- struct in_addr slam_multicast_ip;
-
- slam_port = SLAM_PORT;
- slam_multicast_port = SLAM_MULTICAST_PORT;
- slam_multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP);
-
- if (argc != 2) {
- fprintf(stderr, "Bad argument count\n");
- fprintf(stderr, "Usage: mini-slamd filename\n");
- exit(EXIT_FAILURE);
- }
- filename = argv[1];
- filefd = -1;
- size = 0;
- transaction = 0;
-
- /* Setup the udp socket */
- sockfd = socket(PF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0) {
- fprintf(stderr, "Cannot create socket\n");
- exit(EXIT_FAILURE);
- }
- memset(&sa_src, 0, sizeof(sa_src));
- sa_src.sin_family = AF_INET;
- sa_src.sin_port = htons(slam_port);
- sa_src.sin_addr.s_addr = INADDR_ANY;
-
- result = bind(sockfd, &sa_src, sizeof(sa_src));
- if (result < 0) {
- fprintf(stderr, "Cannot bind socket to port %d\n",
- ntohs(sa_src.sin_port));
- exit(EXIT_FAILURE);
- }
-
- /* Setup the multicast transmission address */
- memset(&sa_mcast, 0, sizeof(sa_mcast));
- sa_mcast.sin_family = AF_INET;
- sa_mcast.sin_port = htons(slam_multicast_port);
- sa_mcast.sin_addr.s_addr = slam_multicast_ip.s_addr;
- if (!IN_MULTICAST(ntohl(sa_mcast.sin_addr.s_addr))) {
- fprintf(stderr, "Not a multicast ip\n");
- exit(EXIT_FAILURE);
- }
-
- /* Set the multicast ttl */
- mcast_ttl = SLAM_MULTICAST_TTL;
- setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
- &mcast_ttl, sizeof(mcast_ttl));
-
- /* Set the multicast loopback status */
- mcast_loop = SLAM_MULTICAST_LOOPBACK;
- setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &mcast_loop, sizeof(mcast_loop));
-
-
- state = STATE_WAITING;
- packet = 0;
- packet_count = 0;
- fds[0].fd = sockfd;
- fds[0].events = POLLIN;
- fds[0].revents = 0;
- for(;;) {
- switch(state) {
- case STATE_PINGING:
- state = STATE_WAITING;
- next_client(&master_client);
- if (master_client.sin_family == AF_UNSPEC) {
- break;
- }
- #if DEBUG
- printf("Pinging %s:%d\n",
- inet_ntoa(master_client.sin_addr),
- ntohs(master_client.sin_port));
- fflush(stdout);
- #endif
-
- /* Prepare the request packet, it is all header */
- ptr = request_packet;
- end = &request_packet[sizeof(request_packet) -1];
- slam_encode(&ptr, end, transaction);
- slam_encode(&ptr, end, size);
- slam_encode(&ptr, end, SLAM_BLOCK_SIZE);
- request_len = ptr - request_packet;
-
- result = sendto(sockfd, request_packet, request_len, 0,
- &master_client, sizeof(master_client));
- /* Forget the client I just asked, when the reply
- * comes in we will remember it again.
- */
- del_client(&master_client);
- break;
- case STATE_WAITING:
- {
- int timeout;
- int from_len;
- timeout = -1;
- if (master_client.sin_family != AF_UNSPEC) {
- timeout = SLAM_PING_TIMEOUT;
- }
- result = poll(fds, sizeof(fds)/sizeof(fds[0]), timeout);
- if (result == 0) {
- /* On a timeout try the next client */
- state = STATE_PINGING;
- break;
- }
- if (result > 0) {
- from_len = sizeof(master_client);
- result = recvfrom(sockfd,
- nack_packet, sizeof(nack_packet), 0,
- &master_client, &from_len);
- if (result < 0)
- break;
- nack_len = result;
- #if DEBUG
- printf("Received Nack from %s:%d\n",
- inet_ntoa(master_client.sin_addr),
- ntohs(master_client.sin_port));
- fflush(stdout);
- #endif
- #if DEBUG
- {
- ptr = nack_packet;
- end = ptr + result;
- packet = 0;
- result = 0;
- while(ptr < end) {
- packet += slam_decode(&ptr, end, &result);
- if (result < 0) break;
- packet_count = slam_decode(&ptr, end, &result);
- if (result < 0) break;
- printf("%d-%d ",
- packet, packet + packet_count -1);
- }
- printf("\n");
- fflush(stdout);
- }
- #endif
- /* Forget this client temporarily.
- * If the packet appears good they will be
- * readded.
- */
- del_client(&master_client);
- ptr = nack_packet;
- end = ptr + nack_len;
- result = 0;
- packet = slam_decode(&ptr, end, &result);
- if (result < 0)
- break;
- packet_count = slam_decode(&ptr, end, &result);
- if (result < 0)
- break;
- /* We appear to have a good packet, keep
- * this client.
- */
- push_client(&master_client);
-
- /* Reopen the file to transmit */
- if (filefd != -1) {
- close(filefd);
- }
- filefd = open(filename, O_RDONLY);
- if (filefd < 0) {
- fprintf(stderr, "Cannot open %s: %s\n",
- filename, strerror(errno));
- break;
- }
- size = lseek(filefd, 0, SEEK_END);
- if (size < 0) {
- fprintf(stderr, "Seek failed on %s: %s\n",
- filename, strerror(errno));
- break;
- }
- result = fstat(filefd, &st);
- if (result < 0) {
- fprintf(stderr, "Stat failed on %s: %s\n",
- filename, strerror(errno));
- break;
- }
- transaction = st.st_mtime;
-
- state = STATE_TRANSMITTING;
- break;
- }
- break;
- }
- case STATE_RECEIVING:
- /* Now clear the queue of received packets */
- {
- struct sockaddr_in from;
- int from_len;
- uint8_t dummy_packet[SLAM_MAX_NACK];
- state = STATE_TRANSMITTING;
- result = poll(fds, sizeof(fds)/sizeof(fds[0]), 0);
- if (result < 1)
- break;
- from_len = sizeof(from);
- result = recvfrom(sockfd,
- dummy_packet, sizeof(dummy_packet), 0,
- &from, &from_len);
- if (result <= 0)
- break;
- #if DEBUG
- printf("Received Nack from %s:%d\n",
- inet_ntoa(from.sin_addr),
- ntohs(from.sin_port));
- fflush(stdout);
- #endif
- /* Receive packets until I don't get any more */
- state = STATE_RECEIVING;
- /* Process a packet */
- if (dummy_packet[0] == '\0') {
- /* If the first byte is null it is a disconnect
- * packet.
- */
- del_client(&from);
- }
- else {
- /* Otherwise attempt to add the client. */
- add_client(&from);
- }
- break;
- }
- case STATE_TRANSMITTING:
- {
- off_t off;
- off_t offset;
- ssize_t bytes;
- uint8_t *ptr2, *end2;
-
- /* After I transmit a packet check for packets to receive. */
- state = STATE_RECEIVING;
-
- /* Find the packet to transmit */
- offset = packet * SLAM_BLOCK_SIZE;
-
- /* Seek to the desired packet */
- off = lseek(filefd, offset, SEEK_SET);
- if ((off < 0) || (off != offset)) {
- fprintf(stderr, "Seek failed on %s:%s\n",
- filename, strerror(errno));
- break;
- }
- /* Encode the packet header */
- ptr2 = data_packet;
- end2 = data_packet + sizeof(data_packet);
- slam_encode(&ptr2, end2, transaction);
- slam_encode(&ptr2, end2, size);
- slam_encode(&ptr2, end2, SLAM_BLOCK_SIZE);
- slam_encode(&ptr2, end2, packet);
- data_len = ptr2 - data_packet;
-
- /* Read in the data */
- bytes = read(filefd, &data_packet[data_len],
- SLAM_BLOCK_SIZE);
- if (bytes <= 0) {
- fprintf(stderr, "Read failed on %s:%s\n",
- filename, strerror(errno));
- break;
- }
- data_len += bytes;
- /* Write out the data */
- result = sendto(sockfd, data_packet, data_len, 0,
- &sa_mcast, sizeof(sa_mcast));
- if (result != data_len) {
- fprintf(stderr, "Send failed %s\n",
- strerror(errno));
- break;
- }
- #if DEBUG > 1
- printf("Transmitted: %d\n", packet);
- fflush(stdout);
- #endif
- /* Compute the next packet */
- packet++;
- packet_count--;
- if (packet_count == 0) {
- packet += slam_decode(&ptr, end, &result);
- if (result >= 0)
- packet_count = slam_decode(&ptr, end, &result);
- if (result < 0) {
- /* When a transmission is done close the file,
- * so it may be updated. And then ping then start
- * pinging clients to get the transmission started
- * again.
- */
- state = STATE_PINGING;
- close(filefd);
- filefd = -1;
- break;
- }
- }
- break;
- }
- }
- }
- return EXIT_SUCCESS;
- }
|