1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
|
/* standalone discard and other services
2000.10.16 Rafal Maszkowski <rzm@icm.edu.pl>
based on Richard Stevens unpv12e/tcpcliserv/tcpserv09.c
Adding a new service: see MODE_TEMPLATE
TODO:
- binding a specific IPv[46] local address
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h> /* for Solaris */
#include "compat.h"
/* Following could be derived from SOMAXCONN in <sys/socket.h>, but many
kernels still #define it as 5, while actually supporting many more */
#define LISTENQ 1024 /* 2nd argument to listen() */
/* Following shortens all the type casts of pointer arguments */
#define SA struct sockaddr
/* Put the program in the background, and dissociate from the controlling
terminal. If NOCHDIR is zero, do `chdir ("/")'. If NOCLOSE is zero,
redirects stdin, stdout, and stderr to /dev/null. */
/* extern int daemon __P ((int __nochdir, int __noclose)); */
#define MODE_ECHO 1
#define MODE_DISCARD 2
#define MODE_CHARGEN 3
#define MODE_TEMPLATE 999
#define CHARGEN_START 32
#define CHARGEN_STOP 126
#define CHARGEN_WIDTH 72
int defports[] = { 0, 7, 9, 19, 0 };
int opt_proto = SOCK_STREAM;
void
process(int fdin, int fdout, int opt_mode) {
int bytes, ch, ind;
char buf[4096];
switch(opt_mode) {
case MODE_ECHO: for ( ; ; ) {
if ((bytes = read(fdin, buf, sizeof(buf))) <= 0) break;
if ((bytes = write(fdin, buf, bytes)) <= 0) break;
}
break;
case MODE_CHARGEN:
for (ch = CHARGEN_START; ch<=CHARGEN_STOP; ch++) buf[ch-CHARGEN_START] = ch;
bcopy(buf, buf+CHARGEN_STOP-CHARGEN_START+1, CHARGEN_STOP-CHARGEN_START+1);
for (ind = 0; ; ind++) {
if ((bytes = write(fdout, buf+ind, CHARGEN_WIDTH)) <= 0) break;
if ((bytes = write(fdout, "\r\n", 2)) <= 0) break;
if (ind>CHARGEN_STOP-CHARGEN_START) ind = 0;
}
break;
case MODE_TEMPLATE:
strncpy(buf, "Just a template for new services.\r\n", sizeof(buf));
write(fdout, buf, strlen(buf));
break;
default: /* MODE_DISCARD */ for ( ; ; ) {
if ((bytes = read(fdin, buf, sizeof(buf))) <= 0) break;
}
}
}
void
sig_chld(int signo) {
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) {
/* printf("child %d terminated\n", pid); */
}
return;
}
int
name2mode(char *name) {
if (strncmp(name, "discard", 8)==0) return MODE_DISCARD;
if (strncmp(name, "echo", 5)==0) return MODE_ECHO;
if (strncmp(name, "chargen", 8)==0) return MODE_CHARGEN;
if (strncmp(name, "template", 9)==0) return MODE_TEMPLATE;
return -1;
}
void
usage(char *name) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [options]\n\n", name);
fprintf(stderr, "%s is a standalone echo/discard/chargen service.\n", name);
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, "\n");
#ifdef HAVE_GETADDRINFO
fprintf(stderr, "-4, --ipv4 bind ipv4 address (default)\n");
fprintf(stderr, "-6, --ipv6 bind ipv6 address (means: both?)\n");
#endif /* HAVE_GETADDRINFO */
fprintf(stderr, "-f, --foreground do not be a daemon (ignored with -i)\n");
fprintf(stderr, "-h, --help this help\n");
fprintf(stderr, "-i, --inetd runs as an inetd service\n");
fprintf(stderr, "-m MODE MODE can be echo, discard or chargen (default: %s)\n", name);
fprintf(stderr, "-p PORT listen on PORT instead of default 7/9/19\n");
fprintf(stderr, "-t, --tcp use TCP%s\n", opt_proto==SOCK_STREAM ? " (default)" : "" );
fprintf(stderr, "-u, --udp use UDP%s\n", opt_proto==SOCK_DGRAM ? " (default)" : "" );
exit(0);
}
void usage_small(char *name) {
fprintf(stderr, "type %s --help for help\n", name);
exit(0);
}
int
main(int argc, char **argv)
{
int listenfd, connfd, opt_fg = 0, opt_inetd = 0,
opt_port = -1, opt_mode = MODE_DISCARD, on = 1, opt_family = AF_INET;
pid_t childpid;
void sig_chld(int);
char fname[100], *name = NULL;
/* changing default, based on program name */
strncpy(fname, argv[0], sizeof(fname));
if ( (name = rindex(fname, '/')) ) {
name++;
} else {
name = fname;
}
opt_mode = name2mode(name);
while (1) {
static const struct option long_options[] = {
{ "foreground", no_argument, NULL, 'f' },
{ "help", no_argument, NULL, 'h' },
{ "inetd", no_argument, NULL, 'i' },
{ "ipv4", no_argument, NULL, '4' },
{ "ipv6", no_argument, NULL, '6' },
{ "mode", required_argument, NULL, 'm' },
{ "tcp", no_argument, NULL, 't' },
{ "udp", no_argument, NULL, 'u' },
{ NULL, 0, NULL, 0 } };
int optchar;
if ((optchar = getopt_long (argc, argv, "46fhim:p:tu", long_options, NULL)) == -1) break;
switch (optchar) {
#ifdef HAVE_GETADDRINFO
case '4': opt_family = AF_INET; break;
case '6': opt_family = AF_INET6; break;
#endif /* HAVE_GETADDRINFO */
case 'f': opt_fg = 1; break;
case 'h': usage(name); break;
case 'i': opt_inetd = 1; break;
case 'm': if ( (opt_mode = name2mode(optarg)) == -1 ) { printf("Unknown mode\n"); exit(1); }
break;
case 'p': opt_port = atoi(optarg);
if ((opt_port < 0) || (opt_port > 65535)) {
printf("Local port must be in 0..65535 range\n"); exit(1);
} break;
case 't': opt_proto = SOCK_STREAM; break;
case 'u': opt_proto = SOCK_DGRAM; break;
default: usage_small(name);
}
}
if (opt_port == -1) opt_port = defports[opt_mode];
if (opt_inetd) {
process(STDIN_FILENO, STDOUT_FILENO, opt_mode);
exit(0);
}
if (!opt_fg) daemon(0, 1);
if ( (listenfd = socket(opt_family, opt_proto, 0)) == -1 ) {
printf("socket(%d): %s\n", opt_family, strerror(errno)); exit(1);
}
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
if (opt_family == AF_INET) {
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = opt_family;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(opt_port);
if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) == -1) {
printf("bind(%d): %s\n", opt_family, strerror(errno)); exit(1);
}
#ifdef HAVE_GETADDRINFO
} else {
struct sockaddr_in6 servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin6_family = opt_family;
servaddr.sin6_flowinfo = 0;
servaddr.sin6_port = htons(opt_port);
servaddr.sin6_addr = in6addr_any; /* structure assignment */
if (bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) == -1) {
printf("bind(%d): %s\n", opt_family, strerror(errno)); exit(1);
}
#endif /* HAVE_GETADDRINFO */
}
listen(listenfd, LISTENQ);
signal(SIGCHLD, sig_chld);
for ( ; ; ) {
socklen_t clilen;
struct sockaddr_in cliaddr;
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR) continue; /* back to for() */
else strerror(errno);
}
if ( (childpid = fork()) == 0) { /* child process */
close(listenfd); /* close listening socket */
process(connfd, connfd, opt_mode); /* process the request */
exit(0);
}
close(connfd); /* parent closes connected socket */
}
return 0;
}
|