/* * * Function: main() * */ int main(int argc, char *argv[]) { char *program_name = argv[0]; int optc; /* option */ int sock_fd; /* socket descriptor for a connection */ char *server_name; /* Name (or IP address) of the server */ sa_family_t family; /* protocol family */ char *portnum; /* port number in string representation */ struct addrinfo hints; /* hints for getaddrinfo() */ struct addrinfo *res; /* pointer to addrinfo structure */ int err; /* return value of getaddrinfo */ int on; /* on/off at an socket option */ int recvbuf_size; /* size of the receive buffer */ int sock_optlen; /* size of the result parameter */ char *recvbuf; /* pointer to the received message */ ssize_t recvbyte_size; /* size of the receive byte */ time_t start_time; /* time when the timer is start */ double timeout = 0.0; /* timeout */ int background = 0; /* If non-zero work in the background */ size_t window_scaling = 0; /* if non-zero, in the window scaling mode */ debug = 0; /* Initilalize the client information */ family = PF_UNSPEC; server_name = NULL; portnum = NULL; /* Retrieve the options */ while ((optc = getopt(argc, argv, "S:f:p:t:bwdh")) != EOF) { switch (optc) { case 'S': server_name = strdup(optarg); if (server_name == NULL) { fprintf(stderr, "strdup() failed."); exit(EXIT_FAILURE); } break; case 'f': if (strncmp(optarg, "4", 1) == 0) family = PF_INET; /* IPv4 */ else if (strncmp(optarg, "6", 1) == 0) family = PF_INET6; /* IPv6 */ else { fprintf(stderr, "protocol family should be 4 or 6.\n"); usage(program_name, EXIT_FAILURE); } break; case 'p': { unsigned long int tmp; tmp = strtoul(optarg, NULL, 0); if (tmp < PORTNUMMIN || PORTNUMMAX < tmp) { fprintf(stderr, "The range of port is from %u to %u\n", PORTNUMMIN, PORTNUMMAX); usage(program_name, EXIT_FAILURE); } portnum = strdup(optarg); } break; case 't': timeout = strtod(optarg, NULL); if (timeout < 0) { fprintf(stderr, "Timeout value is bigger than 0\n"); usage(program_name, EXIT_FAILURE); } break; case 'b': background = 1; break; case 'w': window_scaling = 1; break; case 'd': debug = 1; break; case 'h': usage(program_name, EXIT_SUCCESS); break; default: usage(program_name, EXIT_FAILURE); } } /* Check the server name is specified. */ if (server_name == NULL) { fprintf(stderr, "server name isn't specified.\n"); usage(program_name, EXIT_FAILURE); } /* Check the family is specified. */ if (family == PF_UNSPEC) { fprintf(stderr, "protocol family isn't specified.\n"); usage(program_name, EXIT_FAILURE); } /* Check the port number is specified. */ if (portnum == NULL) { fprintf(stderr, "port number isn't specified.\n"); usage(program_name, EXIT_FAILURE); } /* At first, SIGHUP are Ignored. */ handler.sa_handler = set_signal_flag; handler.sa_flags = 0; if (sigfillset(&handler.sa_mask) < 0) fatal_error("sigfillset()"); if (sigaction(SIGHUP, &handler, NULL) < 0) fatal_error("sigaction()"); /* Set the hints to addrinfo() */ memset(&hints, '\0', sizeof(struct addrinfo)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; /* Translate the network and service information of the client */ err = getaddrinfo(server_name, portnum, &hints, &res); if (err) { fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err)); exit(EXIT_FAILURE); } if (res->ai_next) { fprintf(stderr, "getaddrinfo(): multiple address is found."); exit(EXIT_FAILURE); } /* Create a socket */ sock_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock_fd < 0) fatal_error("socket()"); /* Enable to reuse the socket */ on = 1; if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int))) fatal_error("setsockopt()"); /* Maximize socket buffer, when window scaling mode */ if (window_scaling) maximize_sockbuf(sock_fd); /* Connect to the server */ if (connect(sock_fd, res->ai_addr, res->ai_addrlen) < 0) fatal_error("connect()"); freeaddrinfo(res); free(server_name); /* If -b option is specified, work as a daemon */ if (background) if (daemon(0, 0) < 0) fatal_error("daemon()"); /* Get the size of receive buffer */ sock_optlen = sizeof(recvbuf_size); if (getsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, &recvbuf_size, &sock_optlen) < 0) fatal_error("getsockopt()"); if (debug) fprintf(stderr, "recvbuf size of socket(%d) is %d\n", sock_fd, recvbuf_size); /* Prepare a buffer to receive bytes */ recvbuf = (char *)malloc(recvbuf_size); if (recvbuf == NULL) { fprintf(stderr, "malloc() is failed.\n"); exit(EXIT_FAILURE); } /* * Loop for receiving data from the server */ start_time = time(NULL); handler.sa_handler = set_signal_flag; if (sigaction(SIGHUP, &handler, NULL) < 0) fatal_error("sigaction()"); for(;;) { recvbyte_size = recv(sock_fd, recvbuf, recvbuf_size, 0); if (recvbyte_size < (ssize_t)0) { if (catch_sighup) break; else fatal_error("sendto()"); } else if (recvbyte_size == (ssize_t)0) break; /* client timeout */ if (timeout) if (timeout < difftime(time(NULL), start_time)) break; /* Catch SIGHUP */ if (catch_sighup) break; } if (close(sock_fd) < 0) fatal_error("close()"); free(recvbuf); if (debug) fprintf (stderr, "Client is finished without any error\n"); exit(EXIT_SUCCESS); }
/* * Function: create_listen_socket() * * Descripton: * Create a socket to listen for connections on a socket. * The socket discripter is stored info_p->listen_sd. * * Argument: * info_p: pointer to a server infomation * * Return value: * None */ void create_listen_socket(struct server_info *info_p) { int on; /* on/off at an socket option */ int err; /* return value of getaddrinfo */ struct addrinfo hints; /* hints for getaddrinfo() */ struct addrinfo *res; /* pointer to addrinfo */ /* Set the hints to addrinfo() */ memset(&hints, '\0', sizeof(struct addrinfo)); hints.ai_family = info_p->family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; /* Translate the network and service information of the server */ err = getaddrinfo(NULL, info_p->portnum, &hints, &res); if (err) { fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(err)); exit(EXIT_FAILURE); } if (res->ai_next) { fprintf(stderr, "getaddrinfo(): multiple address is found."); exit(EXIT_FAILURE); } /* Create a socket for listening. */ info_p->listen_sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (info_p->listen_sd < 0) fatal_error("socket()"); #ifdef IPV6_V6ONLY /* Don't accept IPv4 mapped address if the protocol family is IPv6 */ if (res->ai_family == PF_INET6) { on = 1; if (setsockopt(info_p->listen_sd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(int))) fatal_error("setsockopt()"); } #endif /* Enable to reuse the socket */ on = 1; if (setsockopt(info_p->listen_sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int))) fatal_error("setsockopt()"); /* Disable the Nagle algorithm, when small sending mode */ if (info_p->small_sending) { on = 1; if (setsockopt(info_p->listen_sd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(int))) fatal_error("setsockopt()"); if (debug) { fprintf(stderr, "small sending[on]\n"); } } /* Maximize socket buffer, when window scaling mode */ if (info_p->window_scaling) maximize_sockbuf(info_p->listen_sd); /* Bind to the local address */ if (bind(info_p->listen_sd, res->ai_addr, res->ai_addrlen) < 0) fatal_error("bind()"); freeaddrinfo(res); /* Start to listen for connections */ if (listen(info_p->listen_sd, 5) < 0) fatal_error("listen()"); }