#pragma implementation /* * Most of this source is copied directly from net-tools-1.54/netstat.c. * * Adapted to Linuxconf by Torbjörn Gard */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inetdconf.h" #include "fwport.h" #include #include #include #include #include #include #include #include #include #include #include void *xmalloc(size_t sz); #define new(p) ((p) = xmalloc(sizeof(*(p)))) char *safe_strncpy(char *dst, const char *src, size_t size); #define HAS_INODE 1 #define PROGNAME_WIDTH 20 int flag_inet; int flag_inet6; static int flag_not = 1; static int flag_tcp = 1; static int flag_udp = 1; static int flag_arg = 0; FILE *procinfo; #define INFO_GUTS1(file,name,proc) \ procinfo = fopen((file), "r"); \ if (procinfo == NULL) { \ if (errno != ENOENT) { \ perror((file)); \ return -1; \ } \ rc = 1; \ } else { \ do { \ if (fgets(buffer, sizeof(buffer), procinfo)) \ (proc)(lnr++, buffer); \ } while (!feof(procinfo)); \ fclose(procinfo); \ } #if HAVE_AFINET6 #define INFO_GUTS2(file,proc) \ lnr = 0; \ procinfo = fopen((file), "r"); \ if (procinfo != NULL) { \ do { \ if (fgets(buffer, sizeof(buffer), procinfo)) \ (proc)(lnr++, buffer); \ } while (!feof(procinfo)); \ fclose(procinfo); \ } #else #define INFO_GUTS2(file,proc) #endif #define INFO_GUTS3 \ return rc; #define INFO_GUTS6(file,file6,name,proc) \ char buffer[8192]; \ int rc = 0; \ int lnr = 0; \ if (!flag_arg || flag_inet) { \ INFO_GUTS1(file,name,proc) \ } \ if (!flag_arg || flag_inet6) { \ INFO_GUTS2(file6,proc) \ } \ INFO_GUTS3 #define INFO_GUTS(file,name,proc) \ char buffer[8192]; \ int rc = 0; \ int lnr = 0; \ INFO_GUTS1(file,name,proc) \ INFO_GUTS3 typedef enum { SS_FREE = 0, /* not allocated */ SS_UNCONNECTED, /* unconnected to any socket */ SS_CONNECTING, /* in process of connecting */ SS_CONNECTED, /* connected to socket */ SS_DISCONNECTING /* in process of disconnecting */ } socket_state; enum { TCP_ESTABLISHED = 1, TCP_SYN_SENT, TCP_SYN_RECV, TCP_FIN_WAIT1, TCP_FIN_WAIT2, TCP_TIME_WAIT, TCP_CLOSE, TCP_CLOSE_WAIT, TCP_LAST_ACK, TCP_LISTEN, TCP_CLOSING /* now a valid state */ }; #define SO_ACCEPTCON (1<<16) /* performed a listen */ #define SO_WAITDATA (1<<17) /* wait data to read */ #define SO_NOSPACE (1<<18) /* no space to write */ #define PRG_HASH_SIZE 211 static struct prg_node { struct prg_node *next; int inode; char name[PROGNAME_WIDTH]; } *prg_hash[PRG_HASH_SIZE]; static char prg_cache_loaded = 0; #define PRG_HASHIT(x) ((x) % PRG_HASH_SIZE) #define PRG_SOCKET_PFX "socket:[" #define PRG_SOCKET_PFXl (strlen(PRG_SOCKET_PFX)) #ifndef LINE_MAX #define LINE_MAX 4096 #endif #define _PATH_PROCNET_TCP "/proc/net/tcp" #define _PATH_PROCNET_UDP "/proc/net/udp" #define _PATH_PROCNET_TCP6 "/proc/net/tcp6" #define _PATH_PROCNET_UDP6 "/proc/net/udp6" #define PATH_PROC "/proc" #define PATH_FD_SUFF "fd" #define PATH_FD_SUFFl strlen(PATH_FD_SUFF) #define PATH_PROC_X_FD PATH_PROC "/%s/" PATH_FD_SUFF #define PATH_CMDLINE "cmdline" #define PATH_CMDLINEl strlen(PATH_CMDLINE) /* NOT working as of glibc-2.0.7: */ #undef DIRENT_HAVE_D_TYPE_WORKS static void prg_cache_add(int inode, char *name) { unsigned hi = PRG_HASHIT(inode); struct prg_node **pnp,*pn; prg_cache_loaded=2; for (pnp=prg_hash+hi;(pn=*pnp);pnp=&pn->next) { if (pn->inode==inode) { /* Some warning should be appropriate here as we got multiple processes for one i-node */ return; } } if (!(*pnp=(prg_node *)malloc(sizeof(**pnp)))) return; pn=*pnp; pn->next=NULL; pn->inode=inode; if (strlen(name)>sizeof(pn->name)-1) name[sizeof(pn->name)-1]='\0'; strcpy(pn->name,name); } static const char *prg_cache_get(int inode) { unsigned hi=PRG_HASHIT(inode); struct prg_node *pn; for (pn=prg_hash[hi];pn;pn=pn->next) if (pn->inode==inode) return(pn->name); return("-"); } static void prg_cache_clear(void) { struct prg_node **pnp,*pn; if (prg_cache_loaded == 2) for (pnp=prg_hash;pnpnext; free(pn); } prg_cache_loaded=0; } static void prg_cache_load(void) { char line[LINE_MAX],*serr,eacces=0; int procfdlen,fd,cmdllen,lnamelen; char lname[30],cmdlbuf[512],finbuf[PROGNAME_WIDTH]; long inode; const char *cs,*cmdlp; DIR *dirproc=NULL,*dirfd=NULL; struct dirent *direproc,*direfd; if (prg_cache_loaded ) return; prg_cache_loaded=1; cmdlbuf[sizeof(cmdlbuf)-1]='\0'; if (!(dirproc=opendir(PATH_PROC))) { /* * FIXME: ErrorMsg */ return; } while (errno=0,direproc=readdir(dirproc)) { #ifdef DIRENT_HAVE_D_TYPE_WORKS if (direproc->d_type!=DT_DIR) continue; #endif for (cs=direproc->d_name;*cs;cs++) if (!isdigit(*cs)) break; if (*cs) continue; procfdlen=snprintf(line,sizeof(line),PATH_PROC_X_FD,direproc->d_name); if (procfdlen<=0 || (unsigned int)procfdlen>=sizeof(line)-5) continue; errno=0; dirfd=opendir(line); if (! dirfd) { if (errno==EACCES) eacces=1; continue; } line[procfdlen] = '/'; cmdlp = NULL; while ((direfd = readdir(dirfd))) { #ifdef DIRENT_HAVE_D_TYPE_WORKS if (direfd->d_type!=DT_LNK) continue; #endif if (procfdlen+1+strlen(direfd->d_name)+1>sizeof(line)) continue; memcpy(line + procfdlen - PATH_FD_SUFFl, PATH_FD_SUFF "/", PATH_FD_SUFFl+1); strcpy(line + procfdlen + 1, direfd->d_name); lnamelen=readlink(line,lname,sizeof(lname)); if ((unsigned int)lnamelen < strlen(PRG_SOCKET_PFX+2)) continue; if (memcmp(lname, PRG_SOCKET_PFX, PRG_SOCKET_PFXl) || lname[lnamelen-1]!=']') continue; lname[lnamelen-1]='\0'; inode = strtol(lname+PRG_SOCKET_PFXl,&serr,0); if (!serr || *serr || inode < 0 || inode >= INT_MAX) continue; if (!cmdlp) { if (procfdlen - PATH_FD_SUFFl + PATH_CMDLINEl >= sizeof(line) - 5) continue; strcpy(line + procfdlen-PATH_FD_SUFFl, PATH_CMDLINE); fd = open(line, O_RDONLY); if (fd < 0) continue; cmdllen = read(fd, cmdlbuf, sizeof(cmdlbuf) - 1); if (close(fd)) continue; if (cmdllen == -1) continue; if ((unsigned int)cmdllen < sizeof(cmdlbuf) - 1) cmdlbuf[cmdllen]='\0'; if ((cmdlp = strrchr(cmdlbuf, '/'))) cmdlp++; else cmdlp = cmdlbuf; } snprintf(finbuf, sizeof(finbuf), "%s %s", direproc->d_name, cmdlp); prg_cache_add(inode, finbuf); } closedir(dirfd); dirfd = NULL; } if (dirproc) closedir(dirproc); if (dirfd) closedir(dirfd); } /* * From net-tools-1.54/lib/util.c: */ /* Like strncpy but make sure the resulting string is always 0 terminated. */ char *safe_strncpy(char *dst, const char *src, size_t size) { dst[size-1] = '\0'; return strncpy(dst,src,size-1); } /* * From net-tools-1.54/lib/inet.c: */ static int INET_rresolve(char *name, size_t len, struct sockaddr_in *sin, int numeric, unsigned int netmask) { unsigned long ad; /* Grmpf. -FvK */ if (sin->sin_family != AF_INET) { #ifdef DEBUG fprintf(stderr, "rresolve: unsupport address family %d !\n", sin->sin_family); #endif errno = EAFNOSUPPORT; return (-1); } ad = (unsigned long) sin->sin_addr.s_addr; #ifdef DEBUG fprintf (stderr, "rresolve: %08lx, mask %08x, num %08x %08lx\n", ad, netmask, numeric, INADDR_ANY); #endif if (ad == INADDR_ANY) { if ((numeric & 0x0FFF) == 0) { if (numeric & 0x8000) safe_strncpy(name, "default", len); else safe_strncpy(name, "*", len); return (0); } else { safe_strncpy(name, "*", len); return (0); } } safe_strncpy(name, inet_ntoa(sin->sin_addr), len); return (0); } /* Display an Internet socket address. */ static char *INET_sprint(struct sockaddr *sap, int numeric) { static char buff[128]; //fprintf(stderr,"ports.cc: INET_sprint\n"); if (sap->sa_family == 0xFFFF || sap->sa_family == 0) { return safe_strncpy(buff, "[NONE SET]", sizeof(buff)); } if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap, numeric, 0xffffff00) != 0) { return (NULL); } //fprintf(stderr,"ports.cc: INET_sprint: %s\n", buff); return (buff); } FWPORT::FWPORT() { port = 0; uid = 0; pid = 0; accept = 0; } FWPORTLIST::FWPORTLIST() { } PRIVATE void FWPORTLIST::tcp_do_one(int lnr, const char *line) { unsigned long rxq, txq, time_len, retr, inode; int num, local_port, rem_port, d, state, uid, timer_run, timeout; char rem_addr[128], local_addr[128], more[512]; #if HAVE_AFINET6 struct sockaddr_in6 localaddr, remaddr; char addr6p[16][3], addr6[128]; #else struct sockaddr_in localaddr, remaddr; #endif if (lnr == 0) return; num = sscanf(line, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", &d, local_addr, &local_port, rem_addr, &rem_port, &state, &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); if (strlen(local_addr) > 8) { #if HAVE_AFINET6 /* Demangle what the kernel gives us */ sscanf(local_addr, "%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7], addr6p[8], addr6p[9], addr6p[10], addr6p[11], addr6p[12], addr6p[13], addr6p[14], addr6p[15]); snprintf(addr6, sizeof(addr6), "%s%s:%s%s:%s%s:%s%s:%s%s:%s%s:%s%s:%s%s", addr6p[3], addr6p[2], addr6p[1], addr6p[0], addr6p[7], addr6p[6], addr6p[5], addr6p[4], addr6p[11], addr6p[10], addr6p[9], addr6p[8], addr6p[15], addr6p[14], addr6p[13], addr6p[12]); inet6_aftype.input(1, addr6, (struct sockaddr *) &localaddr); sscanf(rem_addr, "%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s%2s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7], addr6p[8], addr6p[9], addr6p[10], addr6p[11], addr6p[12], addr6p[13], addr6p[14], addr6p[15]); snprintf(addr6, sizeof(addr6), "%s%s:%s%s:%s%s:%s%s:%s%s:%s%s:%s%s:%s%s", addr6p[3], addr6p[2], addr6p[1], addr6p[0], addr6p[7], addr6p[6], addr6p[5], addr6p[4], addr6p[11], addr6p[10], addr6p[9], addr6p[8], addr6p[15], addr6p[14], addr6p[13], addr6p[12]); inet6_aftype.input(1, addr6, (struct sockaddr *) &remaddr); localaddr.sin6_family = AF_INET6; remaddr.sin6_family = AF_INET6; #endif } else { sscanf(local_addr, "%X", &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); sscanf(rem_addr, "%X", &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); ((struct sockaddr *) &localaddr)->sa_family = AF_INET; ((struct sockaddr *) &remaddr)->sa_family = AF_INET; } if (num < 11) { fprintf(stderr, "warning, got bogus tcp line.\n"); return; } if (state == TCP_LISTEN) { time_len = 0; retr = 0L; rxq = 0L; txq = 0L; } safe_strncpy(local_addr, INET_sprint((struct sockaddr *) &localaddr, flag_not), sizeof(local_addr)); if ( strcmp( local_addr, "*" ) != 0 ) { return; } { FWPORT *fwport = new FWPORT(); struct passwd *pw; fwport->protocol.setfrom( "tcp" ); char word[100]; char *p = str_copyword( word, prg_cache_get(inode), sizeof(word)); fwport->pid = atoi( word ); p = str_copyword( word, p, sizeof(word)); fwport->program.setfrom( word ); fwport->port = local_port; fwport->uid = uid; if ((pw = getpwuid(uid)) != NULL) fwport->user.setfrom( pw->pw_name ); else fwport->user.setfrom( uid ); add( fwport ); } } PRIVATE void FWPORTLIST::udp_do_one(int lnr, const char *line) { char local_addr[64], rem_addr[64]; char *udp_state, more[512]; int num, local_port, rem_port, d, state, timer_run, uid, timeout; #if HAVE_AFINET6 struct sockaddr_in6 localaddr, remaddr; char addr6p[8][5]; char addr6[128]; #else struct sockaddr_in localaddr, remaddr; #endif unsigned long rxq, txq, time_len, retr, inode; if (lnr == 0) return; more[0] = '\0'; num = sscanf(line, "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n", &d, local_addr, &local_port, rem_addr, &rem_port, &state, &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more); if (strlen(local_addr) > 8) { #if HAVE_AFINET6 sscanf(local_addr, "%4s%4s%4s%4s%4s%4s%4s%4s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); inet6_aftype.input(1, addr6, (struct sockaddr *) &localaddr); sscanf(rem_addr, "%4s%4s%4s%4s%4s%4s%4s%4s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); snprintf(addr6, sizeof(addr6), "%s:%s:%s:%s:%s:%s:%s:%s", addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]); inet6_aftype.input(1, addr6, (struct sockaddr *) &remaddr); localaddr.sin6_family = AF_INET6; remaddr.sin6_family = AF_INET6; #endif } else { sscanf(local_addr, "%X", &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr); sscanf(rem_addr, "%X", &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr); ((struct sockaddr *) &localaddr)->sa_family = AF_INET; ((struct sockaddr *) &remaddr)->sa_family = AF_INET; } retr = 0L; more[0] = '\0'; if (num < 10) { fprintf(stderr, "warning, got bogus udp line.\n"); return; } switch (state) { case TCP_ESTABLISHED: udp_state = "ESTABLISHED"; break; case TCP_CLOSE: udp_state = ""; break; default: udp_state = "UNKNOWN"; break; } #if HAVE_AFINET6 #define notnull(A) (((A.sin6_family == AF_INET6) && \ ((A.sin6_addr.s6_addr32[0]) || \ (A.sin6_addr.s6_addr32[1]) || \ (A.sin6_addr.s6_addr32[2]) || \ (A.sin6_addr.s6_addr32[3]))) || \ ((A.sin6_family == AF_INET) && \ ((struct sockaddr_in *) &A)->sin_addr.s_addr)) #else #define notnull(A) (A.sin_addr.s_addr) #endif //fprintf(stderr, "ports.cc:udp\n"); safe_strncpy(local_addr, INET_sprint((struct sockaddr *) &localaddr, flag_not), sizeof(local_addr)); if ( strcmp( local_addr, "*" ) != 0 ) { return; } { FWPORT *fwport = new FWPORT(); struct passwd *pw; fwport->protocol.setfrom( "udp" ); char word[100]; char *p = str_copyword( word, prg_cache_get(inode), sizeof(word)); fwport->pid = atoi( word ); p = str_copyword( word, p, sizeof(word)); fwport->program.setfrom( word ); fwport->port = local_port; fwport->uid = uid; if ((pw = getpwuid(uid)) != NULL) fwport->user.setfrom( pw->pw_name ); else fwport->user.setfrom( uid ); add( fwport ); } } PRIVATE int FWPORTLIST::getlist_tcp( ) { //fprintf(stderr,"PRIVATE int FWPORTLIST::getlist_tcp( )\n"); { INFO_GUTS6(_PATH_PROCNET_TCP, _PATH_PROCNET_TCP6, "AF INET (tcp)", tcp_do_one); } return( 1 ); } PRIVATE int FWPORTLIST::getlist_udp( ) { //fprintf(stderr,"PRIVATE int FWPORTLIST::getlist_udp( )\n"); { INFO_GUTS6(_PATH_PROCNET_UDP, _PATH_PROCNET_UDP6, "AF INET (udp)", udp_do_one); } return( 1 ); } static int cmp_by_name (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { FWPORT *s1 = (FWPORT*)o1; FWPORT *s2 = (FWPORT*)o2; int ret = s1->protocol.cmp(s2->protocol); if (ret == 0){ if ( s1->port < s2->port ) ret = -1; else if ( s1->port > s2->port ) ret = 1; else ret = 0; } return ret; } PUBLIC void FWPORTLIST::sort() { ARRAY::sort (cmp_by_name); } PUBLIC void FWPORTLIST::update_from_db() { char service[100]; for (int i=0; iprotocol.get(), fwport->port); fwport->accept = (char)linuxconf_getvalnum( K_FIREWALL, service, 0 ); } } PUBLIC void FWPORTLIST::read( ) { flag_inet = 1; flag_arg = flag_tcp + flag_udp; prg_cache_load(); getlist_tcp(); getlist_udp(); prg_cache_clear(); } PUBLIC FWPORT *FWPORTLIST::getitem (int no) const { return (FWPORT*)ARRAY::getitem (no); }