/*************************************************************************** * nbase_misc.c -- Some small miscelaneous utility/compatability * * functions. * * * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * * The Nmap Security Scanner is (C) 1996-2012 Insecure.Com LLC. Nmap is * * also a registered trademark of Insecure.Com LLC. This program is free * * software; you may redistribute and/or modify it under the terms of the * * GNU General Public License as published by the Free Software * * Foundation; Version 2 with the clarifications and exceptions described * * below. This guarantees your right to use, modify, and redistribute * * this software under certain conditions. If you wish to embed Nmap * * technology into proprietary software, we sell alternative licenses * * (contact sales@insecure.com). Dozens of software vendors already * * license Nmap technology such as host discovery, port scanning, OS * * detection, version detection, and the Nmap Scripting Engine. * * * * Note that the GPL places important restrictions on "derived works", yet * * it does not provide a detailed definition of that term. To avoid * * misunderstandings, we interpret that term as broadly as copyright law * * allows. For example, we consider an application to constitute a * * "derivative work" for the purpose of this license if it does any of the * * following: * * o Integrates source code from Nmap * * o Reads or includes Nmap copyrighted data files, such as * * nmap-os-db or nmap-service-probes. * * o Executes Nmap and parses the results (as opposed to typical shell or * * execution-menu apps, which simply display raw Nmap output and so are * * not derivative works.) * * o Integrates/includes/aggregates Nmap into a proprietary executable * * installer, such as those produced by InstallShield. * * o Links to a library or executes a program that does any of the above * * * * The term "Nmap" should be taken to also include any portions or derived * * works of Nmap, as well as other software we distribute under this * * license such as Zenmap, Ncat, and Nping. This list is not exclusive, * * but is meant to clarify our interpretation of derived works with some * * common examples. Our interpretation applies only to Nmap--we don't * * speak for other people's GPL works. * * * * If you have any questions about the GPL licensing restrictions on using * * Nmap in non-GPL works, we would be happy to help. As mentioned above, * * we also offer alternative license to integrate Nmap into proprietary * * applications and appliances. These contracts have been sold to dozens * * of software vendors, and generally include a perpetual license as well * * as providing for priority support and updates. They also fund the * * continued development of Nmap. Please email sales@insecure.com for * * further information. * * * * As a special exception to the GPL terms, Insecure.Com LLC grants * * permission to link the code of this program with any version of the * * OpenSSL library which is distributed under a license identical to that * * listed in the included docs/licenses/OpenSSL.txt file, and distribute * * linked combinations including the two. You must obey the GNU GPL in all * * respects for all of the code used other than OpenSSL. If you modify * * this file, you may extend this exception to your version of the file, * * but you are not obligated to do so. * * * * If you received these files with a written license agreement or * * contract stating terms other than the terms above, then that * * alternative license agreement takes precedence over these comments. * * * * Source is provided to this software because we believe users have a * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes (none * * have been found so far). * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to send your changes * * to nmap-dev@insecure.org for possible incorporation into the main * * distribution. By sending these changes to Fyodor or one of the * * Insecure.Org development mailing lists, or checking them into the Nmap * * source code repository, it is understood (unless you specify otherwise) * * that you are offering the Nmap Project (Insecure.Com LLC) the * * unlimited, non-exclusive right to reuse, modify, and relicense the * * code. Nmap will always be available Open Source, but this is important * * because the inability to relicense code has caused devastating problems * * for other Free Software projects (such as KDE and NASM). We also * * occasionally relicense the code to third parties as discussed above. * * If you wish to specify special license conditions of your * * contributions, just say so when you send them. * * * * 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 v2.0 for more details at * * http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file * * included with Nmap. * * * ***************************************************************************/ /* $Id$ */ #include "nbase.h" #ifndef WIN32 #include #ifndef errno extern int errno; #endif #else #include #endif #include #include #include "nbase_ipv6.h" #include "nbase_crc32ct.h" #include #include #ifdef WIN32 #include #endif #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif /* Returns the UNIX/Windows errno-equivalent. Note that the Windows call is socket/networking specific. The windows error number returned is like WSAMSGSIZE, but nbase.h includes #defines to correlate many of the common UNIX errors with their closest Windows equivalents. So you can use EMSGSIZE or EINTR. */ int socket_errno() { #ifdef WIN32 return WSAGetLastError(); #else return errno; #endif } /* We can't just use strerror to get socket errors on Windows because it has its own set of error codes: WSACONNRESET not ECONNRESET for example. This function will do the right thing on Windows. Call it like socket_strerror(socket_errno()) */ char *socket_strerror(int errnum) { #ifdef WIN32 static char buffer[128]; FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, 0, errnum, 0, buffer, sizeof(buffer), NULL); return buffer; #else return strerror(errnum); #endif } /* Compares two sockaddr_storage structures with a return value like strcmp. First the address families are compared, then the addresses if the families are equal. The structures must be real full-length sockaddr_storage structures, not something shorter like sockaddr_in. */ int sockaddr_storage_cmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b) { if (a->ss_family < b->ss_family) return -1; else if (a->ss_family > b->ss_family) return 1; if (a->ss_family == AF_INET) { struct sockaddr_in *sin_a = (struct sockaddr_in *) a; struct sockaddr_in *sin_b = (struct sockaddr_in *) b; if (sin_a->sin_addr.s_addr < sin_b->sin_addr.s_addr) return -1; else if (sin_a->sin_addr.s_addr > sin_b->sin_addr.s_addr) return 1; else return 0; } else if (a->ss_family == AF_INET6) { struct sockaddr_in6 *sin6_a = (struct sockaddr_in6 *) a; struct sockaddr_in6 *sin6_b = (struct sockaddr_in6 *) b; return memcmp(sin6_a->sin6_addr.s6_addr, sin6_b->sin6_addr.s6_addr, sizeof(sin6_a->sin6_addr.s6_addr)); } else { assert(0); } return 0; /* Not reached */ } int sockaddr_storage_equal(const struct sockaddr_storage *a, const struct sockaddr_storage *b) { return sockaddr_storage_cmp(a, b) == 0; } /* This function is an easier version of inet_ntop because you don't need to pass a dest buffer. Instead, it returns a static buffer that you can use until the function is called again (by the same or another thread in the process). If there is a wierd error (like sslen being too short) then NULL will be returned. */ const char *inet_ntop_ez(const struct sockaddr_storage *ss, size_t sslen) { const struct sockaddr_in *sin = (struct sockaddr_in *) ss; static char str[INET6_ADDRSTRLEN]; #if HAVE_IPV6 const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) ss; #endif str[0] = '\0'; if (sin->sin_family == AF_INET) { if (sslen < sizeof(struct sockaddr_in)) return NULL; return inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)); } #if HAVE_IPV6 else if(sin->sin_family == AF_INET6) { if (sslen < sizeof(struct sockaddr_in6)) return NULL; return inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str)); } #endif //Some laptops report the ip and address family of disabled wifi cards as null //so yes, we will hit this sometimes. return NULL; } /* Create a new socket inheritable by subprocesses. On non-Windows systems it's just a normal socket. */ int inheritable_socket(int af, int style, int protocol) { #ifdef WIN32 /* WSASocket is just like socket, except that the sockets it creates are inheritable by subprocesses (such as are created by CreateProcess), while those created by socket are not. */ return WSASocket(af, style, protocol, NULL, 0, 0); #else return socket(af, style, protocol); #endif } /* The dup function on Windows works only on file descriptors, not socket handles. This function accomplishes the same thing for sockets. */ int dup_socket(int sd) { #ifdef WIN32 HANDLE copy; if (DuplicateHandle(GetCurrentProcess(), (HANDLE) sd, GetCurrentProcess(), ©, 0, FALSE, DUPLICATE_SAME_ACCESS) == 0) { return -1; } return (int) copy; #else return dup(sd); #endif } int unblock_socket(int sd) { #ifdef WIN32 u_long one = 1; if(sd != 501) // Hack related to WinIP Raw Socket support ioctlsocket (sd, FIONBIO, &one); #else int options; /*Unblock our socket to prevent recvfrom from blocking forever on certain target ports. */ options = O_NONBLOCK | fcntl(sd, F_GETFL); fcntl(sd, F_SETFL, options); #endif //WIN32 return 1; } /* Convert a socket to blocking mode */ int block_socket(int sd) { #ifdef WIN32 unsigned long options=0; if(sd == 501) return 1; ioctlsocket(sd, FIONBIO, &options); #else int options; options = (~O_NONBLOCK) & fcntl(sd, F_GETFL); fcntl(sd, F_SETFL, options); #endif return 1; } /* Convert a time specification into a count of seconds. A time specification is * a non-negative real number, possibly followed by a units suffix. The suffixes * are "ms" for milliseconds, "s" for seconds, "m" for minutes, or "h" for * hours. Seconds is the default with no suffix. -1 is returned if the string * can't be parsed. */ double tval2secs(const char *tspec) { double d; char *tail; errno = 0; d = strtod(tspec, &tail); if (*tspec == '\0' || errno != 0) return -1; if (strcasecmp(tail, "ms") == 0) return d / 1000.0; else if (*tail == '\0' || strcasecmp(tail, "s") == 0) return d; else if (strcasecmp(tail, "m") == 0) return d * 60.0; else if (strcasecmp(tail, "h") == 0) return d * 60.0 * 60.0; else return -1; } long tval2msecs(const char *tspec) { double s, ms; s = tval2secs(tspec); if (s == -1) return -1; ms = s * 1000.0; if (ms > LONG_MAX || ms < LONG_MIN) return -1; return (long) ms; } /* Returns the unit portion of a time specification (such as "ms", "s", "m", or "h"). Returns NULL if there was a parsing error or no unit is present. */ const char *tval_unit(const char *tspec) { double d; char *tail; errno = 0; d = strtod(tspec, &tail); /* Avoid GCC 4.6 error "variable 'd' set but not used [-Wunused-but-set-variable]". */ (void) d; if (*tspec == '\0' || errno != 0 || *tail == '\0') return NULL; return tail; } /* A replacement for select on Windows that allows selecting on stdin * (file descriptor 0) and selecting on zero file descriptors (just for * the timeout). Plain Windows select doesn't work on non-sockets like * stdin and returns an error if no file descriptors were given, because * they were NULL or empty. This only works for sockets and stdin; if * you have a descriptor referring to a normal open file in the set, * Windows will return WSAENOTSOCK. */ int fselect(int s, fd_set *rmaster, fd_set *wmaster, fd_set *emaster, struct timeval *tv) { #ifdef WIN32 static int stdin_thread_started = 0; int fds_ready = 0; int iter = -1, i; struct timeval stv; fd_set rset, wset, eset; /* Figure out whether there are any FDs in the sets, as @$@!$# Windows returns WSAINVAL (10022) if you call a select() with no FDs, even though the Linux man page says that doing so is a good, reasonably portable way to sleep with subsecond precision. Sigh. */ for(i = s; i > STDIN_FILENO; i--) { if ((rmaster != NULL && FD_ISSET(i, rmaster)) || (wmaster != NULL && FD_ISSET(i, wmaster)) || (emaster != NULL && FD_ISSET(i, emaster))) break; s--; } /* Handle the case where stdin is not being read from. */ if (rmaster == NULL || !FD_ISSET(STDIN_FILENO, rmaster)) { if (s > 0) { /* Do a normal select. */ return select(s, rmaster, wmaster, emaster, tv); } else { /* No file descriptors given. Just sleep. */ if (tv == NULL) { /* Sleep forever. */ while (1) sleep(10000); } else { usleep(tv->tv_sec * 1000000UL + tv->tv_usec); return 0; } } } /* This is a hack for Windows, which doesn't allow select()ing on * non-sockets (like stdin). We remove stdin from the fd_set and * loop while select()ing on everything else, with a timeout of * 125ms. Then we check if stdin is ready and increment fds_ready * and set stdin in rmaster if it looks good. We just keep looping * until we have something or it times out. */ /* nbase_winunix.c has all the nasty details behind checking if * stdin has input. It involves a background thread, which we start * now if necessary. */ if (!stdin_thread_started) { int ret = win_stdin_start_thread(); assert(ret != 0); stdin_thread_started = 1; } FD_CLR(STDIN_FILENO, rmaster); if (tv) { int usecs = (tv->tv_sec * 1000000) + tv->tv_usec; iter = usecs / 125000; if (usecs % 125000) iter++; } FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); while (!fds_ready && iter) { stv.tv_sec = 0; stv.tv_usec = 125000; if (rmaster) rset = *rmaster; if (wmaster) wset = *wmaster; if (emaster) eset = *emaster; fds_ready = 0; /* selecting on anything other than stdin? */ if (s > 1) fds_ready = select(s, &rset, &wset, &eset, &stv); if (fds_ready > -1 && win_stdin_ready()) { FD_SET(STDIN_FILENO, &rset); fds_ready++; } if (tv) iter--; } if (rmaster) *rmaster = rset; if (wmaster) *wmaster = wset; if (emaster) *emaster = eset; return fds_ready; #else return select(s, rmaster, wmaster, emaster, tv); #endif } /* * CRC32 Cyclic Redundancy Check * * From: http://www.ietf.org/rfc/rfc1952.txt * * Copyright (c) 1996 L. Peter Deutsch * * Permission is granted to copy and distribute this document for any * purpose and without charge, including translations into other * languages and incorporation into compilations, provided that the * copyright notice and this notice are preserved, and that any * substantive changes or deletions from the original are clearly * marked. * */ /* Table of CRCs of all 8-bit messages. */ static unsigned long crc_table[256]; /* Flag: has the table been computed? Initially false. */ static int crc_table_computed = 0; /* Make the table for a fast CRC. */ static void make_crc_table(void) { unsigned long c; int n, k; for (n = 0; n < 256; n++) { c = (unsigned long) n; for (k = 0; k < 8; k++) { if (c & 1) { c = 0xedb88320L ^ (c >> 1); } else { c = c >> 1; } } crc_table[n] = c; } crc_table_computed = 1; } /* Update a running crc with the bytes buf[0..len-1] and return the updated crc. The crc should be initialized to zero. Pre- and post-conditioning (one's complement) is performed within this function so it shouldn't be done by the caller. Usage example: unsigned long crc = 0L; while (read_buffer(buffer, length) != EOF) { crc = update_crc(crc, buffer, length); } if (crc != original_crc) error(); */ static unsigned long update_crc(unsigned long crc, unsigned char *buf, int len) { unsigned long c = crc ^ 0xffffffffL; int n; if (!crc_table_computed) make_crc_table(); for (n = 0; n < len; n++) { c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); } return c ^ 0xffffffffL; } /* Return the CRC of the bytes buf[0..len-1]. */ unsigned long nbase_crc32(unsigned char *buf, int len) { return update_crc(0L, buf, len); } /* * CRC-32C (Castagnoli) Cyclic Redundancy Check. * Taken straight from RFC 4960 (SCTP). */ /* Return the CRC-32C of the bytes buf[0..len-1] */ unsigned long nbase_crc32c(unsigned char *buf, int len) { int i; unsigned long crc32 = ~0L; unsigned long result; unsigned char byte0, byte1, byte2, byte3; for (i = 0; i < len; i++) { CRC32C(crc32, buf[i]); } result = ~crc32; /* result now holds the negated polynomial remainder; * since the table and algorithm is "reflected" [williams95]. * That is, result has the same value as if we mapped the message * to a polynomial, computed the host-bit-order polynomial * remainder, performed final negation, then did an end-for-end * bit-reversal. * Note that a 32-bit bit-reversal is identical to four inplace * 8-bit reversals followed by an end-for-end byteswap. * In other words, the bytes of each bit are in the right order, * but the bytes have been byteswapped. So we now do an explicit * byteswap. On a little-endian machine, this byteswap and * the final ntohl cancel out and could be elided. */ byte0 = result & 0xff; byte1 = (result >> 8) & 0xff; byte2 = (result >> 16) & 0xff; byte3 = (result >> 24) & 0xff; crc32 = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3); return crc32; } /* * Adler32 Checksum Calculation. * Taken straight from RFC 2960 (SCTP). */ #define ADLER32_BASE 65521 /* largest prime smaller than 65536 */ /* * Update a running Adler-32 checksum with the bytes buf[0..len-1] * and return the updated checksum. The Adler-32 checksum should * be initialized to 1. */ static unsigned long update_adler32(unsigned long adler, unsigned char *buf, int len) { unsigned long s1 = adler & 0xffff; unsigned long s2 = (adler >> 16) & 0xffff; int n; for (n = 0; n < len; n++) { s1 = (s1 + buf[n]) % ADLER32_BASE; s2 = (s2 + s1) % ADLER32_BASE; } return (s2 << 16) + s1; } /* Return the Adler32 of the bytes buf[0..len-1] */ unsigned long nbase_adler32(unsigned char *buf, int len) { return update_adler32(1L, buf, len); } #undef ADLER32_BASE /* This function returns a string containing the hexdump of the supplied * buffer. It uses current locale to determine if a character is printable or * not. It prints 73char+\n wide lines like these: 0000 e8 60 65 86 d7 86 6d 30 35 97 54 87 ff 67 05 9e .`e...m05.T..g.. 0010 07 5a 98 c0 ea ad 50 d2 62 4f 7b ff e1 34 f8 fc .Z....P.bO{..4.. 0020 c4 84 0a 6a 39 ad 3c 10 63 b2 22 c4 24 40 f4 b1 ...j9.<.c.".$@.. * The lines look basically like Wireshark's hex dump. * WARNING: This function returns a pointer to a DYNAMICALLY allocated buffer * that the caller is supposed to free(). * */ char *hexdump(const u8 *cp, u32 length){ static char asciify[257]; /* Stores cha6acter table */ int asc_init=0; /* Flag to generate table only once */ u32 i=0, hex=0, asc=0; /* Array indexes */ u32 line_count=0; /* For byte count at line start */ char *current_line=NULL; /* Current line to write */ char *buffer=NULL; /* Dynamic buffer we return */ #define LINE_LEN 74 /* Lenght of printed line */ char line2print[LINE_LEN]; /* Stores current line */ char printbyte[16]; /* For byte conversion */ int bytes2alloc; /* For buffer */ memset(line2print, ' ', LINE_LEN); /* We fill the line with spaces */ /* On the first run, generate a list of nice printable characters * (according to current locale) */ if( asc_init==0){ asc_init=1; for(i=0; i<256; i++){ if( isalnum(i) || isdigit(i) || ispunct(i) ){ asciify[i]=i; } else{ asciify[i]='.'; } } } /* Allocate enough space to print the hex dump */ bytes2alloc=(length%16==0)? (1 + LINE_LEN * (length/16)) : (1 + LINE_LEN * (1+(length/16))) ; buffer=(char *)safe_zalloc(bytes2alloc); current_line=buffer; #define HEX_START 7 #define ASC_START 57 /* This is how or line looks like. 0000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f .`e...m05.T..g..[\n] 01234567890123456789012345678901234567890123456789012345678901234567890123 0 1 2 3 4 5 6 7 ^ ^ ^ | | | HEX_START ASC_START Newline */ i=0; while( i < length ){ memset(line2print, ' ', LINE_LEN); /* Fill line with spaces */ snprintf(line2print, sizeof(line2print), "%04x", (16*line_count++) % 0xFFFF); /* Add line No.*/ line2print[4]=' '; /* Replace the '\0' inserted by snprintf() with a space */ hex=HEX_START; asc=ASC_START; do { /* Print 16 bytes in both hex and ascii */ if (i%16 == 8) hex++; /* Insert space every 8 bytes */ snprintf(printbyte, sizeof(printbyte), "%02x", cp[i]);/* First print the hex number */ line2print[hex++]=printbyte[0]; line2print[hex++]=printbyte[1]; line2print[hex++]=' '; line2print[asc++]=asciify[ cp[i] ]; /* Then print its ASCII equivalent */ i++; } while (i < length && i%16 != 0); /* Copy line to output buffer */ line2print[LINE_LEN-1]='\n'; memcpy(current_line, line2print, LINE_LEN); current_line += LINE_LEN; } buffer[bytes2alloc-1]='\0'; return buffer; } /* End of hexdump() */ /* This is like strtol or atoi, but it allows digits only. No whitespace, sign, or radix prefix. */ long parse_long(const char *s, char **tail) { if (!isdigit((int) (unsigned char) *s)) { *tail = (char *) s; return 0; } return strtol(s, (char **) tail, 10); } /* This function takes a byte count and stores a short ascii equivalent in the supplied buffer. Eg: 0.122MB, 10.322Kb or 128B. */ char *format_bytecount(unsigned long long bytes, char *buf, size_t buflen) { assert(buf != NULL); if (bytes < 1000) Snprintf(buf, buflen, "%uB", (unsigned int) bytes); else if (bytes < 1000000) Snprintf(buf, buflen, "%.3fKB", bytes / 1000.0); else Snprintf(buf, buflen, "%.3fMB", bytes / 1000000.0); return buf; } /* Compare a canonical option name (e.g. "max-scan-delay") with a user-generated option such as "max_scan_delay" and returns 0 if the two values are considered equivalant (for example, - and _ are considered to be the same), nonzero otherwise. */ int optcmp(const char *a, const char *b) { while(*a && *b) { if (*a == '_' || *a == '-') { if (*b != '_' && *b != '-') return 1; } else if (*a != *b) return 1; a++; b++; } if (*a || *b) return 1; return 0; } /* Returns one if the file pathname given exists, is not a directory and * is readable by the executing process. Returns two if it is readable * and is a directory. Otherwise returns 0. */ int fileexistsandisreadable(const char *pathname) { char *pathname_buf = strdup(pathname); int status = 0; struct stat st; #ifdef WIN32 // stat on windows only works for "dir_name" not for "dir_name/" or "dir_name\\" int pathname_len = strlen(pathname_buf); char last_char = pathname_buf[pathname_len - 1]; if( last_char == '/' || last_char == '\\') pathname_buf[pathname_len - 1] = '\0'; #endif if (stat(pathname_buf, &st) == -1) status = 0; else if (access(pathname_buf, R_OK) != -1) status = S_ISDIR(st.st_mode) ? 2 : 1; free(pathname_buf); return status; } #if HAVE_PROC_SELF_EXE static char *executable_path_proc_self_exe(void) { char buf[1024]; char *path; int n; n = readlink("/proc/self/exe", buf, sizeof(buf)); if (n < 0 || n >= sizeof(buf)) return NULL; path = (char *) safe_malloc(n + 1); /* readlink does not null-terminate. */ memcpy(path, buf, n); path[n] = '\0'; return path; } #endif #if HAVE_MACH_O_DYLD_H #include /* See the dyld(3) man page on OS X. */ static char *executable_path_NSGetExecutablePath(void) { char buf[1024]; uint32_t size; size = sizeof(buf); if (_NSGetExecutablePath(buf, &size) == 0) return strdup(buf); else return NULL; } #endif #if WIN32 static char *executable_path_GetModuleFileName(void) { char buf[1024]; int n; n = GetModuleFileName(GetModuleHandle(0), buf, sizeof(buf)); if (n <= 0 || n >= sizeof(buf)) return NULL; return strdup(buf); } #endif static char *executable_path_argv0(const char *argv0) { if (argv0 == NULL) return NULL; /* We can get the path from argv[0] if it contains a directory separator. (Otherwise it was looked up in $PATH). */ if (strchr(argv0, '/') != NULL) return strdup(argv0); #if WIN32 if (strchr(argv0, '\\') != NULL) return strdup(argv0); #endif return NULL; } char *executable_path(const char *argv0) { char *path; path = NULL; #if HAVE_PROC_SELF_EXE if (path == NULL) path = executable_path_proc_self_exe(); #endif #if HAVE_MACH_O_DYLD_H if (path == NULL) path = executable_path_NSGetExecutablePath(); #endif #if WIN32 if (path == NULL) path = executable_path_GetModuleFileName(); #endif if (path == NULL) path = executable_path_argv0(argv0); return path; }