interface.c
上传用户:xxcykj
上传日期:2007-01-04
资源大小:727k
文件大小:12k
源码类别:

Email客户端

开发平台:

Unix_Linux

  1. /*
  2.  * interface.c -- implements fetchmail 'interface' and 'monitor' commands
  3.  *
  4.  * This module was implemented by George M. Sipe <gsipe@pobox.com>
  5.  * or <gsipe@acm.org> and is:
  6.  *
  7.  * Copyright (c) 1996,1997 by George M. Sipe
  8.  *
  9.  *      FreeBSD specific portions written by and Copyright (c) 1999 
  10.  *      Andy Doran <ad@psn.ie>.
  11.  *
  12.  * This is free software; you can redistribute it and/or modify it under
  13.  * the terms of the GNU General Public License as published by the Free
  14.  * Software Foundation; version 2, or (at your option) any later version.
  15.  */
  16. #include <sys/types.h>
  17. #include <sys/param.h>
  18. #if (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__)
  19. #include "config.h"
  20. #include <stdio.h>
  21. #include <string.h>
  22. #if defined(STDC_HEADERS)
  23. #include <stdlib.h>
  24. #endif
  25. #if defined(HAVE_UNISTD_H)
  26. #include <unistd.h>
  27. #endif
  28. #include <sys/ioctl.h>
  29. #include <sys/socket.h>
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #include <net/if.h>
  33. #if defined(__FreeBSD__)
  34. #if __FreeBSD_version >= 300001
  35. #include <net/if_var.h>
  36. #endif
  37. #include <kvm.h>
  38. #include <nlist.h>
  39. #include <sys/fcntl.h>
  40. #endif
  41. #include "config.h"
  42. #include "fetchmail.h"
  43. #include "i18n.h"
  44. #include "tunable.h"
  45. typedef struct {
  46. struct in_addr addr, dstaddr, netmask;
  47. int rx_packets, tx_packets;
  48. } ifinfo_t;
  49. struct interface_pair_s {
  50. struct in_addr interface_address;
  51. struct in_addr interface_mask;
  52. } *interface_pair;
  53. static char *netdevfmt;
  54. /*
  55.  * Count of packets to see on an interface before monitor considers it up.
  56.  * Needed because when pppd shuts down the link, the packet counts go up
  57.  * by two (one rx and one tx?, maybe).  A value of 2 seems to do the trick,
  58.  * but we'll give it some extra.
  59.  */
  60. #define MONITOR_SLOP 5
  61. #if defined(linux)
  62. void interface_init(void)
  63. /* figure out which /proc/dev/net format to use */
  64. {
  65.     FILE *fp = popen("uname -r", "r"); /* still wins if /proc is out */
  66.     /* pre-linux-2.2 format -- transmit packet count in 8th field */
  67.     netdevfmt = "%d %d %*d %*d %*d %d %*d %d %*d %*d %*d %*d %d";
  68.     if (!fp)
  69. return;
  70.     else
  71.     {
  72. int major, minor;
  73. if (fscanf(fp, "%d.%d.%*d", &major, &minor) != 2)
  74.     return;
  75. if (major >= 2 && minor >= 2)
  76.     /* Linux 2.2 -- transmit packet count in 10th field */
  77.     netdevfmt = "%d %d %*d %*d %*d %d %*d %*d %*d %d %*d %*d %d";
  78.     }
  79. }
  80. static int _get_ifinfo_(int socket_fd, FILE *stats_file, const char *ifname,
  81. ifinfo_t *ifinfo)
  82. /* get active network interface information - return non-zero upon success */
  83. {
  84. int namelen = strlen(ifname);
  85. struct ifreq request;
  86. char *cp, buffer[256];
  87. int found = 0;
  88. int counts[4];
  89. /* initialize result */
  90. memset((char *) ifinfo, 0, sizeof(ifinfo_t));
  91. /* get the packet I/O counts */
  92. while (fgets(buffer, sizeof(buffer) - 1, stats_file)) {
  93. for (cp = buffer; *cp && *cp == ' '; ++cp);
  94. if (!strncmp(cp, ifname, namelen) &&
  95. cp[namelen] == ':') {
  96. cp += namelen + 1;
  97. if (sscanf(cp, netdevfmt,
  98.    counts, counts+1, counts+2, 
  99.    counts+3,&found)>4) { /* found = dummy */
  100.         /* newer kernel with byte counts */
  101.         ifinfo->rx_packets=counts[1];
  102.         ifinfo->tx_packets=counts[3];
  103. } else {
  104.         /* older kernel, no byte counts */
  105.         ifinfo->rx_packets=counts[0];
  106.         ifinfo->tx_packets=counts[2];
  107. }
  108.                         found = 1;
  109. }
  110. }
  111.         if (!found) return (FALSE);
  112. /* see if the interface is up */
  113. strcpy(request.ifr_name, ifname);
  114. if (ioctl(socket_fd, SIOCGIFFLAGS, &request) < 0)
  115. return(FALSE);
  116. if (!(request.ifr_flags & IFF_RUNNING))
  117. return(FALSE);
  118. /* get the IP address */
  119. strcpy(request.ifr_name, ifname);
  120. if (ioctl(socket_fd, SIOCGIFADDR, &request) < 0)
  121. return(FALSE);
  122. ifinfo->addr = ((struct sockaddr_in *) (&request.ifr_addr))->sin_addr;
  123. /* get the PPP destination IP address */
  124. strcpy(request.ifr_name, ifname);
  125. if (ioctl(socket_fd, SIOCGIFDSTADDR, &request) >= 0)
  126. ifinfo->dstaddr = ((struct sockaddr_in *)
  127. (&request.ifr_dstaddr))->sin_addr;
  128. /* get the netmask */
  129. strcpy(request.ifr_name, ifname);
  130. if (ioctl(socket_fd, SIOCGIFNETMASK, &request) >= 0) {
  131.           ifinfo->netmask = ((struct sockaddr_in *)
  132.                              (&request.ifr_netmask))->sin_addr;
  133.           return (TRUE);
  134.         }
  135. return(FALSE);
  136. }
  137. static int get_ifinfo(const char *ifname, ifinfo_t *ifinfo)
  138. {
  139. int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  140. FILE *stats_file = fopen("/proc/net/dev", "r");
  141. int result;
  142. if (socket_fd < 0 || !stats_file)
  143. result = FALSE;
  144. else
  145. {
  146.     char *sp = strchr(ifname, '/');
  147.     if (sp)
  148. *sp = '';
  149.     result = _get_ifinfo_(socket_fd, stats_file, ifname, ifinfo);
  150.     if (sp)
  151. *sp = '/';
  152. }
  153. if (socket_fd >= 0)
  154.     SockClose(close(socket_fd));
  155. if (stats_file)
  156.     fclose(stats_file); /* not checking should be safe, mode was "r" */
  157. return(result);
  158. }
  159. #elif defined __FreeBSD__
  160. static kvm_t *kvmfd;
  161. static struct nlist symbols[] = 
  162. {
  163. {"_ifnet"},
  164. {NULL}
  165. };
  166. static u_long ifnet_savedaddr;
  167. static gid_t if_rgid;
  168. static gid_t if_egid;
  169. void 
  170. interface_set_gids(gid_t egid, gid_t rgid)
  171. {
  172. if_rgid = rgid;
  173. if_egid = egid;
  174. }
  175. static int 
  176. openkvm(void)
  177. {
  178. if ((kvmfd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
  179. return FALSE;
  180. if (kvm_nlist(kvmfd, symbols) < 0)
  181. return FALSE;
  182.    
  183. if (kvm_read(kvmfd, (unsigned long) symbols[0].n_value, &ifnet_savedaddr, sizeof(unsigned long)) == -1)
  184. return FALSE;
  185. return TRUE;
  186. }
  187. static int 
  188. get_ifinfo(const char *ifname, ifinfo_t *ifinfo)
  189. {
  190. char             tname[16];
  191. char iname[16];
  192. struct ifnet            ifnet;
  193. unsigned long    ifnet_addr = ifnet_savedaddr;
  194. #if __FreeBSD_version >= 300001
  195. struct ifnethead ifnethead;
  196. struct ifaddrhead ifaddrhead;
  197. #endif
  198. struct ifaddr ifaddr;
  199. unsigned long ifaddr_addr;
  200. struct sockaddr sa;
  201. unsigned long sa_addr;
  202. uint i;
  203. if (if_egid)
  204. setegid(if_egid);
  205. for (i = 0; ifname[i] && ifname[i] != '/'; i++)
  206. iname[i] = ifname[i];
  207. iname[i] = '';
  208. if (!kvmfd)
  209. {
  210. if (!openkvm())
  211. {
  212. report(stderr, 0, _("Unable to open kvm interface. Make sure fetchmail is SGID kmem."));
  213. if (if_egid)
  214. setegid(if_rgid);
  215. exit(1);
  216. }
  217. }
  218. #if __FreeBSD_version >= 300001
  219. kvm_read(kvmfd, ifnet_savedaddr, (char *) &ifnethead, sizeof ifnethead);
  220. ifnet_addr = (u_long) ifnethead.tqh_first;
  221. #else
  222. ifnet_addr = ifnet_savedaddr;
  223. #endif
  224. while (ifnet_addr)
  225. {
  226. kvm_read(kvmfd, ifnet_addr, &ifnet, sizeof(ifnet));
  227. kvm_read(kvmfd, (unsigned long) ifnet.if_name, tname, sizeof tname);
  228. snprintf(tname, sizeof tname - 1, "%s%d", tname, ifnet.if_unit);
  229. if (!strcmp(tname, iname))
  230. {
  231. if (!(ifnet.if_flags & IFF_UP))
  232. {
  233. if (if_egid)
  234. setegid(if_rgid);
  235. return 0;
  236. }
  237. ifinfo->rx_packets = ifnet.if_ipackets;
  238. ifinfo->tx_packets = ifnet.if_opackets;
  239. #if __FreeBSD_version >= 300001
  240. ifaddr_addr = (u_long) ifnet.if_addrhead.tqh_first;
  241. #else
  242. ifaddr_addr = (u_long) ifnet.if_addrlist;
  243. #endif
  244. while(ifaddr_addr)
  245. {
  246. kvm_read(kvmfd, ifaddr_addr, &ifaddr, sizeof(ifaddr));
  247. kvm_read(kvmfd, (u_long)ifaddr.ifa_addr, &sa, sizeof(sa));
  248. if (sa.sa_family != AF_INET)
  249. {
  250. #if __FreeBSD_version >= 300001
  251. ifaddr_addr = (u_long) ifaddr.ifa_link.tqe_next;
  252. #else
  253. ifaddr_addr = (u_long) ifaddr.ifa_next;
  254. #endif
  255. continue;
  256. }
  257. ifinfo->addr.s_addr = *(u_long *)(sa.sa_data + 2);
  258. kvm_read(kvmfd, (u_long)ifaddr.ifa_dstaddr, &sa, sizeof(sa));
  259. ifinfo->dstaddr.s_addr = *(u_long *)(sa.sa_data + 2);
  260. kvm_read(kvmfd, (u_long)ifaddr.ifa_netmask, &sa, sizeof(sa));
  261. ifinfo->netmask.s_addr = *(u_long *)(sa.sa_data + 2);
  262. if (if_egid)
  263. setegid(if_rgid);
  264. return 1;
  265. }
  266. if (if_egid)
  267. setegid(if_rgid);
  268. return 0;
  269. }
  270. #if __FreeBSD_version >= 300001
  271. ifnet_addr = (u_long) ifnet.if_link.tqe_next;
  272. #else
  273. ifnet_addr = (unsigned long) ifnet.if_next;
  274. #endif
  275. }
  276. if (if_egid)
  277. setegid(if_rgid);
  278. return 0;
  279. }
  280. #endif /* defined __FreeBSD__ */
  281. #ifndef HAVE_INET_ATON
  282. /*
  283.  * Note: This is not a true replacement for inet_aton(), as it won't
  284.  * do the right thing on "255.255.255.255" (which translates to -1 on
  285.  * most machines).  Fortunately this code will be used only if you're
  286.  * on an older Linux that lacks a real implementation.
  287.  */
  288. #ifdef HAVE_NETINET_IN_SYSTM_H
  289. # include <sys/types.h>
  290. # include <netinet/in_systm.h>
  291. #endif
  292. #include <netinet/in.h>
  293. #include <netinet/ip.h>
  294. #include <arpa/inet.h>
  295. #include <string.h>
  296. static int inet_aton(const char *cp, struct in_addr *inp) {
  297.     long addr;
  298.     addr = inet_addr(cp);
  299.     if (addr == ((long) -1)) return 0;
  300.     memcpy(inp, &addr, sizeof(addr));
  301.     return 1;
  302. }
  303. #endif /* HAVE_INET_ATON */
  304. void interface_parse(char *buf, struct hostdata *hp)
  305. /* parse 'interface' specification */
  306. {
  307. char *cp1, *cp2;
  308. hp->interface = xstrdup(buf);
  309. /* find and isolate just the IP address */
  310. if (!(cp1 = strchr(buf, '/')))
  311. {
  312. (void) report(stderr,
  313.       _("missing IP interface addressn"));
  314. exit(PS_SYNTAX);
  315. }
  316. *cp1++ = '00';
  317. /* find and isolate just the netmask */
  318. if (!(cp2 = strchr(cp1, '/')))
  319. cp2 = "255.255.255.255";
  320. else
  321. *cp2++ = '00';
  322. /* convert IP address and netmask */
  323. hp->interface_pair = (struct interface_pair_s *)xmalloc(sizeof(struct interface_pair_s));
  324. if (!inet_aton(cp1, &hp->interface_pair->interface_address))
  325. {
  326. (void) report(stderr,
  327.       _("invalid IP interface addressn"));
  328. exit(PS_SYNTAX);
  329. }
  330. if (!inet_aton(cp2, &hp->interface_pair->interface_mask))
  331. {
  332. (void) report(stderr,
  333.       _("invalid IP interface maskn"));
  334. exit(PS_SYNTAX);
  335. }
  336. /* apply the mask now to the IP address (range) required */
  337. hp->interface_pair->interface_address.s_addr &=
  338. hp->interface_pair->interface_mask.s_addr;
  339. /* restore original interface string (for configuration dumper) */
  340. *--cp1 = '/';
  341. return;
  342. }
  343. void interface_note_activity(struct hostdata *hp)
  344. /* save interface I/O counts */
  345. {
  346. ifinfo_t ifinfo;
  347. struct query *ctl;
  348. /* if not monitoring link, all done */
  349. if (!hp->monitor)
  350. return;
  351. /* get the current I/O stats for the monitored link */
  352. if (get_ifinfo(hp->monitor, &ifinfo))
  353. /* update this and preceeding host entries using the link
  354.    (they were already set during this pass but the I/O
  355.    count has now changed and they need to be re-updated)
  356. */
  357. for (ctl = querylist; ctl; ctl = ctl->next) {
  358. if (ctl->server.monitor && !strcmp(hp->monitor, ctl->server.monitor))
  359. ctl->server.monitor_io =
  360. ifinfo.rx_packets + ifinfo.tx_packets;
  361. /* do NOT update host entries following this one */
  362. if (&ctl->server == hp)
  363. break;
  364. }
  365. #ifdef ACTIVITY_DEBUG
  366. (void) report(stdout, 
  367.       _("activity on %s -noted- as %dn"), 
  368.       hp->monitor, hp->monitor_io);
  369. #endif
  370. }
  371. int interface_approve(struct hostdata *hp)
  372. /* return TRUE if OK to poll, FALSE otherwise */
  373. {
  374. ifinfo_t ifinfo;
  375. /* check interface IP address (range), if specified */
  376. if (hp->interface) {
  377. /* get interface info */
  378. if (!get_ifinfo(hp->interface, &ifinfo)) {
  379. (void) report(stdout, 
  380.       _("skipping poll of %s, %s downn"),
  381.       hp->pollname, hp->interface);
  382. return(FALSE);
  383. }
  384. /* check the IP address (range) */
  385. if ((ifinfo.addr.s_addr &
  386. hp->interface_pair->interface_mask.s_addr) !=
  387. hp->interface_pair->interface_address.s_addr) {
  388. (void) report(stdout,
  389. _("skipping poll of %s, %s IP address excludedn"),
  390. hp->pollname, hp->interface);
  391. return(FALSE);
  392. }
  393. }
  394. /* if not monitoring link, all done */
  395. if (!hp->monitor)
  396. return(TRUE);
  397. #ifdef ACTIVITY_DEBUG
  398. (void) report(stdout, 
  399.       _("activity on %s checked as %dn"), 
  400.       hp->monitor, hp->monitor_io);
  401. #endif
  402. /* if monitoring, check link for activity if it is up */
  403. if (get_ifinfo(hp->monitor, &ifinfo))
  404. {
  405.     int diff = (ifinfo.rx_packets + ifinfo.tx_packets)
  406. - hp->monitor_io;
  407.     /*
  408.      * There are three cases here:
  409.      *
  410.      * (a) If the new packet count is less than the recorded one,
  411.      * probably pppd was restarted while fetchmail was running.
  412.      * Don't skip.
  413.      *
  414.      * (b) newpacket count is greater than the old packet count,
  415.      * but the difference is small and may just reflect the overhead
  416.      * of a link shutdown.  Skip.
  417.      *
  418.      * (c) newpacket count is greater than the old packet count,
  419.      * and the difference is large. Connection is live.  Don't skip.
  420.      */
  421.     if (diff >= 0 && diff <= MONITOR_SLOP)
  422.     {
  423. (void) report(stdout, 
  424.       _("skipping poll of %s, %s inactiven"),
  425.       hp->pollname, hp->monitor);
  426. return(FALSE);
  427.     }
  428. }
  429. #ifdef ACTIVITY_DEBUG
  430.        report(stdout, _("activity on %s was %d, is %dn"),
  431.              hp->monitor, hp->monitor_io,
  432.              ifinfo.rx_packets + ifinfo.tx_packets);
  433. #endif
  434. return(TRUE);
  435. }
  436. #endif /* (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) */