/* Copyright (C) 2000 - 2006 Christian Kreibich . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies of the Software and its documentation and acknowledgment shall be given in the documentation and software packages that this Software was used. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif #ifdef LINUX_HOST #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define LND_SWAPNAME_PFX "netdude-swap-" guint libnd_misc_writen(int fd, const guchar *data, guint size) { guint nleft; int nwritten; const guchar *ptr; /* See Stevens's UNP ... */ ptr = data; nleft = size; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) < 0) { if (errno == EINTR) { D(("Interrupted write: %s\n", strerror(errno))); nwritten = 0; } else { D(("Error when writing: %s\n", strerror(errno))); return -1; } } nleft -= nwritten; ptr += nwritten; } return size; } guint libnd_misc_readn(int fd, guchar *data, guint size) { guint nleft; int nread; guchar *ptr; /* See Stevens's UNP ... */ ptr = data; nleft = size; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; else return -1; } else if (nread == 0) { break; /* EOF */ } nleft -= nread; ptr += nread; } return size - nleft; } guint libnd_misc_readline(int fd, guchar *data, guint size) { int remaining = size; int n = 0, total = 0; char *eol; while (remaining > 0) { if ((n = read(fd, data, remaining)) > 0) { total += n; if ( (eol = strchr((const char *) data, '\n'))) return total; data += n; remaining -= n; } } return size; } int libnd_misc_ones_complement_checksum(const void *p, int b, u_int32_t sum) { const u_short *sp; sp = (u_short *) p; /* better be aligned! */ b /= 2; /* convert to count of short's */ /* No need for endian conversions. */ while ( --b >= 0 ) sum += *sp++; while ( sum > 0xffff ) sum = (sum & 0xffff) + (sum >> 16); return sum; } /* * Checksum routine for Internet Protocol family headers (C Version) */ u_short libnd_misc_in_cksum(u_short *addr, int len, u_int preadd) { register int nleft = len; register u_short *w = addr; register u_short answer; register int sum = 0; sum += preadd; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) sum += *(guchar *)w; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return answer; } gboolean libnd_misc_exists(const char *filename) { struct stat st; if (!filename || filename[0] == '\0') return FALSE; if (lstat(filename, &st) < 0) return FALSE; return TRUE; } gboolean libnd_misc_is_tcpdump_file(const char *name) { FILE *f; u_int32_t magic = 0; if ((f = fopen(name, "r"))) { fread((void*)&magic, sizeof(u_int32_t), 1, f); fclose(f); if ((magic == 0xa1b2c3d4) || (magic == 0xd4c3b2a1) || (magic == 0xa1b2cd34) || /* Extended tracefile with Alexey's patches */ (magic == 0x34cdb2a1)) return TRUE; } D(("fopen() failed, reason: %s\n", strerror(errno))); return FALSE; } gboolean libnd_misc_is_dir(const char *filename) { struct stat st; if (!filename || filename[0] == '\0') return FALSE; if (lstat(filename, &st) < 0) return FALSE; return S_ISDIR(st.st_mode); } guint64 libnd_misc_get_size(const char *filename) { struct stat st; if (!filename || filename[0] == '\0') return 0; if (lstat(filename, &st) < 0) return 0; return st.st_size; } gboolean libnd_misc_can_exec(const char *filename) { struct stat st; if (!filename || filename[0] == '\0') return FALSE; /* Stat the file to see if it's executable and if the user may run it. */ if (lstat(filename, &st) < 0) return FALSE; if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)) return TRUE; return FALSE; } gboolean libnd_misc_can_read(const char *filename) { struct stat st; if (!filename || filename[0] == '\0') return FALSE; /* Stat the file to see if it's executable and if the user may run it. */ if (lstat(filename, &st) < 0) return FALSE; if ((st.st_mode & S_IRUSR) || (st.st_mode & S_IRGRP) || (st.st_mode & S_IROTH)) return TRUE; return FALSE; } gboolean libnd_misc_can_write(const char *filename) { struct stat st; if (!filename || filename[0] == '\0') return FALSE; /* Stat the file to see if it's executable and if the user may run it. */ if (lstat(filename, &st) < 0) return FALSE; if ((st.st_mode & S_IWUSR) || (st.st_mode & S_IWGRP) || (st.st_mode & S_IWOTH)) return TRUE; return FALSE; } gint libnd_misc_int_cmp(gconstpointer row1, gconstpointer row2) { if ((int) row1 < (int) row2) return -1; if ((int) row1 > (int) row2) return 1; return 0; } char * libnd_misc_add_slash(char *filename) { int len; if (!filename) return NULL; len = strlen(filename); if (filename[len - 1] == '/') return filename; filename = realloc(filename, len + 2); filename[len] = '/'; filename[len+1] = 0; return filename; } GList * libnd_misc_get_if_names(int flags) { int fd, last_len, len, num_ifs; guchar *buf = NULL, *ptr, *cptr; struct ifconf ifconf; struct ifreq *ifr, ifrcopy; GList *l = NULL, *ifs = NULL; D_ENTER; if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { D(("Socket error\n")); goto cleanup; } /* Start with looking for up to 10 adresses, * loop if that's not enough, doubling the amount each time. */ num_ifs = 5; buf = NULL; memset(&ifconf, 0, sizeof(struct ifconf)); do { last_len = ifconf.ifc_len; g_free(buf); num_ifs *= 2; len = sizeof(struct ifreq) * num_ifs; if (! (buf = malloc(len))) { D(("Out of memory.\n")); fflush(stderr); goto cleanup_fd; } ifconf.ifc_len = len; ifconf.ifc_buf = (char *) buf; if (ioctl(fd, SIOCGIFCONF, &ifconf) < 0) { D(("ioctl error\n")); goto cleanup_fd; } } while (ifconf.ifc_len != last_len); D(("Interface list aquired.\n")); for (ptr = buf; ptr < buf + ifconf.ifc_len; ) { ifr = (struct ifreq *) ptr; #ifdef HAVE_SOCKADDR_SA_LEN len = MAX(sizeof(struct sockaddr, ifr->ifr_addr.sa_len)); #else switch (ifr->ifr_addr.sa_family) { #ifdef IPV6 case AF_INET6: len = sizeof(struct sockaddr_in6); break; #endif case AF_INET: default: len = sizeof(struct sockaddr); } #endif ptr += sizeof(ifr->ifr_name) + len; /* Create copy of ifreq for flags retrieval: */ ifrcopy = *ifr; if (ioctl(fd, SIOCGIFFLAGS, &ifrcopy) >= 0) { /* If the interface doesn't have what's asked for, skip. */ if ((ifrcopy.ifr_flags & flags) != flags) continue; } /* Solaris interface alias handling, see Stevens */ if ( (cptr = (guchar *) strchr(ifr->ifr_name, ':'))) *cptr = '\0'; for (l = ifs; l; l = g_list_next(l)) { /* If we've had that name already, skip it. */ if (strncmp(l->data, ifr->ifr_name, IFNAMSIZ) == 0) continue; } ifs = g_list_prepend(ifs, g_strdup(ifr->ifr_name)); } /* If we're on Linux, use at least kernel 2.2, and so * far we've found any up interfaces, also offer the "any" device: */ #ifdef LINUX_HOST if (ifs && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 2, 0))) ifs = g_list_append(ifs, g_strdup("any")); #endif /* Finally, sort the list alphanumerically */ ifs = g_list_sort(ifs, (GCompareFunc) strcmp); cleanup_fd: close(fd); cleanup: g_free(buf); D_RETURN_(ifs); } int libnd_misc_get_if_mtu(const char *ifname) { struct ifreq ifreq; int fd; int mtu = (64 * 1024); /* based on pcap's BIGGER_THAN_ALL_MTUS :) */ if (!ifname) return mtu; #ifdef SIOCGIFMTU memset(&ifreq, 0, sizeof(struct ifreq)); memcpy(&ifreq.ifr_name, ifname, MIN(strlen(ifname), IFNAMSIZ)); ifreq.ifr_name[IFNAMSIZ - 1] = '\0'; if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { D(("Socket error\n")); return mtu; } if (ioctl(fd, SIOCGIFMTU, &ifreq) < 0) { D(("ioctl error\n")); goto cleanup; } mtu = ifreq.ifr_mtu; cleanup: close(fd); #endif return mtu; } void libnd_misc_get_hardware_string(char *str, guint str_len, guchar *address, guint addr_len) { guint i; char *str_ptr; if (!str) return; str_ptr = str; for (i = 0; i < addr_len - 1; i++) { g_snprintf(str_ptr, str_len - (str_ptr - str), "%.2x:", address[i]); str_ptr += 3; } g_snprintf(str_ptr, MAXPATHLEN - (str_ptr - str), "%.2x", address[addr_len - 1]); } /* fixme - this should be const char* */ char * libnd_misc_get_unnamed_string(void) { static int index = 1; static char name[MAXPATHLEN]; g_snprintf(name, MAXPATHLEN, "netdude-%i.trace", index++); return name; } char * libnd_misc_timeval_to_string(struct bpf_timeval *tv) { static char s[MAXPATHLEN]; if (!tv) return ""; g_snprintf(s, MAXPATHLEN, "%lu days, %.2lu:%.2lu:%.2lu:%lu", (long unsigned int) tv->tv_sec / 86400, (long unsigned int) (tv->tv_sec % 86400) / 3600, (long unsigned int) (tv->tv_sec % 3600) / 60, (long unsigned int) (tv->tv_sec % 60), (long unsigned int) tv->tv_usec); return s; } guchar * libnd_misc_memdup(const guchar *data, guint data_len) { guchar *result = NULL; if (!data || data_len == 0) return NULL; if (! (result = malloc(data_len))) return NULL; memcpy(result, data, data_len); return result; } void libnd_misc_ctime(const struct bpf_timeval *tv, char *buf, int buflen, gboolean fix_gmt, gboolean add_usec) { char *timestr, *bufptr; time_t time_gmt; if (!tv || !buf || buflen == 0) return; time_gmt = tv->tv_sec; if (fix_gmt) time_gmt += libnd_misc_get_gmt_deviation(); /* Get the "real" timestring and paste it into the buffer */ timestr = ctime(&time_gmt); g_snprintf(buf, buflen, "%s", timestr); /* Find last occurrences of whitespace -- that's where we * want to insert the microseconds. */ timestr = strrchr(timestr, ' '); bufptr = strrchr(buf, ' '); *bufptr = '\0'; /* Insert the microseconds */ if (add_usec) g_snprintf(buf + strlen(buf), buflen - strlen(buf), ":%lu", (long unsigned) tv->tv_usec); /* And then the rest of the original ctime string */ g_snprintf(buf + strlen(buf), buflen - strlen(buf), "%s", timestr); } int libnd_misc_get_gmt_deviation(void) { static gboolean deja_vu = FALSE; static int deviation; time_t t; int dir; struct tm *gmttime, *loctime; struct tm tm; if (deja_vu) return deviation; t = time(NULL); gmttime = &tm; *gmttime = *gmtime(&t); loctime = localtime(&t); deviation = (loctime->tm_hour - gmttime->tm_hour) * 3600 + (loctime->tm_min - gmttime->tm_min) * 60; if ((dir = loctime->tm_year - gmttime->tm_year) == 0) dir = loctime->tm_yday - gmttime->tm_yday; deviation += dir * 24 * 3600; D(("Deviation is %i\n", deviation)); deja_vu = TRUE; return deviation; } char * libnd_misc_get_tmpfile(const char *filename) { static int counter = 0; char *workdir; char result[MAXPATHLEN]; if (!filename || !*filename) filename = "none"; libnd_prefs_get_str_item(LND_DOM_NETDUDE, "workdir", &workdir); g_snprintf(result, MAXPATHLEN, "%s/%s-%u-%i-%s", workdir, LND_SWAPNAME_PFX, getpid(), ++counter, g_basename(filename)); D(("Allocated swapname '%s'\n", result)); return g_strdup(result); }