/* * Copyright (C) 1999-2004 Joachim Wieland * * 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 of the License, 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, USA. */ #include "jftpgw.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_CRYPT_H #include /* getpass() */ #define _XOPEN_SOURCE #include /* crypt() */ #endif static int changecode(char *const, const char*); extern int timeout; extern int chlds_exited; extern struct uidstruct runasuser; extern struct serverinfo srvinfo; sigset_t chldterm_sigset, chldterm_oldset; char* errstr = 0; void enough_mem(const void* ptr) { if (ptr == (void*) 0) { jlog(0, "Not enough memory for malloc. Exiting."); exit(1); } } /* concating snprintf * * * * determines the length of the string pointed to by `os', appending * * formatted string to a maximium length of `len'. * * * */ void scnprintf (char *os, size_t len, const char *str, ...) { va_list vl; char *ostmp = os + strlen (os); va_start (vl, str); vsnprintf (ostmp, len - strlen (os) - 1, str, vl); va_end (vl); return; } void set_errstr(const char* s) { if (errstr) { free(errstr); } errstr = strdup(s); } const char* get_errstr(void) { if (errstr) { return errstr; } else { return "No detailed error information available... :-("; } } void free_errstr(void) { if (errstr) { free(errstr); errstr = (char*) 0; } } /* #ifndef HAVE_STRCASESTR */ char* my_strcasestr(const char* haystack, const char* needle) { char* nhay = strdup(haystack); char* nneed = strdup(needle); const char* match; char* p; enough_mem(nhay); enough_mem(nneed); p = nhay; while (*p) { *p = (char) toupper((int)*p); p++; } p = nneed; while (*p) { *p = (char) toupper((int)*p); p++; } match = strstr(nhay, nneed); if (match) { match = haystack + (match - nhay); } free(nhay); free(nneed); return (char*) match; } /* #endif */ /* writes a char* to an fd and checks the return value */ int say(int fd, const char* phrase) { int i; struct timeval writetime = { 300, 0 }; fd_set writeset; FD_ZERO(&writeset); FD_SET(fd, &writeset); i = select(fd + 1, NULL, &writeset, NULL, &writetime); if (i == 0) { jlog(2, "Timeout reached in say()"); jlog(2, "Timeout in %s line %d\n", __FILE__ ,__LINE__); timeout = 1; return -1; } if (i == -1) { jlog(1, "Error in select() in say(): %s", strerror(errno)); return -1; } if (my_strcasestr(phrase, "PASS ") == (char*) 0) { jlog(9, "Write(%d): %s", fd, phrase); } else { jlog(9, "Write(%d): ***hidden***", fd); } i = write(fd, phrase, strlen(phrase)); if (i < 0) { jlog(3, "write (say) failed: %s", strerror(errno)); if (my_strcasestr(phrase, "PASS ") == (char*) 0) { jlog(3, "should say %s to %d", phrase, fd); } else { jlog(3, "should say ***hidden*** to %d", fd); } } return i; } #define SAYBUFFERSIZE 200 int sayf(int fd, const char* fmt, ...) { va_list args; static char str[SAYBUFFERSIZE]; va_start(args, fmt); vsnprintf(str, SAYBUFFERSIZE - 1, fmt, args); va_end(args); return say(fd, str); } int changeid(int who_id, int what, const char* reason) { int i = 0; char* wstr; const char* user = (char*) 0; const char* group = (char*) 0; const char* s_user = (char*) 0; const char* s_group = (char*) 0; uid_t uid = getuid(); uid_t euid = geteuid(); uid_t tuid; /* Target ids we want to change to */ uid_t teuid; gid_t tgid; gid_t tegid; uid_t s_uid; gid_t s_gid; uid_t value; /* just for the logging statement */ if (who_id == PRIV) { tuid = 0; value = 0; wstr = "UID"; /* can't change the (E)(U/G)ID if we are not root */ if (uid != 0) { return 0; } /* we are done if we are root already */ if (euid == 0) { return 0; } i = setuid(tuid); } else { /* who_id == UNPRIV, s_* = "source_*" */ s_user = runasuser.username; s_uid = runasuser.uid; s_group = runasuser.groupname; s_gid = runasuser.gid; user = config_get_option("runasuser"); group = config_get_option("runasgroup"); if (what == UID || what == EUID) { /* no user is set - ok */ if (!user) { return 0; } /* down here we have a user set */ if (!s_user || strcmp(user, s_user) != 0) { jlog(2, "Invalid user specified: %s", user); } } if (what == GID || what == EGID) { /* no group is set - ok */ if (!group) { return 0; } /* down here we have a group set */ if (!s_group || strcmp(group, s_group) != 0) { jlog(2, "Invalid user specified: %s", group); } } /* can't change the (E)(U/G)ID if we are not root */ if (uid != 0) { return 0; } if (euid != 0) { /* UID is root, but effective UID is not. The GID * should be changed. Change to root first. */ if (changeid(PRIV, UID, "Want to change UID/GID. " "Changing back to root first") < 0) { return -1; } /* Make sure that each call to changeid(..., GID, ...) * is followed by one to changeid(..., UID, ...) */ } jlog(7, "%s", reason); switch(what) { case EUID: wstr = "EUID"; teuid = s_uid; value = teuid; #ifdef HAVE_SETEUID i = seteuid(teuid); #else /* HP-UX does not know seteuid() */ i = setreuid(-1, euid); #endif break; case UID: wstr = "UID"; tuid = s_uid; value = tuid; /* we are done if we are root already */ if (tuid == 0 && euid == 0) { return 0; } i = setuid(tuid); break; case EGID: wstr = "EGID"; tegid = s_gid; value = tegid; #ifdef HAVE_SETEGID i = setegid(tegid); #else /* HP-UX does not know setegid() */ i = setregid(-1, tegid); #endif break; case GID: wstr = "GID"; tgid = s_gid; value = tgid; i = setgid(tgid); break; default: wstr = "ERROR"; value = 0; } } if (i) { jlog(3, "Could not change the %s to %d: %s", wstr, value, strerror(errno)); } else { jlog(8, "Changed %s to %d", wstr, value); } return i; } /* just get the status of the child so that it can end */ void reap_chld_info (int signo) { int err = errno; int status; /* signal handler but waitpid() is reentrant */ while (waitpid (-1, &status, WNOHANG) > 0 || errno == EINTR) { errno = 0; }; errno = err; } /* get the status of the child and unregister it */ /* this function is the signal handler */ void childterm (int signo) { chlds_exited++; } int get_chld_pid() { int ret; int status; pid_t pid; while ((pid = waitpid (-1, &status, WNOHANG)) > 0 || errno == EINTR) { errno = 0; jlog(9, "A child exited. Pid: %d", pid); jlog(9, "unregistering pid ..."); ret = unregister_pid(pid); if (ret) { jlog(3, "Error unregistering pid"); } else { jlog(9, "unregistered"); } } chlds_exited = 0; return 0; } /* returns the code in a response */ int getcode(const char* response) { char buffer[4]; strncpy(buffer, response, sizeof(buffer) - 1); buffer[3] = '\0'; return atoi(buffer); } /* Checks if the string RESONSE starts with SHOULDBE */ int checkdigits(const char* response, const int shouldbe) { return (getcode(response) == shouldbe); } /* extracts the numerical code out of an FTP server response. */ int respcode(const char* response) { int resp = 0; int i; const char* respoff = response; if (!response) { return 0; } while (respoff[3] != ' ') { respoff = strchr(respoff, '\n'); if (!respoff) { return -1; } else { respoff++; /* skip over '\n' */ } } i = sscanf(respoff, "%d ", &resp); if (i != 1 && resp < 100) { return -1; } return resp; } const char* gethostentip(const char* iplist) { static char ipbuf[16]; snprintf(ipbuf, 16, "%d.%d.%d.%d", (unsigned char) iplist[0], (unsigned char) iplist[1], (unsigned char) iplist[2], (unsigned char) iplist[3] ); return ipbuf; } /* parsesock parses a comma separated list of IP and Port like in * PORT 127,0,0,1,15,216 * or the PASV answer. * */ int parsesock(char* buffer, struct sockaddr_in *sin, int mode) { int i1, i2, i3, i4, lo, hi, count; unsigned long int iaddr; unsigned int port; char ipbuf[16]; memset((void*)sin, 0, sizeof(*sin)); count = sscanf(buffer, "%d,%d,%d,%d,%d,%d", &i1, &i2, &i3, &i4, &hi, &lo); /* sscanf must have read 6 arguments and all the parameters must be * less than 255 ( 0xff ) */ if (!(count != 6 || i1 > 0xff || i2 > 0xff || i3 > 0xff || i4 > 0xff || hi > 0xff || lo > 0xff)) { snprintf(ipbuf, 16, "%d.%d.%d.%d", i1, i2, i3, i4); iaddr = inet_addr(ipbuf); if (iaddr != -1 || !strcmp(ipbuf, BROADCAST)) { sin->sin_family = AF_INET; sin->sin_addr.s_addr = iaddr; port = hi * 256 + lo; sin->sin_port = htons(port); return 0; } else { jlog(3, "Invalid address in the PASV or PORT command: %s", buffer); } } jlog(3, "Error parsing IP and port from %s", buffer); return -1; } /* sets HOWMANY bits on. */ unsigned long int setlastbits(int howmany) { unsigned long e = 0; int i; for (i=0; i < 32; i++) { if (i <= howmany) { e |= 1; } e = e << 1; } return ntohl(e); } void toupstr(char* s) { while (*s) { *s = toupper((int)*s); s++; } } void char_squeeze(char *const s, int c) { char last = 0; int i = 0, j = 0; char* tmp = malloc(strlen(s) + 1); enough_mem(tmp); do { if (s[i] == last && s[i] == c) { /* do nothing */ } else { tmp[j++] = s[i]; } last = s[i]; i++; } while (s[i]); tmp[j] = '\0'; strcpy(s, tmp); free(tmp); } struct ip_t parse_ip(const char* s) { struct ip_t s_ip; struct in_addr iaddr; int ret; const char* slash = strchr(s, '/'); const char* dot; char* ip; if (!slash) { /* no netmask specified */ slash = s + strlen(s); } ip = malloc(slash - s + 1); enough_mem(ip); strncpy(ip, s, slash - s); ip[slash - s] = '\0'; ret = inet_aton(ip, &iaddr); free(ip); ip =0; if (ret == 0) { /* inet_aton error */ s_ip.ip = -1; s_ip.netmask = -1; return s_ip; } s_ip.ip = iaddr.s_addr; if (*slash) { slash++; } /* slash points to the netmask now or is 0 if none has been specified */ if (!*slash) { s_ip.netmask = -1; /* 255.255.255.255 */ return s_ip; } dot = strchr(slash, '.'); if (!dot) { /* a decimal number netmask has been specified */ s_ip.netmask = setlastbits(atoi(slash)); return s_ip; } ret = inet_aton(slash, &iaddr); if (ret == 0) { /* error */ s_ip.netmask = inet_addr("255.255.255.255"); } else { s_ip.netmask = iaddr.s_addr; } return s_ip; } int cmp_domains(const char* name, const char* pattern) { const char* start; /* the hostname may not be shorter than the pattern * * pattern: .foobar.mit.edu * name: bla.mit.edu * * => won't match */ if (strlen(name) < strlen(pattern)) { return 0; } start = name + strlen(name) - strlen(pattern); return !strcasecmp(start, pattern); } void err_time_readline(int fd) { jlog(2, "Timeout reached in readline()"); say(fd, "500 Connection timed out\r\n"); } void err_readline(int fd) { char* m; int e = errno; size_t msize; const char* err; char* s = "An error occurred in ftp_readline: %s"; if (!(err = get_errstr())) { err = strerror(e); } jlog(2, s, err); msize = strlen(s) + strlen(err) + 7; m = (char*) malloc(msize); enough_mem(m); strcpy(m, "500 "); snprintf(m + strlen(m), msize /* initial size */ - strlen(m) /* "500 " */ - 3, /* \r\n\0 */ s, err); strcat(m, "\r\n"); say(fd, m); free(m); } /* extracts joe from joe@foo,21 or foo bar from "foo bar"@bla,21 */ char* extract_username(const char* s) { const char* p; char* r; if (*s == '"') { p = strchr(s+1, '"'); if (!p) { return 0; } } else { p = strchr(s, '@'); if (!p) { p = strchr(s, ','); } if (!p) { p = s + strlen(s); } } r = malloc(p - s + 1); enough_mem(r); strncpy(r, s, p - s); r[p - s] = '\0'; return r; } /* extracts joe@host from joe@host,21 */ char* extract_userhost(const char* s) { const char *p; char *r; if (*s == '"') { p = strchr(s+1, '"'); if (!p) { return 0; } p = strchr(p+1, ','); if (!p) { p = s + strlen(s); } } else { p = strchr(s, ','); if (!p) { p = s + strlen(s); } } r = (char*)malloc(p - s + 1); enough_mem(r); strncpy(r, s, p - s); r[p - s] = '\0'; return r; } /* extracts foo from joe@foo,21 or bla from "foo bar"@bla,21 */ char* extract_hostname(const char* s) { const char* p; char* t = extract_userhost(s); char* r; if (!t) { return (char*) 0; } p = strrchr(t, '@'); if (!p) { return (char*) 0; } p++; r = strdup(p); enough_mem(r); free(t); return r; } unsigned int extract_port(const char* s) { const char* p, *t; unsigned int pno; int i; if (!s || !*s) { return 0; } if ( ! (p = strrchr(s, '@')) ) { return 0; } if ( ! (t = strchr(p, ':')) ) { t = strchr(p, ','); } if (!t) { return 0; } i = sscanf(t, "%u", &pno); if ( i != 1 ) { return 0; } return pno; } char* extract_path(const char* pathfile) { const char* last_slash = strrchr(pathfile, '/'); char* path; size_t size; if ( ! last_slash) { /* no path, it's just a filename */ return strdup("."); } size = last_slash - pathfile + 1 + 1; path = (char*) malloc(size); enough_mem(path); snprintf(path, size, "%s", pathfile); return path; } char* extract_file(const char* pathfile) { const char* last_slash = strrchr(pathfile, '/'); char* file; size_t size; if ( ! last_slash) { /* no path, it's just a filename */ return strdup(pathfile); } size = strlen(pathfile) - ( last_slash - pathfile + 1 ) + 1; file = (char*) malloc(size); enough_mem(file); snprintf(file, size, "%s", last_slash + 1); return file; } int cryptcmp(const char* encrypted, const char* clear) { const char* cmp; #ifdef HAVE_CRYPT cmp = crypt(clear, encrypted); #else cmp = clear; #endif if ( ! clear || ! encrypted ) { return 1; } return strcmp(cmp, encrypted); } char* cryptpw(const char* clear) { #ifdef HAVE_CRYPT char* crypted = 0; char* ret = 0; char salt[3]; char saltposs[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '/' }; int idx1, idx2; time_t tm; tm = time(NULL) * getpid(); srand(tm); idx1 = (int)((double)rand() / ((double)RAND_MAX + 1) * sizeof saltposs); idx2 = (int)((double)rand() / ((double)RAND_MAX + 1) * sizeof saltposs); salt[0] = saltposs[idx1]; salt[1] = saltposs[idx2]; salt[2] = '\0'; crypted = crypt(clear, salt); ret = (char*) malloc(strlen(crypted) + 1); enough_mem(ret); strcpy(ret, crypted); printf("%s\n", crypted); return ret; #else return "foo"; #endif } /* encrypt_password() just reads a password from stdin and outputs the * encrypted version */ void encrypt_password() { #ifdef HAVE_CRYPT char* pw = getpass("Password: "); char* crypted = cryptpw(pw); memset(crypted, 0, strlen(pw)); #else char* crypted = "No crypt support compiled in."; #endif printf("%s\n", crypted); } char* to_ascii(char *data, int *len, int strictconversion) { int count, len2 = *len; char* buffer2 = (char*) malloc(*len * 2); int last = 0; char *b2ptr = buffer2; for (count = 0; count < len2; count++) { if ((data[count] == 10)) { if (strictconversion || last != 13) { *b2ptr = 13; b2ptr++; (*len)++; } } *b2ptr = data[count]; last = *b2ptr; b2ptr++; } return buffer2; } FILE* open_logfile(const char* fname) { FILE* logf; int err; umask(0077); logf = fopen(fname, "a"); umask(0000); err = errno; if (!logf) { jlog(1, "Couldn't open the log file %s: %s", fname, strerror(errno)); } errno = err; return logf; } /* changecode() changes the response code at the beginning of each line to a * new value. In fact, NEW is a _char*_, so you can put any data at the * beginning of each line, not just a number * * Parameters: msg: the origianl message * new: the portion that should be prepended. * * Return values: 0 on success * * Called by login() in order to display the original 220 welcome message * after the login with a changed code. * * */ static int changecode(char *const msg, const char* new) { char *nextline = msg; while (nextline) { if (nextline[0] >= '0' && nextline[0] <= '9') { /* we subtract 2 because of "\r\n". If nextline is * "foo\r\n" we may at most substitute "bar" or * another 3 character long string, since * 3 == 5 - 2 */ if (strlen(new) <= strlen(nextline) - 2) { strncpy(nextline, new, strlen(new)); } } nextline = strstr(nextline, "\r\n"); nextline += 2; if (!*nextline) { /* end */ nextline = 0; } } return 0; } /* merges two FTP resopnses. * * Let resp1 be "220 Bla" and resp2 "331 Foo", the function will return a * malloc()ed string * "331-Bla * 331 Foo" * * The code will be read from the char* pointing to resp2 */ char* merge_responses(const char* resp1, const char* resp2) { char codebuf[5]; char *ret_str; size_t ret_size; codebuf[0] = codebuf[1] = codebuf[2] = ' '; strncpy(codebuf, resp2, 3); codebuf[3] = '-'; codebuf[4] = '\0'; ret_size = strlen(resp1) + strlen(resp2) + 1 + 1; ret_str = (char*) malloc(ret_size); enough_mem(ret_str); snprintf(ret_str, ret_size, "%s", resp1); changecode(ret_str, codebuf); scnprintf(ret_str, ret_size, "%s", resp2); return ret_str; } int change_root(const char* stage) { int i; int change_id_back = 0; const char* directory = config_get_option("changerootdir"); const char* req_stage = config_get_option("changeroot"); if (srvinfo.chrooted) { return 0; } if (strcasecmp(req_stage, stage) != 0) { return 0; } if (!directory) { return 0; } if (geteuid() != 0) { if (getuid() != 0) { jlog(4, "Not root - no attempt to chroot()"); return 0; } /* getuid == 0 but geteuid != 0 */ if (changeid(PRIV, UID, "Changing UID to root for chroot") < 0) { return -1; } change_id_back = 1; } /* getuid == 0, we have a directory and the state matches */ i = chdir(directory); if (i) { jlog(4, "Could not chdir to %s", directory); changeid(UNPRIV, EUID, "After chroot (failed)"); return -1; } i = chroot(directory); if (!i) { jlog(7, "Changed root directory to %s", directory); srvinfo.chrooted = 1; } else { jlog(4, "Error changeing root directory to %s: %s", directory, strerror(errno)); changeid(UNPRIV, EUID, "After chroot (failed)"); return -1; } if (changeid(UNPRIV, EUID, "After chroot (succeeded)") < 0) { return -1; } i = chdir("/"); if (i) { jlog(4, "Could not chdir to / of the chrooted environment"); return -1; } return 0; } int dropprivileges(const char* stage) { if (config_compare_option("dropprivileges", stage)) { if (config_get_option("runasgroup")) { if (changeid(UNPRIV, GID, "Dropping group privileges") < 0) { return -1; } } if (config_get_option("runasuser")) { if (changeid(UNPRIV, UID, "Dropping user privileges") < 0) { return -1; } } else { if (getuid() == 0) { /* don't bring up a message if we aren't * root anyway */ jlog(2, "Option runasuser not set but dropping " "privileges requested - terminating"); jlog(2, "If you don't have a dropprivileges option in your configuration file at all, this is because dropprivileges is a standard option. If you really want to run as root, set \"dropprivileges never\" in the configuration file."); return -1; } } } return 0; } unsigned long int get_uint_ip(int type, struct clientinfo* clntinfo) { unsigned long int *ip_ptr; int *fd_ptr; if (type == GET_IP_SERVER) { ip_ptr = &clntinfo->server_ip; fd_ptr = &clntinfo->serversocket; } else if (type == GET_IP_CLIENT) { ip_ptr = &clntinfo->client_ip; fd_ptr = &clntinfo->clientsocket; } else { return UINT_MAX; } /* The IP is already known, return it */ if (*ip_ptr != UINT_MAX) { return *ip_ptr; } /* The IP is not known but if we are connected we look at the socket */ if (*fd_ptr != -1) { *ip_ptr = get_uint_peer_ip(*fd_ptr); return *ip_ptr; } /* if we are testing for the destination, i.e. SERVER and * clntinfo->destination happens to be an IP, use this one */ if (type == GET_IP_SERVER && clntinfo->destination) { *ip_ptr = inet_addr(clntinfo->destination); } return *ip_ptr; } const char* get_char_ip(int type, struct clientinfo *clntinfo) { unsigned long int ip = get_uint_ip(type, clntinfo); struct in_addr addr; addr.s_addr = ip; return inet_ntoa(addr); } /* * Takes a socket descriptor and returns the string contain the peer's * IP address. */ const char *get_char_peer_ip(int fd) { unsigned long int ip; struct in_addr addr; ip = get_uint_peer_ip(fd); addr.s_addr = ip; return inet_ntoa(addr); } /* * Takes a socket descriptor and returns the peer's IP address as a unsigned * int */ unsigned long int get_uint_peer_ip(int fd) { struct sockaddr_in name; #ifdef HAVE_SOCKLEN_T socklen_t namelen; #else int namelen; #endif namelen = sizeof(name); if (getpeername(fd, (struct sockaddr *) &name, &namelen) != 0) { jlog(2, "Could not get peername: %s", strerror(errno)); return UINT_MAX; } return name.sin_addr.s_addr; } int get_interface_ip(const char* iface, struct sockaddr_in *sin) { #ifdef HAVE_SIOCGIFADDR struct ifreq req; int fd = socket(PF_INET, SOCK_DGRAM, 0); int ret; #ifndef IF_NAMESIZE # define IF_NAMESIZE IFNAMSIZ #endif memset(req.ifr_name, 0, IF_NAMESIZE); strncpy(req.ifr_name, iface, IF_NAMESIZE - 1); req.ifr_addr.sa_family = AF_INET; ret = ioctl(fd, SIOCGIFADDR, &req); close(fd); if (ret == 0) { memcpy(sin, &req.ifr_addr, sizeof(struct sockaddr_in)); return 0; } #endif return -1; } int get_interface_name(const struct sockaddr_in sin_req, char* iface) { #ifdef HAVE_SIOCGIFCONF struct ifconf ifc; struct sockaddr_in sin; int ret, fd = -1, nr = 30, n, found; struct ifreq *ifr; fd = socket (PF_INET, SOCK_DGRAM, 0); if (fd < 0) { jlog(7, "Failed to create socket in get_interface_name"); return -1; } memset (&ifc, 0, sizeof(ifc)); ifc.ifc_buf = (void*) 0; ifc.ifc_len = sizeof(struct ifreq) * nr; ifc.ifc_buf = malloc(ifc.ifc_len); enough_mem(ifc.ifc_buf); for (;;) { ifc.ifc_len = sizeof(struct ifreq) * nr; ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len); enough_mem(ifc.ifc_buf); if ((ret = ioctl(fd, SIOCGIFCONF, &ifc)) < 0) { jlog(5, "ioctl error: %s", strerror(errno)); break; } if (ifc.ifc_len == sizeof(struct ifreq) * nr) { /* assume it overflowed and try again */ nr += 10; continue; } break; } if (ret < 0) { free(ifc.ifc_buf); return -1; } /* loop through interfaces returned from SIOCGIFCONF */ found = 0; ifr = ifc.ifc_req; for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) { if (get_interface_ip(ifr->ifr_name, &sin) == 0) { if (sin.sin_addr.s_addr == sin_req.sin_addr.s_addr) { snprintf(iface, IF_NAMESIZE, "%s", ifr->ifr_name); jlog(8, "Interface %s was IP %s", ifr->ifr_name, inet_ntoa(sin.sin_addr)); found = 1; break; } } ifr++; } /* we don't need this memory any more */ free (ifc.ifc_buf); close (fd); if (found == 1) { return 0; } #endif return -1; } const char* conv_ip_to_char(unsigned long int ip) { struct in_addr addr; addr.s_addr = ip; return inet_ntoa(addr); } void replace_not_larger(char* s, char* replace_what, char* replace_with) { char* p, *pend; p = s; while ((p = strstr(p, replace_what))) { pend = p + strlen(replace_what); snprintf(p, strlen(p), "%s%s", replace_with, pend); } } char* char_prepend(const char* prefix, const char* ostr) { char* nstr; int newsize; if ( ! ostr && ! prefix ) { return (char*) 0; } if ( ! ostr ) { return strdup(prefix); } if ( ! prefix ) { return strdup(ostr); } newsize = strlen(ostr) + strlen(prefix) + 1; nstr = (char*) malloc( newsize ); enough_mem(nstr); snprintf(nstr, newsize, "%s%s", prefix, ostr); return nstr; } char* char_append(const char* ostr, const char* suffix) { return char_prepend(ostr, suffix); } char* char_enclose(const char* prefix, const char* ostr, const char* suffix) { char* nstr1, *nstr2; nstr1 = char_prepend(prefix, ostr); nstr2 = char_prepend(nstr1, suffix); free(nstr1); return nstr2; } char* strnulldup(const char* str) { char* s; if ( ! str ) { return (char*) 0; } s = strdup(str); enough_mem(s); return s; } char* strfilldup(const char* str, const char* fill) { char* s; if ( ! str ) { s = strdup(fill); } else { s = strdup(str); } enough_mem(s); return s; }