/* Copyright 2003 Roger Dingledine */ /* See LICENSE for licensing information */ /* $Id$ */ /** * \file util.c * * \brief Common functions for strings, IO, network, data structures, * process control. **/ /* This is required on rh7 to make strptime not complain. */ #define _GNU_SOURCE #include "orconfig.h" #include "util.h" #include "log.h" #include "crypto.h" /* XXXX probably some of these are unneeded. find out which. */ #ifdef MS_WINDOWS #include #include #include #include #endif #ifdef HAVE_CTYPE_H #include #endif #include #include #include #include #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_SYS_LIMITS_H #include #endif #ifdef HAVE_MACHINE_LIMITS_H #ifndef __FreeBSD__ /* FreeBSD has a bug where it complains that this file is obsolete, and I should migrate to using sys/limits. It complains even when I include both. */ #include #endif #endif #ifdef HAVE_SYS_TYPES_H #include /* Must be included before sys/stat.h for Ultrix */ #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_FCNTL_H #include #endif #ifdef HAVE_FCNTL_H #include #endif /* used by inet_addr, not defined on solaris anywhere!? */ #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long) -1) #endif #ifndef O_BINARY #define O_BINARY 0 #endif /* ===== * Memory management * ===== */ /** Allocate a chunk of size bytes of memory, and return a pointer to * result. On error, log and terminate the process. (Same as malloc(size), * but never returns NULL.) */ void *tor_malloc(size_t size) { void *result; /* Some libcs don't do the right thing on size==0. Override them. */ if (size==0) { size=1; } result = malloc(size); if(!result) { log_fn(LOG_ERR, "Out of memory. Dying."); exit(1); } // memset(result,'X',size); /* deadbeef to encourage bugs */ return result; } /* Allocate a chunk of size bytes of memory, fill the memory with * zero bytes, and return a pointer to the result. Log and terminate * the process on error. (Same as calloc(size,1), but never returns NULL.) */ void *tor_malloc_zero(size_t size) { void *result = tor_malloc(size); memset(result, 0, size); return result; } /** Change the size of the memory block pointed to by ptr to size * bytes long; return the new memory block. On error, log and * terminate. (Like realloc(ptr,size), but never returns NULL.) */ void *tor_realloc(void *ptr, size_t size) { void *result; result = realloc(ptr, size); if (!result) { log_fn(LOG_ERR, "Out of memory. Dying."); exit(1); } return result; } /** Return a newly allocated copy of the NUL-terminated string s. On * error, log and terminate. (Like strdup(s), but never returns * NULL.) */ char *tor_strdup(const char *s) { char *dup; tor_assert(s); dup = strdup(s); if(!dup) { log_fn(LOG_ERR,"Out of memory. Dying."); exit(1); } return dup; } /** Allocate and return a new string containing the first n * characters of s. If s is longer than n * characters, only the first n are copied. The result is * always NUL-terminated. (Like strndup(s,n), but never returns * NULL.) */ char *tor_strndup(const char *s, size_t n) { char *dup; tor_assert(s); dup = tor_malloc(n+1); strlcpy(dup, s, n+1); return dup; } /* ===== * String manipulation * ===== */ /** Remove from the string s every character which appears in * strip. Return the number of characters removed. */ int tor_strstrip(char *s, const char *strip) { char *read = s; while (*read) { if (strchr(strip, *read)) { ++read; } else { *s++ = *read++; } } *s = '\0'; return read-s; } /** Set the dest_len-byte buffer buf to contain the * string s, with the string insert inserted after every * n characters. Return 0 on success, -1 on failure. * * If rule is ALWAYS_TERMINATE, then always end the string with * insert, even if its length is not a multiple of n. If * rule is NEVER_TERMINATE, then never end the string with * insert, even if its length is a multiple of n. * If rule is TERMINATE_IF_EVEN, then end the string with insert * exactly when its length is a multiple of n. */ int tor_strpartition(char *dest, size_t dest_len, const char *s, const char *insert, size_t n, part_finish_rule_t rule) { char *destp; size_t len_in, len_out, len_ins; int is_even, remaining; tor_assert(s); tor_assert(insert); tor_assert(n > 0); len_in = strlen(s); len_ins = strlen(insert); len_out = len_in + (len_in/n)*len_ins; is_even = (len_in%n) == 0; switch(rule) { case ALWAYS_TERMINATE: if (!is_even) len_out += len_ins; break; case NEVER_TERMINATE: if (is_even && len_in) len_out -= len_ins; break; case TERMINATE_IF_EVEN: break; } if (dest_len < len_out+1) return -1; destp = dest; remaining = len_in; while(remaining) { strncpy(destp, s, n); remaining -= n; if (remaining < 0) { if (rule == ALWAYS_TERMINATE) strcpy(destp+n+remaining,insert); break; } else if (remaining == 0 && rule == NEVER_TERMINATE) { *(destp+n) = '\0'; break; } strcpy(destp+n, insert); s += n; destp += n+len_ins; } tor_assert(len_out == strlen(dest)); return 0; } /** Return a pointer to a NUL-terminated hexidecimal string encoding * the first fromlen bytes of from. (fromlen must be \<= 32.) The * result does not need to be deallocated, but repeated calls to * hex_str will trash old results. */ const char *hex_str(const char *from, size_t fromlen) { static char buf[65]; if (fromlen>(sizeof(buf)-1)/2) fromlen = (sizeof(buf)-1)/2; base16_encode(buf,sizeof(buf),from,fromlen); return buf; } /** Convert all alphabetic characters in the nul-terminated string s to * lowercase. */ void tor_strlower(char *s) { while (*s) { *s = tolower(*s); ++s; } } /* Compares the first strlen(s2) characters of s1 with s2. Returns as for * strcmp. */ int strcmpstart(const char *s1, const char *s2) { size_t n = strlen(s2); return strncmp(s1, s2, n); } /** Return a pointer to the first char of s that is not whitespace and * not a comment, or to the terminating NUL if no such character exists. */ const char *eat_whitespace(const char *s) { tor_assert(s); while(isspace((int)*s) || *s == '#') { while(isspace((int)*s)) s++; if(*s == '#') { /* read to a \n or \0 */ while(*s && *s != '\n') s++; if(!*s) return s; } } return s; } /** Return a pointer to the first char of s that is not a space or a tab, * or to the terminating NUL if no such character exists. */ const char *eat_whitespace_no_nl(const char *s) { while(*s == ' ' || *s == '\t') ++s; return s; } /** Return a pointer to the first char of s that is whitespace or #, * or to the terminating NUL if no such character exists. */ const char *find_whitespace(const char *s) { tor_assert(s); while(*s && !isspace((int)*s) && *s != '#') s++; return s; } /** Extract a long from the start of s, in the given numeric base. If * there is unconverted data and next is provided, set *next to the * first unconverted character. An error has occurred if no characters * are converted; or if there are unconverted characters and next is NULL; or * if the parsed value is not between min and max. When no error occurs, * return the parsed value and set *ok (if provided) to 1. When an error * ocurs, return 0 and set *ok (if provided) to 0. */ long tor_parse_long(const char *s, int base, long min, long max, int *ok, char **next) { char *endptr; long r; r = strtol(s, &endptr, base); /* Was at least one character converted? */ if (endptr == s) goto err; /* Were there unexpected unconverted characters? */ if (!next && *endptr) goto err; /* Is r within limits? */ if (r < min || r > max) goto err; if (ok) *ok = 1; if (next) *next = endptr; return r; err: if (ok) *ok = 0; if (next) *next = endptr; return 0; } #if 0 unsigned long tor_parse_ulong(const char *s, int base, unsigned long min, unsigned long max, int *ok, char **next) { char *endptr; unsigned long r; r = strtol(s, &endptr, base); /* Was at least one character converted? */ if (endptr == s) goto err; /* Were there unexpected unconverted characters? */ if (!next && *endptr) goto err; /* Is r within limits? */ if (r < min || r > max) goto err; if (ok) *ok = 1; if (next) *next = endptr; return r; err: if (ok) *ok = 0; if (next) *next = endptr; return 0; } #endif void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen) { const char *end; char *cp; tor_assert(destlen >= srclen*2+1); cp = dest; end = src+srclen; while (srctv_sec - start->tv_sec; if (secdiff+1 > LONG_MAX/1000000) { log_fn(LOG_WARN, "comparing times too far apart."); return LONG_MAX; } udiff = secdiff*1000000L + (end->tv_usec - start->tv_usec); if(udiff < 0) { log_fn(LOG_INFO, "start (%ld.%ld) is after end (%ld.%ld). Returning 0.", (long)start->tv_sec, (long)start->tv_usec, (long)end->tv_sec, (long)end->tv_usec); return 0; } return udiff; } /** Return -1 if *a \< *b, 0 if *a==*b, and 1 if *a \> *b. */ int tv_cmp(struct timeval *a, struct timeval *b) { if (a->tv_sec > b->tv_sec) return 1; if (a->tv_sec < b->tv_sec) return -1; if (a->tv_usec > b->tv_usec) return 1; if (a->tv_usec < b->tv_usec) return -1; return 0; } /** Increment *a by the number of seconds and microseconds in *b. */ void tv_add(struct timeval *a, struct timeval *b) { a->tv_usec += b->tv_usec; a->tv_sec += b->tv_sec + (a->tv_usec / 1000000); a->tv_usec %= 1000000; } /** Increment *a by ms milliseconds. */ void tv_addms(struct timeval *a, long ms) { a->tv_usec += (ms * 1000) % 1000000; a->tv_sec += ((ms * 1000) / 1000000) + (a->tv_usec / 1000000); a->tv_usec %= 1000000; } #define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400))) static int n_leapdays(int y1, int y2) { --y1; --y2; return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400); } /** Number of days per month in non-leap year; used by tor_timegm. */ static const int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /** Return a time_t given a struct tm. The result is given in GMT, and * does not account for leap seconds. */ time_t tor_timegm (struct tm *tm) { /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. * It's way more brute-force than fiddling with tzset(). */ time_t ret; unsigned long year, days, hours, minutes; int i; year = tm->tm_year + 1900; tor_assert(year >= 1970); tor_assert(tm->tm_mon >= 0); tor_assert(tm->tm_mon <= 11); days = 365 * (year-1970) + n_leapdays(1970,year); for (i = 0; i < tm->tm_mon; ++i) days += days_per_month[i]; if (tm->tm_mon > 1 && IS_LEAPYEAR(year)) ++days; days += tm->tm_mday - 1; hours = days*24 + tm->tm_hour; minutes = hours*60 + tm->tm_min; ret = minutes*60 + tm->tm_sec; return ret; } /* strftime is locale-specific, so we need to replace those parts */ static const char *WEEKDAY_NAMES[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; void format_rfc1123_time(char *buf, time_t t) { struct tm *tm = gmtime(&t); strftime(buf, RFC1123_TIME_LEN+1, "XXX, %d XXX %Y %H:%M:%S GMT", tm); tor_assert(tm->tm_wday >= 0); tor_assert(tm->tm_wday <= 6); memcpy(buf, WEEKDAY_NAMES[tm->tm_wday], 3); tor_assert(tm->tm_wday >= 0); tor_assert(tm->tm_mon <= 11); memcpy(buf+8, MONTH_NAMES[tm->tm_mon], 3); } int parse_rfc1123_time(const char *buf, time_t *t) { struct tm tm; char month[4]; char weekday[4]; int i, m; if (strlen(buf) != RFC1123_TIME_LEN) return -1; memset(&tm, 0, sizeof(tm)); if (sscanf(buf, "%3s, %d %3s %d %d:%d:%d GMT", weekday, &tm.tm_mday, month, &tm.tm_year, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) < 7) { log_fn(LOG_WARN, "Got invalid RFC1123 time \"%s\"", buf); return -1; } m = -1; for (i = 0; i < 12; ++i) { if (!strcmp(month, MONTH_NAMES[i])) { m = i; break; } } if (m<0) { log_fn(LOG_WARN, "Got invalid RFC1123 time \"%s\"", buf); return -1; } tm.tm_mon = m; tm.tm_year -= 1900; *t = tor_timegm(&tm); return 0; } void format_iso_time(char *buf, time_t t) { strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", gmtime(&t)); } int parse_iso_time(const char *cp, time_t *t) { struct tm st_tm; #ifdef HAVE_STRPTIME if (!strptime(cp, "%Y-%m-%d %H:%M:%S", &st_tm)) { log_fn(LOG_WARN, "Published time was unparseable"); return -1; } #else unsigned int year=0, month=0, day=0, hour=100, minute=100, second=100; if (sscanf(cp, "%u-%u-%u %u:%u:%u", &year, &month, &day, &hour, &minute, &second) < 6) { log_fn(LOG_WARN, "Published time was unparseable"); return -1; } if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 61) { log_fn(LOG_WARN, "Published time was nonsensical"); return -1; } st_tm.tm_year = year; st_tm.tm_mon = month-1; st_tm.tm_mday = day; st_tm.tm_hour = hour; st_tm.tm_min = minute; st_tm.tm_sec = second; #endif *t = tor_timegm(&st_tm); return 0; } /* ===== * File helpers * ===== */ /** Write count bytes from buf to fd. isSocket * must be 1 if fd was returned by socket() or accept(), and 0 if fd * was returned by open(). Return the number of bytes written, or -1 * on error. Only use if fd is a blocking fd. */ int write_all(int fd, const char *buf, size_t count, int isSocket) { size_t written = 0; int result; while(written != count) { if (isSocket) result = send(fd, buf+written, count-written, 0); else result = write(fd, buf+written, count-written); if(result<0) return -1; written += result; } return count; } /** Read from fd to buf, until we get count bytes * or reach the end of the file. * isSocket must be 1 if fd * was returned by socket() or accept(), and 0 if fd was returned by * open(). Return the number of bytes read, or -1 on error. Only use * if fd is a blocking fd. */ int read_all(int fd, char *buf, size_t count, int isSocket) { size_t numread = 0; int result; while(numread != count) { if (isSocket) result = recv(fd, buf+numread, count-numread, 0); else result = read(fd, buf+numread, count-numread); if(result<0) return -1; else if (result == 0) break; numread += result; } return count; } /* * Filesystem operations. */ /** Return FN_ERROR if filename can't be read, FN_NOENT if it doesn't * exist, FN_FILE if it is a regular file, or FN_DIR if it's a * directory. */ file_status_t file_status(const char *fname) { struct stat st; if (stat(fname, &st)) { if (errno == ENOENT) { return FN_NOENT; } return FN_ERROR; } if (st.st_mode & S_IFDIR) return FN_DIR; else if (st.st_mode & S_IFREG) return FN_FILE; else return FN_ERROR; } /** Check whether dirname exists and is private. If yes return 0. If * it does not exist, and create is set, try to create it and return 0 * on success. Else return -1. */ int check_private_dir(const char *dirname, int create) { int r; struct stat st; tor_assert(dirname); if (stat(dirname, &st)) { if (errno != ENOENT) { log(LOG_WARN, "Directory %s cannot be read: %s", dirname, strerror(errno)); return -1; } if (!create) { log(LOG_WARN, "Directory %s does not exist.", dirname); return -1; } log(LOG_INFO, "Creating directory %s", dirname); #ifdef MS_WINDOWS r = mkdir(dirname); #else r = mkdir(dirname, 0700); #endif if (r) { log(LOG_WARN, "Error creating directory %s: %s", dirname, strerror(errno)); return -1; } else { return 0; } } if (!(st.st_mode & S_IFDIR)) { log(LOG_WARN, "%s is not a directory", dirname); return -1; } #ifndef MS_WINDOWS if (st.st_uid != getuid()) { log(LOG_WARN, "%s is not owned by this UID (%d). You must fix this to proceed.", dirname, (int)getuid()); return -1; } if (st.st_mode & 0077) { log(LOG_WARN, "Fixing permissions on directory %s", dirname); if (chmod(dirname, 0700)) { log(LOG_WARN, "Could not chmod directory %s: %s", dirname, strerror(errno)); return -1; } else { return 0; } } #endif return 0; } /** Create a file named fname with the contents str. Overwrite the * previous fname if possible. Return 0 on success, -1 on failure. * * This function replaces the old file atomically, if possible. */ int write_str_to_file(const char *fname, const char *str, int bin) { return write_bytes_to_file(fname, str, strlen(str), bin); } /* DOCDOC */ int write_bytes_to_file(const char *fname, const char *str, size_t len, int bin) { char tempname[1024]; int fd; int result; if ((strlcpy(tempname,fname,1024) >= 1024) || (strlcat(tempname,".tmp",1024) >= 1024)) { log(LOG_WARN, "Filename %s.tmp too long (>1024 chars)", fname); return -1; } if ((fd = open(tempname, O_WRONLY|O_CREAT|O_TRUNC|(bin?O_BINARY:0), 0600)) < 0) { log(LOG_WARN, "Couldn't open %s for writing: %s", tempname, strerror(errno)); return -1; } result = write_all(fd, str, len, 0); if(result < 0 || (size_t)result != len) { log(LOG_WARN, "Error writing to %s: %s", tempname, strerror(errno)); close(fd); return -1; } if (close(fd)) { log(LOG_WARN,"Error flushing to %s: %s", tempname, strerror(errno)); return -1; } /* XXXX use replace_file() instead. */ #ifdef MS_WINDOWS /* On Windows, rename doesn't replace. We could call ReplaceFile, but * that's hard, and we can probably sneak by without atomicity. */ switch (file_status(fname)) { case FN_ERROR: log(LOG_WARN, "Error replacing %s: %s", fname, strerror(errno)); return -1; case FN_DIR: log(LOG_WARN, "Error replacing %s: is directory", fname); return -1; case FN_FILE: if (unlink(fname)) { log(LOG_WARN, "Error replacing %s while removing old copy: %s", fname, strerror(errno)); return -1; } break; case FN_NOENT: ; } #endif if (rename(tempname, fname)) { log(LOG_WARN, "Error replacing %s: %s", fname, strerror(errno)); return -1; } return 0; } /** Read the contents of filename into a newly allocated string; return the * string on success or NULL on failure. */ char *read_file_to_str(const char *filename, int bin) { int fd; /* router file */ struct stat statbuf; char *string; int r; tor_assert(filename); if(stat(filename, &statbuf) < 0) { log_fn(LOG_INFO,"Could not stat %s.",filename); return NULL; } fd = open(filename,O_RDONLY|(bin?O_BINARY:0),0); if (fd<0) { log_fn(LOG_WARN,"Could not open %s.",filename); return NULL; } string = tor_malloc(statbuf.st_size+1); r = read_all(fd,string,statbuf.st_size,0); if (r<0) { log_fn(LOG_WARN,"Error reading from file '%s': %s", filename, strerror(errno)); tor_free(string); close(fd); return NULL; } else if (bin && r != statbuf.st_size) { /* If we're in binary mode, then we'd better have an exact match for * size. Otherwise, win32 encoding may throw us off, and that's okay. */ log_fn(LOG_WARN,"Could read only %d of %ld bytes of file '%s'.", r, (long)statbuf.st_size,filename); tor_free(string); close(fd); return NULL; } close(fd); string[statbuf.st_size] = 0; /* null terminate it */ return string; } /** read lines from f (no more than maxlen-1 bytes each) until we * get a non-whitespace line. If it isn't of the form "key value" * (value can have spaces), return -1. * Point *key to the first word in line, point *value * to the second. * Put a \0 at the end of key, remove everything at the end of value * that is whitespace or comment. * Return 1 if success, 0 if no more lines, -1 if error. */ int parse_line_from_file(char *line, size_t maxlen, FILE *f, char **key_out, char **value_out) { char *s; try_next_line: if(!fgets(line, maxlen, f)) { if(feof(f)) return 0; return -1; /* real error */ } line[maxlen-1] = '\0'; s = parse_line_from_str(line, key_out, value_out); if (!s) return -1; if (!*key_out) goto try_next_line; return 1; #if 0 if((s = strchr(line,'#'))) /* strip comments */ *s = 0; /* stop the line there */ /* remove end whitespace */ s = strchr(line, 0); /* now we're at the null */ do { *s = 0; s--; } while (s >= line && isspace((int)*s)); key = line; while(isspace((int)*key)) key++; if(*key == 0) goto try_next_line; /* this line has nothing on it */ end = key; while(*end && !isspace((int)*end)) end++; value = end; while(*value && isspace((int)*value)) value++; #if 0 if(!*end || !*value) { /* only a key on this line. no value. */ *end = 0; log_fn(LOG_WARN,"Line has keyword '%s' but no value. Failing.",key); return -1; } #endif *end = 0; /* null it out */ tor_assert(key); tor_assert(value); log_fn(LOG_DEBUG,"got keyword '%s', value '%s'", key, value); *key_out = key, *value_out = value; return 1; #endif } /** DOCDOC. * * Return next line or end of string on success, NULL on failure. */ char * parse_line_from_str(char *line, char **key_out, char **value_out) { char *key, *val, *cp; tor_assert(key_out); tor_assert(value_out); *key_out = *value_out = key = val = NULL; /* Skip until the first keyword. */ while (1) { while (isspace(*line)) ++line; if (*line == '#') { while (*line && *line != '\n') ++line; } else { break; } } if (!*line) { /* End of string? */ *key_out = *value_out = NULL; return line; } /* Skip until the next space. */ key = line; while (*line && !isspace(*line) && *line != '#') ++line; /* Skip until the value */ while (*line == ' ' || *line == '\t') *line++ = '\0'; val = line; /* Find the end of the line. */ while (*line && *line != '\n' && *line != '#') ++line; if (*line == '\n') cp = line++; else { cp = line-1; } while (cp>=val && isspace(*cp)) *cp-- = '\0'; if (*line == '#') { do { *line++ = '\0'; } while (*line && *line != '\n'); if (*line == '\n') ++line; } *key_out = key; *value_out = val; return line; } /** Expand any homedir prefix on 'filename'; return a newly allocated * string. */ char *expand_filename(const char *filename) { tor_assert(filename); /* XXXX Should eventually check for ~username/ */ if (!strncmp(filename,"~/",2)) { size_t len; const char *home = getenv("HOME"); char *result; if (!home) { log_fn(LOG_WARN, "Couldn't find $HOME environment variable while expanding %s", filename); return NULL; } /* minus two characters for ~/, plus one for /, plus one for NUL. * Round up to 16 in case we can't do math. */ len = strlen(home)+strlen(filename)+16; result = tor_malloc(len); tor_snprintf(result,len,"%s/%s",home,filename+2); return result; } else { return tor_strdup(filename); } } /* ===== * Net helpers * ===== */ /** Return true iff ip (in host order) is an IP reserved to localhost, * or reserved for local networks by RFC 1918. */ int is_internal_IP(uint32_t ip) { if (((ip & 0xff000000) == 0x0a000000) || /* 10/8 */ ((ip & 0xff000000) == 0x00000000) || /* 0/8 */ ((ip & 0xff000000) == 0x7f000000) || /* 127/8 */ ((ip & 0xffff0000) == 0xa9fe0000) || /* 169.254/16 */ ((ip & 0xfff00000) == 0xac100000) || /* 172.16/12 */ ((ip & 0xffff0000) == 0xc0a80000)) /* 192.168/16 */ return 1; return 0; } /** Return true iff ip (in host order) is judged to be on the * same network as us. For now, check if it's an internal IP. For XXX008, * also check if it's on the same class C network as our public IP. */ int is_local_IP(uint32_t ip) { return is_internal_IP(ip); } /** Similar behavior to Unix gethostbyname: resolve name, and set * *addr to the proper IP address, in network byte order. Returns 0 * on success, -1 on failure; 1 on transient failure. * * (This function exists because standard windows gethostbyname * doesn't treat raw IP addresses properly.) */ int tor_lookup_hostname(const char *name, uint32_t *addr) { /* Perhaps eventually this should be replaced by a tor_getaddrinfo or * something. */ struct in_addr iaddr; struct hostent *ent; tor_assert(addr); if (!*name) { /* Empty address is an error. */ return -1; } else if (tor_inet_aton(name, &iaddr)) { /* It's an IP. */ memcpy(addr, &iaddr.s_addr, 4); return 0; } else { ent = gethostbyname(name); if (ent) { /* break to remind us if we move away from IPv4 */ tor_assert(ent->h_length == 4); memcpy(addr, ent->h_addr, 4); return 0; } memset(addr, 0, 4); #ifdef MS_WINDOWS return (WSAGetLastError() == WSATRY_AGAIN) ? 1 : -1; #else return (h_errno == TRY_AGAIN) ? 1 : -1; #endif } } /** Parse a string of the form "host[:port]" from addrport. If * address is provided, set *address to a copy of the * host portion of the string. If addr is provided, try to * resolve the host portion of the string and store it into * *addr (in host byte order). If port is provided, * store the port number into *port, or 0 if no port is given. * Return 0 on success, -1 on failure. */ int parse_addr_port(const char *addrport, char **address, uint32_t *addr, uint16_t *port) { const char *colon; char *_address = NULL; int _port; int ok = 1; tor_assert(addrport); tor_assert(port); colon = strchr(addrport, ':'); if (colon) { _address = tor_strndup(addrport, colon-addrport); _port = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL); if (!_port) { log_fn(LOG_WARN, "Port '%s' out of range", colon+1); ok = 0; } } else { _address = tor_strdup(addrport); _port = 0; } if (addr) { /* There's an addr pointer, so we need to resolve the hostname. */ if (tor_lookup_hostname(_address,addr)) { log_fn(LOG_WARN, "Couldn't look up '%s'", _address); ok = 0; *addr = 0; } *addr = ntohl(*addr); } if (address && ok) { *address = _address; } else { if (address) *address = NULL; tor_free(_address); } if (port) *port = ok ? ((uint16_t) _port) : 0; return ok ? 0 : -1; } /** Parse a string s in the format of * (IP(/mask|/mask-bits)?|*):(*|port(-maxport)?), setting the various * *out pointers as appropriate. Return 0 on success, -1 on failure. */ int parse_addr_and_port_range(const char *s, uint32_t *addr_out, uint32_t *mask_out, uint16_t *port_min_out, uint16_t *port_max_out) { char *address; char *mask, *port, *endptr; struct in_addr in; int bits; tor_assert(s); tor_assert(addr_out); tor_assert(mask_out); tor_assert(port_min_out); tor_assert(port_max_out); address = tor_strdup(s); /* Break 'address' into separate strings. */ mask = strchr(address,'/'); port = strchr(mask?mask:address,':'); if (mask) *mask++ = '\0'; if (port) *port++ = '\0'; /* Now "address" is the IP|'*' part... * "mask" is the Mask|Maskbits part... * and "port" is the *|port|min-max part. */ if (strcmp(address,"*")==0) { *addr_out = 0; } else if (tor_inet_aton(address, &in) != 0) { *addr_out = ntohl(in.s_addr); } else { log_fn(LOG_WARN, "Malformed IP %s in address pattern; rejecting.",address); goto err; } if (!mask) { if (strcmp(address,"*")==0) *mask_out = 0; else *mask_out = 0xFFFFFFFFu; } else { endptr = NULL; bits = (int) strtol(mask, &endptr, 10); if (!*endptr) { /* strtol handled the whole mask. */ if (bits < 0 || bits > 32) { log_fn(LOG_WARN, "Bad number of mask bits on address range; rejecting."); goto err; } *mask_out = ~((1<<(32-bits))-1); } else if (tor_inet_aton(mask, &in) != 0) { *mask_out = ntohl(in.s_addr); } else { log_fn(LOG_WARN, "Malformed mask %s on address range; rejecting.", mask); goto err; } } if (!port || strcmp(port, "*") == 0) { *port_min_out = 1; *port_max_out = 65535; } else { endptr = NULL; *port_min_out = (uint16_t) tor_parse_long(port, 10, 1, 65535, NULL, &endptr); if (*endptr == '-') { port = endptr+1; endptr = NULL; *port_max_out = (uint16_t) tor_parse_long(port, 10, 1, 65535, NULL, &endptr); if (*endptr || !*port_max_out) { log_fn(LOG_WARN, "Malformed port %s on address range rejecting.", port); } } else if (*endptr || !*port_min_out) { log_fn(LOG_WARN, "Malformed port %s on address range; rejecting.", port); goto err; } else { *port_max_out = *port_min_out; } if (*port_min_out > *port_max_out) { log_fn(LOG_WARN,"Insane port range on address policy; rejecting."); goto err; } } tor_free(address); return 0; err: tor_free(address); return -1; } /* ===== * Process helpers * ===== */ #ifndef MS_WINDOWS /* Based on code contributed by christian grothoff */ static int start_daemon_called = 0; static int finish_daemon_called = 0; static int daemon_filedes[2]; /** Start putting the process into daemon mode: fork and drop all resources * except standard fds. The parent process never returns, but stays around * until finish_daemon is called. (Note: it's safe to call this more * than once: calls after the first are ignored.) */ void start_daemon(const char *desired_cwd) { pid_t pid; if (start_daemon_called) return; start_daemon_called = 1; if(!desired_cwd) desired_cwd = "/"; /* Don't hold the wrong FS mounted */ if (chdir(desired_cwd) < 0) { log_fn(LOG_ERR,"chdir to %s failed. Exiting.",desired_cwd); exit(1); } pipe(daemon_filedes); pid = fork(); if (pid < 0) { log_fn(LOG_ERR,"fork failed. Exiting."); exit(1); } if (pid) { /* Parent */ int ok; char c; close(daemon_filedes[1]); /* we only read */ ok = -1; while (0 < read(daemon_filedes[0], &c, sizeof(char))) { if (c == '.') ok = 1; } fflush(stdout); if (ok == 1) exit(0); else exit(1); /* child reported error */ } else { /* Child */ close(daemon_filedes[0]); /* we only write */ pid = setsid(); /* Detach from controlling terminal */ /* * Fork one more time, so the parent (the session group leader) can exit. * This means that we, as a non-session group leader, can never regain a * controlling terminal. This part is recommended by Stevens's * _Advanced Programming in the Unix Environment_. */ if (fork() != 0) { exit(0); } return; } } /** Finish putting the process into daemon mode: drop standard fds, and tell * the parent process to exit. (Note: it's safe to call this more than once: * calls after the first are ignored. Calls start_daemon first if it hasn't * been called already.) */ void finish_daemon(void) { int nullfd; char c = '.'; if (finish_daemon_called) return; if (!start_daemon_called) start_daemon(NULL); finish_daemon_called = 1; nullfd = open("/dev/null", O_CREAT | O_RDWR | O_APPEND); if (nullfd < 0) { log_fn(LOG_ERR,"/dev/null can't be opened. Exiting."); exit(1); } /* close fds linking to invoking terminal, but * close usual incoming fds, but redirect them somewhere * useful so the fds don't get reallocated elsewhere. */ if (dup2(nullfd,0) < 0 || dup2(nullfd,1) < 0 || dup2(nullfd,2) < 0) { log_fn(LOG_ERR,"dup2 failed. Exiting."); exit(1); } write(daemon_filedes[1], &c, sizeof(char)); /* signal success */ close(daemon_filedes[1]); } #else /* defined(MS_WINDOWS) */ void start_daemon(const char *cp) {} void finish_daemon(void) {} #endif /** Write the current process ID, followed by NL, into filename. */ void write_pidfile(char *filename) { #ifndef MS_WINDOWS FILE *pidfile; if ((pidfile = fopen(filename, "w")) == NULL) { log_fn(LOG_WARN, "Unable to open %s for writing: %s", filename, strerror(errno)); } else { fprintf(pidfile, "%d\n", (int)getpid()); fclose(pidfile); } #endif } /* Local Variables: mode:c indent-tabs-mode:nil c-basic-offset:2 End: */