/* $Id: smtpdcheck.c,v 1.5 2004/12/28 16:37:25 dm Exp $ */ /* * * Copyright (C) 2004 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "avutil.h" #include #include #include #include #include #include #include #include "getopt_long.h" char *progname; char *stop_name; struct in_addr stop_ip; int stop_ip_set; #ifndef HAVE_STRCASECMP int strcasecmp (const char *s1, const char *s2) { while (tolower (*s1) == tolower (*s2++)) if (!*s1++) return 0; return tolower (*s1) - tolower (s2[-1]); } #endif /* HAVE_STRCASECMP */ static int check_stop (struct hostent *h) { int i; if (stop_name && !strcasecmp (stop_name, h->h_name)) return 1; for (i = 0; h->h_addr_list[i]; i++) if (stop_ip.s_addr == ((struct in_addr *) h->h_addr_list[i])->s_addr) return 1; return 0; } static sigjmp_buf tmojump; static void dojump (int sig) { siglongjmp (tmojump, sig); } /* Return: 1 if server ok, 0 if server not ok, -1 if stop */ static int check_server (const char *name, int timeout) { struct hostent *h; struct sockaddr_in sin; int s; struct sigaction sa, osa; int n; char buf[4]; s = socket (AF_INET, SOCK_STREAM, 0); bzero (&sin, sizeof (sin)); sin.sin_family = AF_INET; if (s < 0 || bind (s, (struct sockaddr *) &sin, sizeof (sin)) < 0) { perror ("TCP socket"); exit (2); } sin.sin_port = htons (25); bzero (&sa, sizeof (sa)); sa.sa_handler = dojump; sa.sa_flags = SA_RESETHAND; if (sigaction (SIGALRM, &sa, &osa) < 0) { perror ("sigaction"); exit (2); } if (sigsetjmp (tmojump, 1)) { sigaction (SIGALRM, &osa, NULL); close (s); return 0; } alarm (timeout); h = gethostbyname (name); if (!h) { alarm (0); sigaction (SIGALRM, &osa, NULL); close (s); return 0; } sin.sin_addr = *(struct in_addr *) h->h_addr; if (check_stop (h)) { alarm (0); sigaction (SIGALRM, &osa, NULL); close (s); return -1; } if (connect (s, (struct sockaddr *) &sin, sizeof (sin)) < 0) { alarm (0); sigaction (SIGALRM, &osa, NULL); close (s); return 0; } n = read (s, buf, sizeof (buf)); alarm (0); sigaction (SIGALRM, &osa, NULL); close (s); if (n < 4 || buf[0] != '2' || !isdigit (buf[1]) || !isdigit (buf[2]) || (buf[3] != ' ' && buf[3] != '-' && buf[3] != '\r')) return 0; return 1; } static void usage (void) __attribute__ ((noreturn)); static void usage (void) { fprintf (stderr, "usage: %s [--stop {ip-addr|name}] [prio:]server1" " [[prio:]server2 ...]\n", progname); exit (1); } int main (int argc, char **argv) { struct option o[] = { { "version", no_argument, NULL, 'v' }, { "stop", required_argument, NULL, 's' }, { "timeout", required_argument, NULL, 't' }, { NULL, 0, NULL, 0 } }; int c, n; int timeout = 10; int ok_pri = -1; char *ok_name = NULL; progname = strrchr (argv[0], '/'); if (progname) progname++; else progname = argv[0]; while ((c = getopt_long (argc, argv, "+s:t:", o, NULL)) != -1) switch (c) { case 'v': version (progname, 1); break; case 's': if (stop_ip_set || stop_name) usage (); else if (inet_aton (optarg, &stop_ip) == 1) stop_ip_set = 1; else { struct hostent *h = gethostbyname (optarg); if (!h) { fprintf (stderr, "%s: no such host\n", optarg); exit (2); } stop_name = xmalloc (1 + strlen (h->h_name)); strcpy (stop_name, h->h_name); stop_ip_set = 1; stop_ip = *(struct in_addr *) h->h_addr; } break; case 't': if (sscanf (optarg, "%u%n", &timeout, &n) != 1 || timeout < 0 || (size_t) n != strlen (optarg)) usage (); break; default: usage (); } for (; optind < argc; optind++) { int pri; char *p; n = 0; sscanf (argv[optind], "%d:%n", &pri, &n); if (!n) { pri = -1; p = argv[optind]; } else p = argv[optind] + n; if (ok_name && pri > ok_pri) break; switch (check_server (p, timeout)) { case -1: exit (0); break; case 1: ok_name = p; ok_pri = pri; break; } } if (ok_name) { printf ("%s\n", ok_name); exit (1); } exit (0); }