/*
* Copyright (C) 1999-2004 Joachim Wieland <joe@mcknight.de>
*
* 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 <stdio.h>
#include <stdarg.h>
#include <sys/time.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <net/if.h>
#ifdef HAVE_CRYPT_H
#include <pwd.h> /* getpass() */
#define _XOPEN_SOURCE
#include <crypt.h> /* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1