/* * (c) 2001 Dirk-Willem van Gulik, All Rights Reserved. See * http://www.webweaving.org/LICENSE for the license * * $Id: widentd.c 1573 2004-01-27 15:33:28Z dirkx $ * */ #define USAGE "widentd [-i ip] [-s port/service] [-v] [-o OS] [-u User]\n" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define pdie(x) { perror(x); exit(4); } #define BLOG (5) /* Listen queue - should not bee too high */ #define TIMEOUT (10) /* 10 second timeout - total transaction * less than 4 x Timeout */ #define UID "chuck-the-bsd-deamon" #define OS "OTHER" #define REPLY "%d , %d : USERID : %s : %s\r\n" #define SERVICE ("ident") /* 113 */ int verbose = 0; const char *user = UID; const char *os = OS; static void usage(void) { fprintf(stderr, USAGE); exit(1); } int main(int argc, char **argv) { char ch; const int yes = 1; int blog = BLOG; unsigned int i; char *myname = argv[0]; fd_set *lst = malloc(FD_SETSIZE), *xst = malloc(FD_SETSIZE), *wst = malloc(FD_SETSIZE); struct addrinfo *ports, *p; const char * node = NULL, * service = SERVICE; struct addrinfo hints; int s = -1; int e; while ((ch = getopt(argc, argv, "vo:u:s:i:")) != -1) switch (ch) { case 'v': verbose = 1; break; case 'u': user = optarg; for (i = 0; i < strlen(user); i++) if (!(isalnum(user[i]) || (user[i] == '@') || (user[i] == '-') || (user[i] == '.'))) { fprintf(stderr, "User name can only contiain a-z, '-', '@' and '.'.\n"); exit(1); } break; case 'o': os = optarg; for (i = 0; i < strlen(os); i++) if (!isalnum(os[i])) { fprintf(stderr, "OS name can only contain a-z.\n"); exit(1); } break; case 'i': node = optarg; break; case 's': service = optarg; break; case 'h': /* no break */ case '?': /* no break */ default: usage(); break; } argc -= optind; argv += optind; if (argc) usage(); if (verbose) printf("Inbound (i.e. listening to)\n\t"); else openlog(myname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); /* no error returned */ memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if ((e=getaddrinfo(node, service, & hints , &ports))) { fprintf(stderr,"interface/service errror: %s\n",gai_strerror(e)); exit(3); }; for( p = ports; p; p = p->ai_next ) { if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) pdie("Failed to open socket"); if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) pdie("Set socket reuse"); if (bind(s, p->ai_addr, p->ai_addrlen) < 0) pdie("Failed to bind in-port"); if (listen(s, blog) < 0) pdie("Listen"); } freeaddrinfo(ports); if (s == -1) pdie("No addresses to bind to"); if (verbose) printf("Wident waiting for requests\n"); else daemon(0, 0); signal(SIGPIPE, SIG_IGN); while (1) { unsigned char buff[1024]; struct sockaddr_storage con; int len = sizeof(con); int cons = accept(s, (struct sockaddr *)&con, &len); struct timeval to; int noreply = 0; enum { READ, WRITE } r_or_w = READ; unsigned int inbuff = 0; char ip[128]; char clienthost[ NI_MAXHOST ]; char clientserv[ NI_MAXSERV ]; if (cons == -1) { perror("Error on accept"); continue; }; if (fcntl(cons, O_NONBLOCK, &yes) == -1) { perror("Error when making non blocking"); close(cons); continue; }; memset(ip,0,sizeof(ip)); memset(clienthost,0,sizeof(clienthost)); memset(clientserv,0,sizeof(clientserv)); getnameinfo((struct sockaddr *)&con, len, clienthost,sizeof(clienthost), clientserv,sizeof(clientserv), NI_NUMERICHOST | NI_NUMERICSERV); snprintf(ip, sizeof(ip) - 1, "%s:%s", clienthost, clientserv); if (verbose) printf("Incoming commection from %s\n", ip); else syslog(LOG_INFO, "widentd rq from %s\n", ip); to.tv_sec = TIMEOUT; to.tv_usec = 0; memset(buff, sizeof(buff), 0); do { int n; FD_ZERO(lst); FD_ZERO(xst); FD_ZERO(wst); if (r_or_w == READ) FD_SET(cons, lst); else FD_SET(cons, wst); n = select(cons + 1, lst, wst, xst, &to); if (n == 0) { if (verbose) fprintf(stderr, "Timeout waiting for %s\n", ip); else syslog(LOG_NOTICE, "widentd timeout for %s\n", ip); break; } else if ((n == -1) && (errno == EAGAIN)) continue; else if (n == -1) { if (verbose) fprintf(stderr, "Select error:%s for %s\n", strerror(errno), ip); else syslog(LOG_ERR, "widentd select error:%s for %s\n", strerror(errno), ip); break; } if (FD_ISSET(cons, xst)) break; if (r_or_w == READ) n = read(cons, buff + inbuff, sizeof(buff) - inbuff); else n = write(cons, buff + inbuff, strlen(buff) - inbuff); if (n == 0) break; /* normal close */ else if ((n == -1) && (errno == EAGAIN)) continue; /* normal interrupt. */ else if (n == -1) { if (verbose) fprintf(stderr, "I/O error:%s\n", strerror(errno)); else syslog(LOG_ERR, "widentd i/o error:%s\n", strerror(errno)); break; } if (r_or_w == READ) { /* We are still in reading mode */ for (i = inbuff; (r_or_w == READ) && (i < inbuff + n); i++) { /* * if we see any CR or LF - consider * the read done */ if ((buff[i] == '\r') || (buff[i] == '\n')) { int a, b; buff[i] = '\0'; if (sscanf(buff, "%d , %d", &a, &b) != 2) { syslog(LOG_NOTICE,"Malformed auth line from %s",ip); a = b = 1; } snprintf(buff, sizeof(buff) - 1, REPLY, a, b, os, user); if (verbose) printf("Sending %s\n", buff); /* no further reads expected. */ shutdown(cons, SHUT_RD); r_or_w = WRITE; inbuff = 0; } } /* * If we've not yet seen a CR or LF, keep * listening but keep a counter to guard * against slow feeding DOS style things. */ if (r_or_w == READ) { inbuff += n; /* * just bail out if things get too * big */ if (inbuff >= sizeof(buff)) { syslog(LOG_NOTICE,"Extremely long line from %s",ip); break; }; }; } else { inbuff += n; if (inbuff >= strlen(buff)) break; /* we're done */ } /* * Try the read/write's about three times; and then * bail out. */ if ((noreply++) > 3) { syslog(LOG_NOTICE,"Extremely Slow feed from %s",ip); break; } } while (1); if (verbose) printf("And closing\n"); /* shutdown for read and write */ shutdown(cons, SHUT_RDWR); close(cons); } while (1); /* NOT REACHED */ }