domain.c
上传用户:zibowangxu
上传日期:2007-01-04
资源大小:331k
文件大小:19k
源码类别:

Ftp客户端

开发平台:

Unix_Linux

  1. /****************************************************************************  
  2.  
  3.   Copyright (c) 1999 WU-FTPD Development Group.  
  4.   All rights reserved.
  5.   
  6.   Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
  7.     The Regents of the University of California.
  8.   Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
  9.   Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
  10.   Portions Copyright (c) 1989 Massachusetts Institute of Technology.
  11.   Portions Copyright (c) 1998 Sendmail, Inc.
  12.   Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P.  Allman.
  13.   Portions Copyright (c) 1997 by Stan Barber.
  14.   Portions Copyright (c) 1997 by Kent Landfield.
  15.   Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
  16.     Free Software Foundation, Inc.  
  17.  
  18.   Use and distribution of this software and its source code are governed 
  19.   by the terms and conditions of the WU-FTPD Software License ("LICENSE").
  20.  
  21.   If you did not receive a copy of the license, it may be obtained online
  22.   at http://www.wu-ftpd.org/license.html.
  23.  
  24.   $Id: domain.c,v 1.10 1999/09/19 19:21:23 wuftpd Exp $
  25.  
  26. ****************************************************************************/
  27. /*
  28.  * domain.c  - DNS functions for WU-FTPD using res_*
  29.  *
  30.  * INITIAL AUTHOR - *      Nikos Mouat    <nikm@cyberflunk.com>
  31.  */
  32. #include "config.h"
  33. #include <stdlib.h>
  34. #include "proto.h"
  35. #ifdef HAVE_LIBRESOLV
  36. #include <sys/types.h>
  37. #include <sys/param.h>
  38. #include <stdio.h>
  39. #include <string.h>
  40. #include <netdb.h>
  41. #include <sys/socket.h>
  42. #include <netinet/in.h>
  43. #include <arpa/inet.h>
  44. #ifndef NO_DNS
  45. #include <arpa/nameser.h>
  46. #include <resolv.h>
  47. #ifndef INT16SZ
  48. #define INT16SZ sizeof(u_short)
  49. #endif /* !INT16SZ */
  50. #ifndef INT32SZ
  51. #define INT32SZ sizeof(u_long)
  52. #endif /* !INT32SZ */
  53. #ifndef INADDRSZ
  54. #define INADDRSZ sizeof(struct in_addr)
  55. #endif /* !INADDRSZ */
  56. #ifndef HFIXEDSZ
  57. #define HFIXEDSZ sizeof(HEADER)
  58. #endif /* !HFIXEDSZ */
  59. #endif /* !NO_DNS */
  60. #include "extensions.h"
  61. /* these should go in a new ftpd.h perhaps? config.h doesn't seem appropriate */
  62. /* and there does not appear to be a global include file                      */
  63. #ifndef TRUE
  64. #define  TRUE   1
  65. #endif
  66. #ifndef FALSE
  67. #define  FALSE  !TRUE
  68. #endif
  69. struct t_resolver_options {
  70.     char *token;
  71.     int value;
  72. } resolver_options[] = {
  73. #ifdef RES_DEBUG
  74.     {
  75. "debug", RES_DEBUG
  76.     },
  77. #endif
  78. #ifdef RES_AAONLY
  79.     {
  80. "aaonly", RES_AAONLY
  81.     },
  82. #endif
  83. #ifdef RES_USEVC
  84.     {
  85. "usevc", RES_USEVC
  86.     },
  87. #endif
  88. #ifdef RES_STAYOPEN
  89.     {
  90. "stayopen", RES_STAYOPEN
  91.     },
  92. #endif
  93. #ifdef RES_PRIMARY
  94.     {
  95. "primary", RES_PRIMARY
  96.     },
  97. #endif
  98. #ifdef RES_IGNTC
  99.     {
  100. "igntc", RES_IGNTC
  101.     },
  102. #endif
  103. #ifdef RES_RECURSE
  104.     {
  105. "recurse", RES_RECURSE
  106.     },
  107. #endif
  108. #ifdef RES_DEFNAMES
  109.     {
  110. "defnames", RES_DEFNAMES
  111.     },
  112. #endif
  113. #ifdef RES_DNSRCH
  114.     {
  115. "dnsrch", RES_DNSRCH
  116.     },
  117. #endif
  118. #ifdef RES_INSECURE1
  119.     {
  120. "insecure1", RES_INSECURE1
  121.     },
  122. #endif
  123. #ifdef RES_INSECURE2
  124.     {
  125. "insecure2", RES_INSECURE2
  126.     },
  127. #endif
  128. #ifdef RES_NOALIASES
  129.     {
  130. "noaliases", RES_NOALIASES
  131.     },
  132. #endif
  133. #ifdef RES_USE_INET6
  134.     {
  135. "use_inet6", RES_USE_INET6
  136.     },
  137. #endif
  138. #ifdef RES_ROTATE
  139.     {
  140. "rotate", RES_ROTATE
  141.     },
  142. #endif
  143. #ifdef RES_NOCHECKNAME
  144.     {
  145. "nocheckname", RES_NOCHECKNAME
  146.     },
  147. #endif
  148. #ifdef RES_KEEPSIG
  149.     {
  150. "keepsig", RES_KEEPSIG
  151.     },
  152. #endif
  153. #ifdef RES_DEFAULT
  154.     {
  155. "default", RES_DEFAULT
  156.     },
  157. #endif
  158.     {
  159. NULL, 0
  160.     }
  161. };
  162. /* globals */
  163. int resolver_initialized = FALSE;
  164. char *remote_hostname = NULL;
  165. char *remote_address = NULL;
  166. int has_reverse_dns = FALSE;
  167. int has_matching_dns = FALSE;
  168. /* Prototypes */
  169. int check_matching_dns(void);
  170. int lookup_ip(char *ip, char **fqdn);
  171. int check_name_for_ip(char *name, char *ip);
  172. int initialize_dns(struct sockaddr_in *remote_socket);
  173. int check_reverse_dns(void);
  174. /* types for res_* answers */
  175. #ifndef NO_DNS
  176. typedef union {
  177.     HEADER qb1;
  178.     u_char qb2[PACKETSZ];
  179. } querybuf;
  180. #endif
  181. /****************************************************************************
  182.  * lookup_name()
  183.  *   This routine takes a FQDN and tries to find a valid IP address.
  184.  *   If a CNAME is returned, it looks further on in the reply for an
  185.  *   A record. It will return as soon as it finds an A record, so hosts
  186.  *   with multiple A records will have the first A record returned.
  187.  ***************************************************************************/
  188. int lookup_name(char *name, char **ip)
  189. {
  190. #ifndef NO_DNS
  191.     u_char *msg_end, *msg_ptr;
  192. #ifdef USE_RES_SEND
  193.     querybuf question, answer;
  194. #else
  195.     querybuf answer;
  196. #endif
  197.     int rc, num_answers, num_query, q_type, q_class, q_ttl, q_len;
  198.     char query_name[MAXDNAME + 1];
  199.     struct in_addr inaddr;
  200.     char *result;
  201.     /* res_mkquery+res_send seem to function identically to res_query, so I'm */
  202.     /* using res_query. If there's some advantage to using res_mkquery that I */
  203.     /* don't know about (which wouldn't surprise me :) then you can just      */
  204.     /* define USE_RES_SEND and it will use that method                        */
  205. #ifdef USE_RES_SEND
  206.     rc = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, question.qb2, sizeof(querybuf));
  207.     if (rc < 0) {
  208. return FALSE;
  209.     }
  210.     rc = res_send(question.qb2, rc, answer.qb2, sizeof(querybuf));
  211. #else
  212.     rc = res_query(name, C_IN, T_A, answer.qb2, sizeof(querybuf));
  213. #endif
  214.     if (rc < 0) {
  215. return FALSE;
  216.     }
  217.     msg_end = (u_char *) & answer + rc;
  218.     num_answers = ntohs(answer.qb1.ancount);
  219.     num_query = ntohs(answer.qb1.qdcount);
  220.     if (num_answers < 1) {
  221. /* No answers mean that this hostname doesn't exist..                 */
  222. return FALSE;
  223.     }
  224.     msg_ptr = answer.qb2 + HFIXEDSZ;
  225.     /* skip over the query */
  226.     for (; (num_query > 0) && (msg_ptr < msg_end); num_query--) {
  227. msg_ptr += dn_skipname(msg_ptr, msg_end) + QFIXEDSZ;
  228.     }
  229.     for (; (num_answers > 0) && (msg_ptr < msg_end); num_answers--) {
  230. rc = dn_expand(answer.qb2, msg_end, msg_ptr, (void *) query_name, MAXDNAME);
  231. msg_ptr += rc;
  232. q_type = _getshort(msg_ptr);
  233. msg_ptr += INT16SZ;
  234. q_class = _getshort(msg_ptr);
  235. msg_ptr += INT16SZ;
  236. q_ttl = _getlong(msg_ptr);
  237. msg_ptr += INT32SZ;
  238. q_len = _getshort(msg_ptr);
  239. msg_ptr += INT16SZ;
  240. /* look at the type of response that we recieved. If it's a CNAME then */
  241. /* we need to find out what the CNAME's IP is.                         */
  242. switch (q_type) {
  243. case T_CNAME:
  244.     /* Got a CNAME - hope that there are other answers with the A */
  245.     msg_ptr += q_len;
  246.     break;
  247. case T_A:
  248.     bcopy(msg_ptr, (char *) &inaddr, INADDRSZ);
  249.     msg_ptr += q_len;
  250.     result = inet_ntoa(inaddr);
  251.     *ip = (char *) malloc(strlen(result) + 1);
  252.     if (*ip == NULL)
  253. return FALSE;
  254.     strcpy(*ip, result);
  255.     return TRUE;
  256. default:
  257.     /* what the ?? - we don't expect this response type */
  258.     return FALSE;
  259. }
  260.     }
  261.     /* oh dear, there was no A's in the response */
  262. #endif /* !NO_DNS */
  263.     return FALSE;
  264. }
  265. /****************************************************************************
  266.  * lookup_ip()
  267.  *   This routine takes an IP address in the format a.b.c.d and returns the
  268.  *   hostname.
  269.  ***************************************************************************/
  270. int lookup_ip(char *ip, char **fqdn)
  271. {
  272. #ifndef NO_DNS
  273.     u_char *msg_end, *msg_ptr;
  274. #ifdef USE_RES_SEND
  275.     querybuf question, answer;
  276. #else
  277.     querybuf answer;
  278. #endif
  279.     int rc, num_answers, num_query, q_type, q_class, q_ttl, q_len;
  280.     char query_name[MAXDNAME + 1];
  281.     char in_addr[MAXDNAME + 1];
  282.     unsigned int a, b, c, d;
  283.     a = b = c = d = 0;
  284.     sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d);
  285.     sprintf(in_addr, "%u.%u.%u.%u.in-addr.arpa", d, c, b, a);
  286. #ifdef USE_RES_SEND
  287.     rc = res_mkquery(QUERY, in_addr, C_IN, T_PTR, NULL, 0, NULL, question.qb2, sizeof(querybuf));
  288.     if (rc < 0) {
  289. return FALSE;
  290.     }
  291.     rc = res_send(question.qb2, rc, answer.qb2, sizeof(querybuf));
  292. #else
  293.     rc = res_query(in_addr, C_IN, T_PTR, answer.qb2, sizeof(querybuf));
  294. #endif
  295.     if (rc < 0) {
  296. return FALSE;
  297.     }
  298.     msg_end = (u_char *) & answer + rc;
  299.     num_answers = ntohs(answer.qb1.ancount);
  300.     num_query = ntohs(answer.qb1.qdcount);
  301.     if (num_answers < 1) {
  302. /* No answers mean that this hostname doesn't exist..                 */
  303. return FALSE;
  304.     }
  305.     msg_ptr = answer.qb2 + HFIXEDSZ;
  306.     /* skip over the query */
  307.     for (; (num_query > 0) && (msg_ptr < msg_end); num_query--) {
  308. msg_ptr += dn_skipname(msg_ptr, msg_end) + QFIXEDSZ;
  309.     }
  310.     for (; num_answers > 0 && (msg_ptr < msg_end); num_answers--) {
  311. rc = dn_expand(answer.qb2, msg_end, msg_ptr, (void *) query_name, MAXDNAME);
  312. /* increment our pointer */
  313. msg_ptr += rc;
  314. /* read off the various answer information */
  315. q_type = _getshort(msg_ptr);
  316. msg_ptr += INT16SZ;
  317. q_class = _getshort(msg_ptr);
  318. msg_ptr += INT16SZ;
  319. q_ttl = _getlong(msg_ptr);
  320. msg_ptr += INT32SZ;
  321. q_len = _getshort(msg_ptr);
  322. msg_ptr += INT16SZ;
  323. switch (q_type) {
  324. case T_PTR:
  325.     /* this is our answer, expand the name, allocate space for it and */
  326.     /* set the return information                                     */
  327.     rc = dn_expand(answer.qb2, msg_end, msg_ptr, (void *) query_name, MAXDNAME);
  328.     if (rc >= 0) {
  329. *fqdn = (char *) malloc(strlen(query_name) + 1);
  330. if (*fqdn == NULL)
  331.     return FALSE;
  332. strcpy(*fqdn, query_name);
  333. return TRUE;
  334.     }
  335.     break;
  336. default:
  337.     /* unknown response type.. keep looking */
  338.     msg_ptr += q_len;
  339. }
  340.     }
  341. #endif /* !NO_DNS */
  342.     return FALSE;
  343. }
  344. /****************************************************************************
  345.  * check_name_for_ip()
  346.  *   This routine checks to see if a given IP address is a valid IP for a
  347.  *   given name. We need this because lookup_name() only returns the first
  348.  *   IP address in the response, and if a user is coming from a different
  349.  *   IP address for the same machine, we want to make sure that we match
  350.  *   them.
  351.  ***************************************************************************/
  352. int check_name_for_ip(char *name, char *ip)
  353. {
  354. #ifndef NO_DNS
  355. #ifdef USE_RES_SEND
  356.     querybuf question, answer;
  357. #else
  358.     querybuf answer;
  359. #endif
  360.     u_char *msg_end, *msg_ptr;
  361.     char query_name[MAXDNAME + 1];
  362.     int rc, num_answers, num_query, q_type, q_class, q_ttl, q_len;
  363.     struct in_addr inaddr, qaddr;
  364.     /* convert the passed IP address into an in_addr (for comparison later) */
  365.     /* not all systems have inet_aton(), so use inet_addr() */
  366.     if ((qaddr.s_addr = inet_addr(ip)) == (u_long) - 1) {
  367. return FALSE;
  368.     }
  369. #ifdef USE_RES_SEND
  370.     rc = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, question.qb2, sizeof(querybuf));
  371.     if (rc < 0) {
  372. return FALSE;
  373.     }
  374.     rc = res_send(question.qb2, rc, answer.qb2, sizeof(querybuf));
  375. #else
  376.     rc = res_query(name, C_IN, T_A, answer.qb2, sizeof(querybuf));
  377. #endif
  378.     if (rc < 0) {
  379. return FALSE;
  380.     }
  381.     msg_end = (u_char *) & answer + rc;
  382.     num_answers = ntohs(answer.qb1.ancount);
  383.     num_query = ntohs(answer.qb1.qdcount);
  384.     if (num_answers < 1) {
  385. /* No answers mean that this hostname doesn't exist..                 */
  386. return FALSE;
  387.     }
  388.     msg_ptr = answer.qb2 + HFIXEDSZ;
  389.     /* skip over the query */
  390.     for (; (num_query > 0) && (msg_ptr < msg_end); num_query--) {
  391. msg_ptr += dn_skipname(msg_ptr, msg_end) + QFIXEDSZ;
  392.     }
  393.     for (; (num_answers > 0) && (msg_ptr < msg_end); num_answers--) {
  394. rc = dn_expand(answer.qb2, msg_end, msg_ptr, (void *) query_name, MAXDNAME);
  395. msg_ptr += rc;
  396. q_type = _getshort(msg_ptr);
  397. msg_ptr += INT16SZ;
  398. q_class = _getshort(msg_ptr);
  399. msg_ptr += INT16SZ;
  400. q_ttl = _getlong(msg_ptr);
  401. msg_ptr += INT32SZ;
  402. q_len = _getshort(msg_ptr);
  403. msg_ptr += INT16SZ;
  404. /* look at the type of response that we recieved. If it's a CNAME then */
  405. /* we need to find out what the CNAME's IP is.                         */
  406. switch (q_type) {
  407. case T_CNAME:
  408.     /* Got a CNAME - hope that there are other answers with the A */
  409.     msg_ptr += q_len;
  410.     break;
  411. case T_A:
  412.     bcopy(msg_ptr, (char *) &inaddr, INADDRSZ);
  413.     msg_ptr += q_len;
  414.     if (memcmp(&inaddr, &qaddr, sizeof(struct in_addr)) == 0) {
  415. return TRUE;
  416.     }
  417.     break;
  418. default:
  419.     /* what the ?? - we don't expect this response type */
  420.     return FALSE;
  421. }
  422.     }
  423. #endif /* !NO_DNS */
  424.     /* no matching IP's */
  425.     return FALSE;
  426. }
  427. /****************************************************************************
  428.  * initialize_dns()
  429.  *    initialize the DNS subsystem, set resolver options and collect global
  430.  *    variables. The remote socket is passed to this routine so that it
  431.  *    can set DNS variables related to the remote site. 
  432.  ***************************************************************************/
  433. int initialize_dns(struct sockaddr_in *remote_socket)
  434. {
  435. #ifndef NO_DNS
  436.     struct aclmember *entry = NULL;
  437.     char *temp_pointer;
  438.     if (resolver_initialized)
  439. return TRUE;
  440.     /* check to see if the resolver has been initialized already */
  441.     if ((_res.options & RES_INIT) == 0) {
  442. if ((res_init()) == -1) {
  443.     /* failed to initialize the resolver */
  444.     return FALSE;
  445. }
  446.     }
  447.     /* load the 'dns resolveroptions X' entries from the config. This will */
  448.     /* change when we have the new config file parsing methods             */
  449.     while (getaclentry("dns", &entry) && ARG0 && ARG1 != NULL) {
  450. /* there are other DNS options, we only care about 'resolveroptions' */
  451. if (!strcasecmp(ARG0, "resolveroptions")) {
  452.     int arg_count;
  453.     /* read in the options, and set _res.options appropriately        */
  454.     for (arg_count = 1; ARG[arg_count] != NULL; arg_count++) {
  455. int operation = 0;
  456. char *option;
  457. int table_index;
  458. int option_bitvalue;
  459. if (ARG[arg_count][0] == '-') {
  460.     operation = -1; /* want to UNSET option */
  461.     option = &ARG[arg_count][1];
  462. }
  463. else if (ARG[arg_count][0] == '+') {
  464.     operation = 1; /* want to SET option */
  465.     option = &ARG[arg_count][1];
  466. }
  467. if (operation == 0) {
  468.     /* no operation specified, assume they meant + so do a SET */
  469.     operation = 1;
  470.     option = &ARG[arg_count][0];
  471. }
  472. option_bitvalue = 0;
  473. /* now lookup option bit value in the resolver_options table */
  474. for (table_index = 0; resolver_options[table_index].token != NULL;
  475.      table_index++) {
  476.     if (!strcasecmp(option, resolver_options[table_index].token)) {
  477. option_bitvalue = resolver_options[table_index].value;
  478.     }
  479. }
  480. /* make sure that we have a valid operation to perform */
  481. if (option_bitvalue == 0) {
  482.     /* nope. keep looking at other args. */
  483.     continue;
  484. }
  485. /* okay, let's do this operation then */
  486. if (operation < 0) {
  487.     /* turn *off* option */
  488.     _res.options &= ~option_bitvalue;
  489. }
  490. else {
  491.     _res.options |= option_bitvalue;
  492. }
  493.     }
  494. }
  495.     }
  496.     /* save the remote address */
  497.     temp_pointer = inet_ntoa(remote_socket->sin_addr);
  498.     remote_address = (char *) malloc(strlen(temp_pointer) + 1);
  499.     if (remote_address == NULL) {
  500. /* memory error */
  501. return FALSE;
  502.     }
  503.     /* size should be identical */
  504.     strcpy(remote_address, temp_pointer);
  505.     /* the old code checks remote_address for 0.0.0.0 and if there's a   */
  506.     /* match, sets the hostname to 'localhost' - so let's do that for    */
  507.     /* compatibility and perhaps unknown resolvers which behave this way */
  508.     if (!strcmp(remote_address, "0.0.0.0")) {
  509. remote_hostname = "localhost";
  510.     }
  511.     else if (lookup_ip(remote_address, &temp_pointer)) {
  512. has_reverse_dns = TRUE;
  513. /* save the remote hostname (returned from lookup_ip()) */
  514. remote_hostname = (char *) malloc(strlen(temp_pointer) + 1);
  515. if (remote_hostname == NULL) {
  516.     /* memory error */
  517.     return FALSE;
  518. }
  519. /* size should be identical */
  520. strcpy(remote_hostname, temp_pointer);
  521. /* ok, we should now have hostname and address based on the real */
  522. /* IP address. Let's check the forward DNS of remote_hostname    */
  523. /* and see if we get the same address..                          */
  524. has_matching_dns = check_name_for_ip(remote_hostname, inet_ntoa(remote_socket->sin_addr));
  525.     }
  526.     else {
  527. has_reverse_dns = FALSE;
  528. has_matching_dns = TRUE; /* no reverse, nothing to match */
  529.     }
  530.     resolver_initialized = TRUE;
  531. #endif /* !NO_DNS */
  532.     return TRUE;
  533. }
  534. /****************************************************************************
  535.  * check_reverse_dns()
  536.  ***************************************************************************/
  537. int check_reverse_dns(void)
  538. {
  539. #ifndef NO_DNS
  540.     struct aclmember *entry = NULL;
  541.     int rc = TRUE;
  542.     /* check the config to see if we care */
  543.     while (getaclentry("dns", &entry) && ARG0 && ARG1 != NULL) {
  544. if (!strcasecmp(ARG0, "refuse_no_reverse")) {
  545.     FILE *msg_file;
  546.     char linebuf[MAXPATHLEN];
  547.     char outbuf[MAXPATHLEN];
  548.     int code = 530;
  549.     char *crptr;
  550.     /* ok, so configuration is telling us to not allow connections */
  551.     /* that don't have any reverse DNS                             */
  552.     if (!has_reverse_dns) {
  553. /* ok, so we need to kick out this user */
  554. /* check to see if admin wants to override */
  555. if (ARG2 && (!strcasecmp(ARG2, "override"))) {
  556.     /* Administrative override - but display the warning anyway */
  557.     code = 220;
  558. }
  559. msg_file = fopen(ARG1, "r");
  560. if (msg_file != NULL) {
  561.     while (fgets(linebuf, sizeof(linebuf), msg_file)) {
  562. if ((crptr = strchr(linebuf, 'n')) != NULL)
  563.     *crptr = '';
  564. msg_massage(linebuf, outbuf, sizeof(outbuf));
  565. lreply(code, "%s", outbuf);
  566.     }
  567.     fclose(msg_file);
  568. #ifndef NO_SUCKING_NEWLINES
  569.     lreply(code, "");
  570. #endif
  571.     if (code == 530) {
  572. reply(code, "");
  573. rc = FALSE;
  574.     }
  575.     else {
  576. lreply(code, "Administrative Override. Permission granted.");
  577. lreply(code, "");
  578.     }
  579. }
  580.     }
  581. }
  582.     }
  583.     return rc;
  584. #else /* NO_DNS */
  585.     return TRUE;
  586. #endif
  587. }
  588. /****************************************************************************
  589.  * check_matching_dns()
  590.  ***************************************************************************/
  591. int check_matching_dns(void)
  592. {
  593. #ifndef NO_DNS
  594.     struct aclmember *entry = NULL;
  595.     int rc = TRUE;
  596.     /* check the config to see if we care */
  597.     while (getaclentry("dns", &entry) && ARG0 && ARG1 != NULL) {
  598. if (!strcasecmp(ARG0, "refuse_mismatch")) {
  599.     FILE *msg_file;
  600.     char linebuf[MAXPATHLEN];
  601.     char outbuf[MAXPATHLEN];
  602.     int code = 530;
  603.     char *crptr;
  604.     /* ok, so configuration is telling us to not allow connections */
  605.     /* that don't have any reverse DNS                             */
  606.     if (!has_matching_dns) {
  607. /* ok, so we need to kick out this user */
  608. /* check to see if admin wants to override */
  609. if (ARG2 && (!strcasecmp(ARG2, "override"))) {
  610.     /* Administrative override - but display the warning anyway */
  611.     code = 220;
  612. }
  613. msg_file = fopen(ARG1, "r");
  614. if (msg_file != NULL) {
  615.     while (fgets(linebuf, sizeof(linebuf), msg_file)) {
  616. if ((crptr = strchr(linebuf, 'n')) != NULL)
  617.     *crptr = '';
  618. msg_massage(linebuf, outbuf, sizeof(outbuf));
  619. lreply(code, "%s", outbuf);
  620.     }
  621.     fclose(msg_file);
  622. #ifndef NO_SUCKING_NEWLINES
  623.     lreply(code, "");
  624. #endif
  625.     if (code == 530) {
  626. reply(code, "");
  627. rc = FALSE;
  628.     }
  629.     else {
  630. lreply(code, "Administrative Override. Permission granted.");
  631. lreply(code, "");
  632.     }
  633. }
  634.     }
  635. }
  636.     }
  637.     return rc;
  638. #else /* NO_DNS */
  639.     return TRUE;
  640. #endif
  641. }
  642. #endif /* HAVE_LIBRESOLV */