/* * $Id: //websites/unixwiz/unixwiz.net/webroot/tools/whoamip.c#1 $ * * written by : Stephen J. Friedl * Software Consultant * Tustin, California USA * http://www.unixwiz.net/tools/ * * *********************************************************** * ** ** * ** This program is in the public domain, and can be used ** * ** by anybody, for any purpose. Have fun. ** * ** ** * *********************************************************** * * This program is used to make the best guess at a user's remote * IP address. We do not have a good mechanism to obtain it directly, * but given the user's tty (usually a pts), we can look in the * utmpx file where it's recorded. * * The "who" program reads from /var/adm/utmpx, but sadly does not * seem to have a way to report the hostname, so we have written * this program to do so. * * Playing on the "whoami" program, we've named it "whoamip". * * WHAT'S THE HOSTNAME? * -------------------- * * We're not able to directly discover the IP address of the other * end of this connection, because our program doesn't have any * access to the direct socket in order to run getpeername(). An * intermediate pseudo-tty isolates us from the socket, so we have * no trivial way to learn this straightway. * * Some operating systems probably provide some kernel-based mechanism * for digging into the network stack, but this is troublesome and * highly non-portable. It's probably nearly unsolvable on many * platforms. * * Instead, we'll get the information indirectly: the init and/or login * program typically maintains this in the utmpx file (in /etc/ or * /var/adm), associating the remote host with the current logged-in * pseudo-tty. * * We locate the current tty and use that as a key to utmpx: this * brings a record with the username and hostname: this is what's * reported to the user. * * We're at the mercy of the login program to fill in the ut_host * field: sometimes it contains the IP address (as a string), and * other times it's a hostname (perhaps truncated). We have no * control over this process and simply take what we're given. * * SSH SHORCUTS * ------------ * * As a short-circuit for users who come in via Secure Shell, we * take a quick check for the $SSH_CLIENT environment variable, * which would contain the IP address. If this is found, we return * it directly without doing any I/O or mucking around with the TTY. * * Use the -S cmdline parameter to disable the $SSH_CLIENT check, * and in any case we do not use the value unless there is a space * after the remote IP address. * * BUILDING + RUNNING * ------------------ * * This requires just a C compiler: * * $ cc whoamip.c -o whoamip * * It should compile without errors or warnings. * * Running the program produces the hostname on the standard * output, which can be captured in the shell (often in /etc/profile). * * $ host=`whoamip` * * We exit with zero (success) if we believe we've located the hostname, * and nonzero (failure) if we can't for whatever reason. * * COMMAND LINE * ------------ * * -V * Show the program's version information, then exit with success * * -S * Disable the $SSH_CLIENT check. * * VERSION HISTORY * --------------- * * 1.0.1 - 2006-07-31 * Initial release */ #include #include #include #include #include #include #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif static const char Version[] = "whoamip 1.0.1 - 2006-07-31 - http://www.unixwiz.net/tools/"; static int no_ssh_client = FALSE; int main(int argc, char **argv) { int c; char *tty, *p, *env; struct utmpx protox, *utp; /*--------------------------------------------------------------- * COMMAND-LINE */ while ( (c = getopt(argc, argv, "VS")) != EOF ) { switch (c) { case 'V': puts(Version); exit(EXIT_SUCCESS); case 'S': no_ssh_client = TRUE; break; default: exit(EXIT_FAILURE); } } /*--------------------------------------------------------------- * SHORTCUT: SSH variable? * * Most users don't use SSH, but for those that do, the $SSH_CLIENT * reports the remote connection: * * SSH_CLIENT="64.170.162.98 51851 22" * * We only care about the IP address and not the ports involved, * but this is a pretty reliable mechanism. */ if ( ! no_ssh_client && (env = getenv("SSH_CLIENT")) != 0 && (p = strchr(env, ' ')) != 0 ) { *p = '\0'; *p = '\0'; puts(env); exit(EXIT_SUCCESS); } /*--------------------------------------------------------------- * FIND TTY NAME * * The utmp/wtmp files ONLY work when indexed by tty name, so we * have to find the current tty. For network connections, this is * virtually always /dev/ptsX (a pseudo-tty), and we remove the * leading /dev/ part. * * Some systems work with /dev/pts123, while others use /dev/pts/123. * We remove /dev/ in either case. * * NOTE: a tty is associated with a *file descriptor*, not a * program, so we have to pick one. We use the standard input, * because the standard output & error may well be attached to * either an output file or a pipe: * * host=`whoamip` * * If we cannot find the tty name, we're done - sorry. * * Otherwise, rip off everything up to the final / */ if ( (tty = ttyname(fileno(stdin))) == 0 ) { fprintf(stderr, "cannot get tty [%s]\n", strerror(errno) ); exit(EXIT_FAILURE); } if ( strncmp(tty, "/dev/", 5) == 0 ) tty += 5; /*--------------------------------------------------------------- * LOCATE UTMPX * * Look in the utmpx file for a record matching this line, but * the lookup is done with a prototype UTMPX record that has the * ut_line member filled in. */ memset(&protox, 0, sizeof protox); strcpy(protox.ut_line, tty); if ( (utp = getutxline(&protox) ) == 0 ) { fprintf(stderr, "cannot locate UTMPX for {%s}\n", tty); exit(EXIT_FAILURE); } /*--------------------------------------------------------------- * FOUND IT! * * We should have a NUL-byte terminated string, but we believe * that we've seen systems which use up to the full size of the * buffer without the trailing NUL. That would mean we might end * up with a runaway string. That would be bad. * * So we create a buffer one byte larger, copy the ut_host field * into it, and assuredly add our own NUL byte. * * This may be overkill, but we don't get so snookered by funky * data. */ { #define SZ sizeof(utp->ut_host) char linebuf[1 + SZ]; memcpy(linebuf, utp->ut_host, SZ); linebuf[SZ] = '\0'; puts(linebuf); } return EXIT_SUCCESS; }