#include #include #include #include #include #include #include #include #include #include const ssize_t SST_NEG1 = -1; const size_t ST_NEG1 = -1; const int SOCK_FALSE = 0; const int SOCK_TRUE = 1; #ifdef OFFICIAL #define PORTNUM 12345 #define DEBUG 0 #else #define PORTNUM 12345 #define DEBUG 1 #endif typedef struct { uint8_t version; uint8_t ttl; uint16_t payload_length; uint32_t account_identifier; uint32_t source_address; uint32_t destination_address; uint16_t checksum; uint16_t protocol; } cmsc417_message_header; int main(int argc, char**argv) { int ret_val = 0; #ifdef OFFICIAL const int MAX_ARGS = 2; #else const int MAX_ARGS = 3; #endif if((2 <= argc) && (argc <= MAX_ARGS)) { /***** * Send *****/ /* Declare and init message */ cmsc417_message_header head; head.version = 1; head.ttl = 1; /* Payload will be a string, argv[1], sans null */ head.payload_length = htons(strlen(argv[1])); head.account_identifier = htonl(6); head.source_address = htonl(0); head.destination_address = htonl(0); head.checksum = htons(0); head.protocol = htons(1); /* XXX this might bite us in the ass if the compiler pads the struct for alignment. * If it does, just calculate the unaligned size and use that for malloc and memcpy. */ size_t msg_size = sizeof(head) + strlen(argv[1]); void* msg_bytes = malloc(msg_size); if(NULL == msg_bytes) { fprintf(stderr, "Error allocating memory for message: %s\n", strerror(errno)); goto END_SEND; } memset(msg_bytes, 0, sizeof(msg_size)); memcpy(msg_bytes, &head, sizeof(head)); memcpy(msg_bytes+sizeof(head), argv[0], strlen(argv[1])); struct sockaddr_in addr; socklen_t addr_size = sizeof(addr); char* addr_str; if(argc >= 3) { addr_str = argv[2]; } else { addr_str = "224.0.50.111"; } addr.sin_family = PF_INET; /* XXX need to handle error from inet_addr */ in_addr_t to_in_addr = inet_addr(addr_str); if((INADDR_NONE == to_in_addr) && (0 != strncmp("255.255.255.255", addr_str, 15))) { fprintf(stderr, "Error converting address '%s': %s\n", addr_str, strerror(errno)); goto END_SEND; } addr.sin_addr.s_addr = htonl(to_in_addr); addr.sin_port = htons(12345); /* SOCK_DGRAM and 0 might not be correct ... */ int sock = socket(PF_INET, SOCK_DGRAM, 0); if(-1 == sock) { fprintf(stderr, "Error creating send socket: %s\n", strerror(errno)); goto END_SEND; } if(-1 == setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &SOCK_TRUE, sizeof(SOCK_TRUE))) { fprintf(stderr, "Error setting broadcast on send: %s\n", strerror(errno)); close(sock); goto END_SEND; } /* Note: saddr fields were initialized in network byte order */ ssize_t sent_size = sendto(sock, msg_bytes, msg_size, 0, (struct sockaddr *) &addr, addr_size); if(DEBUG) { fprintf(stderr, "sent %zu out of %zu bytes\n", sent_size, msg_size); } if(SST_NEG1 == sent_size) { fprintf(stderr, "Error sending message: %s\n", strerror(errno)); } close(sock); /* Jump Free Memory, Jump! */ END_SEND: if(NULL != msg_bytes) { free(msg_bytes); } /***** * Recieve *****/ sock = socket(PF_INET, SOCK_DGRAM, 0); if(-1 == sock) { fprintf(stderr, "Error creating receive socket: %s\n", strerror(errno)); ret_val = 10; goto END_RECV; } if(-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &SOCK_TRUE, sizeof(SOCK_TRUE))) { fprintf(stderr, "Error setting reuse addr on recv: %s\n", strerror(errno)); close(sock); ret_val = 11; goto END_RECV; } /* This is needed on BSD (at least OS X) and shouldn't hurt */ #ifdef SO_REUSEPORT if(-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &SOCK_TRUE, sizeof(SOCK_TRUE))) { fprintf(stderr, "Error setting reuse port on recv: %s\n", strerror(errno)); ret_val = 12; close(sock); goto END_RECV; } #endif /* Re-use addr. We can't bind to multicast, I seem to recall+tried */ addr.sin_addr.s_addr = htonl(INADDR_ANY); int bind_ret = bind(sock, (struct sockaddr *) &addr, addr_size); if(-1 == bind_ret) { fprintf(stderr, "Error binding to %s:%d: %s\n", addr_str, PORTNUM, strerror(errno)); ret_val = 13; close(sock); goto END_RECV; } /* 2^16 - 1 + 1, extra byte so we can definitely append null */ const size_t MAX_SIZE = 65536; void *buf = malloc(MAX_SIZE); if(NULL == msg_bytes) { fprintf(stderr, "Error allocating memory for buffer: %s\n", strerror(errno)); goto END_RECV; } while(1) { /* Safety first kids (debugging sanity, really) */ memset(buf, 0, sizeof(buf)); /* Why is the addr_size argument a non-const pointer?? */ size_t num_recv = recvfrom(sock, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &addr_size); /* size_t num_recv = recv(sock, buf, sizeof(buf), MSG_WAITALL); */ if(DEBUG) { fprintf(stderr, "recieved %zu bytes\n", num_recv); } if(-1 == num_recv) { fprintf(stderr, "Error receiving bytes: %d. Continuing ...\n", errno); continue; } if(num_recv >= sizeof(head)) { memcpy(&head, buf, sizeof(head)); /* Validate */ size_t tot_len = ntohs(head.payload_length) + sizeof(head); if(DEBUG) { fprintf(stderr, "tot_len = %zu\n", tot_len); fprintf(stderr, "head.protocol = %d\n", ntohs(head.protocol)); fprintf(stderr, "head.version = %d\n", head.version); } if((1 == ntohs(head.protocol)) && (1 == head.version) && (tot_len == num_recv)) { ((char*)buf)[tot_len] = '\0'; /* ensure null termination */ /* Is appending a newline okay? */ printf("%s\n", (char*) buf+sizeof(head)); } else { if(DEBUG) { fprintf(stderr, "Dropped packet\n"); } } } } close(sock); END_RECV: if(NULL != buf) { free(buf); } } else { fprintf(stderr, "Usage:\n"); #ifdef OFFICIAL fprintf(stderr, "\tone message\n"); #else fprintf(stderr, "\tone message [address]\n"); #endif } return ret_val; }