#include #include #include #define ERRMSG_BUFFER_SIZE 100 // Size of error message buffer #define ECHO_PORT 7 // Port of echo service (RFC 862) #define BUFFER_SIZE 100 // Size of buffer for datagrams /* Name: getErrorMsg * Desription: Returns a human readable error message for codes from * WSAGetLastError or GetLastError * Parameters: code - error code to return message for * Return code: Pointer to a static buffer containing the message * Notes: Not thread-safe because of static buffer */ const char *getErrorMsg(int code) { static char buffer[ERRMSG_BUFFER_SIZE]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buffer, sizeof(buffer), NULL); return buffer; } /* Name: initWinsock * Desription: Initialized Winsock subsystem * Parameters: none * Return code: true if successful * false if an error occurred */ bool initWinsock(void) { bool failed = false; WSADATA wsa; int startupResult = WSAStartup(MAKEWORD(1, 1), &wsa); if (startupResult != 0) { fprintf(stderr, "Couldn't start Winsock: %s\n", getErrorMsg(startupResult)); failed = true; } return !failed; } /* Name: deinitWinsock * Desription: Deinitialized Winsock subsystem * Parameters: none * Return code: none */ void deinitWinsock(void) { WSACleanup(); } /* Name: getCmdLineArgs * Description: Gets the expected arguments from command line * Parameters: argc - number of arguments specified * argv - array with arguments as strings * server - hostname of server to connect to * Return code: true if successful * false if an error occurred */ bool getCmdLineArgs(int argc, char *argv[], const char **server) { bool result = false; if (argc == 2) { *server = argv[1]; result = true; } else { fprintf(stderr, "usage: %s \n", argv[0]); } return result; } /* Name: resolveHostName * Description: Resolves a hostname into an IP address * Parameters: name - hostname to resolve * addr - buffer to store IP address in * Return code: true if successful * false if an error occurred */ bool resolveHostName(const char *name, struct in_addr *addr) { bool result = false; printf("Resolving hostname \"%s\"...", name); struct hostent *host = gethostbyname(name); if (host) { printf(" OK.\n"); *addr = *(struct in_addr*)host->h_addr; result = true; } else { printf(" FAILED.\n"); fprintf(stderr, "Couldn't resolve hostname: %s\n", getErrorMsg(WSAGetLastError())); } return result; } /* Name: createUdpSocket * Description: Creates a new, unconnected UDP socket * Parameters: sock - buffer to receive socket * Return code: true if successful * false if an error occurred */ bool createUdpSocket(SOCKET *sock) { bool failed = false; *sock = socket(AF_INET, SOCK_DGRAM, 0); if (*sock == INVALID_SOCKET) { fprintf(stderr, "Couldn't create socket: %s\n", getErrorMsg(WSAGetLastError())); failed = true; } return !failed; } /* Name: removeLineBreak * Description: Removes trailing line breaks from a string * Parameters: str - string to remove line break from * Return code: none */ void removeLineBreak(char *str) { if (str) { while (*str && *str != '\r' && *str != '\n') str++; *str = '\0'; } } /* Name: readRequestFromStdin * Description: Reads a zero terminated string from stdin * Parameters: buffer - buffer ot read string into * size - maximum number of bytes, incl. zero termination * Return code: true if successful * false if an error occurred */ bool readRequestFromStdin(char *buffer, int size) { bool result = false; printf("> "); fflush(stdout); if (fgets(buffer, size, stdin) != NULL) { removeLineBreak(buffer); result = true; } else { fprintf(stderr, "Couldn't read from stdin: %s\n", strerror(errno)); } return result; } /* Name: sendEchoRequest * Description: Sends a string as UDP datagram to an echo service * Parameters: sock - socket to use for sending * str - zero terminated string to send * addr - address to send datagram to * port - port to send datagram to * Return code: true if successful * false if an error occurred */ bool sendEchoRequest(SOCKET sock, const char *str, struct in_addr addr, unsigned short port) { bool result = false; struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr = addr; int bytes = sendto(sock, str, strlen(str), 0, (struct sockaddr*) &saddr, sizeof(saddr)); if (bytes > 0) { printf("Sent %i bytes to %s:%u.\n", bytes, inet_ntoa(saddr.sin_addr), port); result = true; } else { fprintf(stderr, "Couldn't write to socket: %s\n", getErrorMsg(WSAGetLastError())); } return result; } /* Name: receiveEchoReply * Description: Receives an echo reply from a socket and writes it to * stdout * Parameters: sock - socket to use for receiving * Return code: true if successful * false if an error occurred */ bool receiveEchoReply(SOCKET sock) { bool result = false; char reply[BUFFER_SIZE]; struct sockaddr_in saddr; int size = sizeof(saddr); int bytes = recvfrom(sock, reply, sizeof(reply) - 1, 0, (struct sockaddr*) &saddr, &size); if (bytes >= 0) { reply[bytes] = '\0'; printf("Received %i bytes from %s:%u: <%s>\n", bytes, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port), reply); result = true; } else { fprintf(stderr, "Couldn't read from socket: %s\n", getErrorMsg(WSAGetLastError())); } return result; } /* Name: safeCloseSocket * Description: Checks if a socket is valid, closes it. The socket is * set to an invalid value to avoid double free errors. * Parameters: sock - socket to communicate with * Return code: none */ void safeCloseSocket(SOCKET *sock) { if (*sock != INVALID_SOCKET) { closesocket(*sock); *sock = INVALID_SOCKET; } } int main(int argc, char *argv[]) { const char *hostname; bool good = getCmdLineArgs(argc, argv, &hostname); bool startupSuccessful = false; if (good) good = startupSuccessful = initWinsock(); struct in_addr addr; if (good) good = resolveHostName(hostname, &addr); SOCKET sock = INVALID_SOCKET; if (good) good = createUdpSocket(&sock); char request[BUFFER_SIZE]; if (good) good = readRequestFromStdin(request, sizeof(request)); if (good) good = sendEchoRequest(sock, request, addr, ECHO_PORT); if (good) good = receiveEchoReply(sock); safeCloseSocket(&sock); return good ? EXIT_SUCCESS : EXIT_FAILURE; }