#include #include #include #include #include #include #include #include #include #include void md5_set(int sock, int family, const char *host, unsigned short port, const char *password) { struct tcp_md5sig md5sig; int s; memset(&md5sig, 0, sizeof(md5sig)); if (family == AF_INET6) { struct sockaddr_in6 sin6 = { .sin6_family = family, .sin6_port = htons(port), }; if (strchr(host, ':') == NULL) { /* convert V4 address to mapped */ sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); s = inet_pton(AF_INET, host, &sin6.sin6_addr.s6_addr32[3]); if (s == 0) fprintf(stderr, "%s: not in IPV4 format\n", host); } else { s = inet_pton(AF_INET6, host, &sin6.sin6_addr); if (s == 0) fprintf(stderr, "%s: not in IPV6 format\n", host); } memcpy(&md5sig.tcpm_addr, &sin6, sizeof(sin6)); } else { struct sockaddr_in sin = { .sin_family = family, .sin_port = htons(port), }; s = inet_pton(AF_INET, host, &sin.sin_addr); if (s == 0) fprintf(stderr, "%s: not in IPV4 format\n", host); memcpy(&md5sig.tcpm_addr, &sin, sizeof(sin)); } if (s <= 0) exit(EXIT_FAILURE); memcpy(md5sig.tcpm_key, password, md5sig.tcpm_keylen = strlen(password)); if (setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) < 0) perror("setsockopt (TCP_MD5SIG)"); } static void usage(void) { fprintf(stderr, "Usage: server port [host port passwd]\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { struct addrinfo *result, *rp; int sfd, s, cc; static struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_flags = AI_PASSIVE, .ai_socktype = SOCK_STREAM, }; char buf[BUFSIZ]; if (argc > 1 && argv[1][0] == '-') { if (strcmp(argv[1], "-4") == 0) { hints.ai_family = AF_INET; } else if (strcmp(argv[1], "-6") == 0) { hints.ai_family = AF_INET6; } else usage(); --argc, ++argv; } if (argc < 2) usage(); s = getaddrinfo(NULL, argv[1], &hints, &result); if (s != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); exit(EXIT_FAILURE); } for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) { perror("socket"); continue; } if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) break; /* Success */ perror("bind"); close(sfd); } if (rp == NULL) { /* No address succeeded */ fprintf(stderr, "Could not bind\n"); exit(EXIT_FAILURE); } if (listen(sfd, 5) < 0) perror("listen"); if (argc > 2) md5_set(sfd, rp->ai_family, argv[2], atoi(argv[3]), argv[4]); freeaddrinfo(result); /* No longer needed */ for (;;) { struct sockaddr_storage peer_addr; socklen_t peer_addr_len = sizeof(peer_addr); char host[NI_MAXHOST], service[NI_MAXSERV]; int afd = accept(sfd, (struct sockaddr *)&peer_addr, &peer_addr_len); if (afd < 0) { perror("accept"); return 1; } s = getnameinfo((struct sockaddr *)&peer_addr, peer_addr_len, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV); if (s == 0) printf("Connect from %s:%s\n", host, service); else fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s)); if (fork() == 0) { close(sfd); while ((cc = read(afd, buf, sizeof(buf))) > 0) if (write(afd, buf, cc) != cc) break; if (cc < 0) perror("read"); else printf("eof\n"); exit(0); } close(afd); } }